【问题标题】:PHP's json_encode does not escape all JSON control charactersPHP json_encode 不会转义所有 JSON 控制字符
【发布时间】:2010-11-06 02:48:12
【问题描述】:

PHP的json_encode函数没有转义字符串中的所有JSON控制字符有什么原因吗?

例如,让我们取一个跨越两行并在其中包含控制字符(\r \n " / \)的字符串:

<?php
$s = <<<END
First row.
Second row w/ "double quotes" and backslash: \.
END;

$s = json_encode($s);
echo $s;
// Will output: "First row.\r\nSecond row w\/ \"double quotes\" and backslash: \\."
?>

请注意,回车符和换行符是未转义的。为什么?

我使用 jQuery 作为我的 JS 库,它的 $.getJSON() 函数在您完全 100% 信任传入数据时会很好。否则我会像其他人一样使用 JSON.org 的库 json2.js。 但是,如果您尝试解析该编码字符串,则会引发错误:

<script type="text/javascript">

JSON.parse(<?php echo $s ?>);  // Will throw SyntaxError 

</script>

而且您无法获取数据!如果您删除或转义该字符串中的 \r \n " 和 \,则 JSON.parse() 不会引发错误。

是否有任何现有的、良好的 PHP 函数用于转义控制字符。带有搜索和替换数组的简单 str_replace 将不起作用。

【问题讨论】:

  • 而不是 学习使用 =$s?> :) 只是我的提示。
  • 感谢您的提示,但快捷回显语法仅在启用 short_open_tag 且我从未使用过短开始标签时才有效,因为我更喜欢
  • @Thinker 另外,如果短开标签在服务器上被关闭,那么您的代码不仅会中断,还会输出到浏览器。
  • @BadHorsie 在发表错误评论之前检查自己。 = 并不是不好的做法,即使从 PHP 5.4 起关闭短标签,它也始终启用。另请阅读php-fig.org/psr/psr-1 了解编码标准
  • @mewm 你是对的,几年前我发表了评论,当时情况有所不同。从那以后,我自己一直在使用短回声标签一段时间。删除了 cmets 以避免向未来的访问者提供不良信息。

标签: php json


【解决方案1】:
function escapeJsonString($value) {
    # list from www.json.org: (\b backspace, \f formfeed)    
    $escapers =     array("\\",     "/",   "\"",  "\n",  "\r",  "\t", "\x08", "\x0c");
    $replacements = array("\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t",  "\\f",  "\\b");
    $result = str_replace($escapers, $replacements, $value);
    return $result;
  }

我正在使用上述函数转义反斜杠(必须是数组中的第一个),并且应该处理换页符和退格(我认为 PHP 不支持 \f 和 \b)。

【讨论】:

  • 不错,正是我需要的。
  • 请注意,您的函数并未根据 RFC 对所有值进行编码。例如,字符代码 15 应编码为 \u000f。
【解决方案2】:

D'oh - 你需要双重编码:JSON.parse 当然期待一个字符串:

<script type="text/javascript">

JSON.parse(<?php echo json_encode($s) ?>);

</script>

【讨论】:

  • 转义换行符不看起来像这样:\\n 吗?现在 \r 和 \n 没有被转义。
  • 不,转义的换行符是“\n”。未转义的换行符将是一个新行。
  • "\\n" 将是一个转义的反斜杠,后跟一个 "n" - 试试 alert('foo\nbar');和警报('foo\\nbar');在一段普通的代码中。
  • 不正确。 var d = JSON.parse('["a\\nb"]');警报(d);会提醒: a b JSON.parse('["a\nb"]') 会抛出错误,因为换行符没有被转义。
  • 看起来很奇怪,但它是正确的 - 第一个说“将此对象转换为 JSON”,然后第二个说“将此 JSON 转换为 Javascript 字符串”
【解决方案3】:

如果没有str_replace,我仍然没有找到任何解决方案。

试试这个代码。

$json_encoded_string = json_encode(...);
$json_encoded_string = str_replace("\r", '\r', $json_encoded_string);
$json_encoded_string = str_replace("\n", '\n', $json_encoded_string);

希望对您有所帮助...

【讨论】:

    【解决方案4】:
    $search = array("\n", "\r", "\u", "\t", "\f", "\b", "/", '"');
    $replace = array("\\n", "\\r", "\\u", "\\t", "\\f", "\\b", "\/", "\"");
    $encoded_string = str_replace($search, $replace, $json);
    

    这是正确的方法

    【讨论】:

    • 这里有一些小故障。最后 2 应该是 "\\", '"' 并且在第 2 行:"\\\\", "\\\""
    【解决方案5】:

    从 PHP 来回转换应该不是问题。 PHP 的 json_encode 进行了正确的编码,但在 java 脚本中重新解释它可能会导致问题。喜欢

    1) 原始字符串 - [带有 nnn 换行符的字符串](其中 nnn 是实际的换行符)

    2) json_encode 会将其转换为 [其中包含“\\n”换行符的字符串](控制字符转换为“\\n” - 文字“\n”

    3) 但是,当您使用 php echo 在文字字符串中再次打印时,“\\n”会被解释为“\n”,这会导致心痛。因为 JSON.parse 会将文字打印的“\n”理解为换行符 - 控制字符 (nnn)

    所以要解决这个问题:-

    一) 首先使用 json_enocde 对 php 中的 json 对象进行编码,得到一个字符串。然后通过一个过滤器运行它,使其可以安全地在 html 和 java 脚本中使用。

    B) 使用来自 PHP 的 JSON 字符串作为“文字”,并将其放在单引号而不是双引号内。


    <?php
           function form_safe_json($json) {
                $json = empty($json) ? '[]' : $json ;
                $search = array('\\',"\n","\r","\f","\t","\b","'") ;
                $replace = array('\\\\',"\\n", "\\r","\\f","\\t","\\b", "&#039");
                $json = str_replace($search,$replace,$json);
                return $json;
            }
    
    
            $title = "Tiger's   /new \\found \/freedom " ;
            $description = <<<END
            Tiger was caged
            in a Zoo 
            And now he is in jungle
            with freedom
        END;
    
            $book = new \stdClass ;
            $book->title = $title ;
            $book->description = $description ;
            $strBook = json_encode($book);
            $strBook = form_safe_json($strBook);
    
            ?>
    
    
        <!DOCTYPE html>
        <html>
    
            <head>
                <title> title</title>
    
                <meta charset="utf-8">
    
    
                <script type="text/javascript" src="/3p/jquery/jquery-1.7.1.min.js"></script>
    
    
                <script type="text/javascript">
                    $(document).ready(function(){
                        var strBookObj = '<?php echo $strBook; ?>' ;
                        try{
                            bookObj = JSON.parse(strBookObj) ;
                            console.log(bookObj.title);
                            console.log(bookObj.description);
                            $("#title").html(bookObj.title);
                            $("#description").html(bookObj.description);
                        } catch(ex) {
                            console.log("Error parsing book object json");
                        }
    
                    });
                </script>
    
            </head>
    
             <body>
    
                 <h2> Json parsing test page </h2>
                 <div id="title"> </div>
                 <div id="description"> </div>
            </body>
        </html>
    

    将字符串放在java脚本中的单引号内。将 JSON 字符串放在双引号内会导致解析器在属性标记处失败(类似于 { "id" : "value" } )。如果您将字符串设置为“文字”并让 JSON 解析器完成工作,则不需要其他转义。

    【讨论】:

      【解决方案6】:

      我不完全了解 var_export 的工作原理,所以如果遇到问题我会更新,但这似乎对我有用:

      <script>
          window.things = JSON.parse(<?php var_export(json_encode($s)); ?>);
      </script>
      

      【讨论】:

        【解决方案7】:

        也许我是盲人,但在你的例子中他们被逃脱了。怎么样

        <script type="text/javascript">
        
        JSON.parse("<?php echo $s ?>");  // Will throw SyntaxError 
        
        </script>
        

        (注意不同的引号)

        【讨论】:

        • 这里不能使用双引号,因为在回显字符串后会是:JSON.parse(""... 等等。
        【解决方案8】:

        只是对Greg的回复的补充:json_encode()的输出已经包含在双引号(")中,所以不需要再用引号括起来:

        <script type="text/javascript">
            JSON.parse(<?php echo $s ?>);
        </script>
        

        【讨论】:

          【解决方案9】:

          控制字符在 HTML 中没有特殊含义,除了 textarea.value 中的换行符。 PHP > 5.2 上的 JSON_encode 将按照您的预期进行。

          如果您只想显示文本,则无需使用 JSON。 JSON 用于 JavaScript 中的数组和对象(以及 PHP 的索引和关联数组)。

          如果您需要 texarea-tag 的换行符:

          $s=preg_replace('/\r */','',$s);
          echo preg_replace('/ *\n */','&#13;',$s);
          

          【讨论】:

            【解决方案10】:

            这是我个人使用的,它从来没有用过。原来也有类似的问题。

            源脚本 (ajax) 将采用一个数组并对其进行 json_encode。示例:

            $return['value'] = 'test';
            $return['value2'] = 'derp';
            
            echo json_encode($return);
            

            我的 javascript 将进行 AJAX 调用并获取回显的“json_encode($return)”作为其输入,在脚本中我将使用以下内容:

            myVar = jQuery.parseJSON(msg.replace(/&quot;/ig,'"'));
            

            以“msg”作为返回值。所以,对你来说,像......

            var msg = '<?php echo $s ?>';
            myVar = jQuery.parseJSON(msg.replace(/&quot;/ig,'"'));
            

            ...可能对你有用。

            【讨论】:

              【解决方案11】:

              当使用任何形式的 Ajax 时,Web 上似乎都缺乏关于从 CGI 服务器接收到的响应格式的详细文档。此处的一些注释和 stackoverflow.com 上的条目指出,无论是由 jQuery 自动完成还是手动使用Javascript 系统或库 JSON 解析调用。

              在程序员发布此问题的每种情况下,都会提出不充分的解决方案(通常在发送方将 \n 替换为 \\n)并且该问题被放弃。当传递意外嵌入控制转义序列的字符串值(例如 Windows 路径名)时,就会发现它们的不足之处。一个例子是 "C:\Chris\Roberts.php",它包含控制字符 ^c 和 ^r,这可以导致字符串 {"file":"C:\Chris\Roberts.php"} 的 JSON 转换为永远循环。生成此类值的一种方法是故意尝试将 PHP 警告和错误消息从服务器传递到客户端,这是一个合理的想法。

              根据定义,Ajax 在后台使用 HTTP 连接。此类连接使用 GET 和 POST 传递数据,这两种方法都需要对发送的数据进行编码以避免语法错误,包括控制字符。

              这提供了足够的提示来构建似乎是一个解决方案(它需要更多测试):在 PHP(发送)端使用 rawurlencode 对数据进行编码,并在 Javascript(接收)端使用 unescape 进行解码数据。在某些情况下,您会将这些应用于整个文本字符串,在其他情况下,您将仅将它们应用于 JSON 中的值。

              如果这个想法被证明是正确的,可以构建简单的例子来帮助各级程序员一劳永逸地解决这个问题。

              【讨论】:

                【解决方案12】:

                除非使用 AJAX,否则有 2 种解决方案:

                1. 将数据写入输入,并在 JS 中读取:

                  <input type="hidden" value="<?= htmlencode(json_encode($data)) ?>"/>
                  
                2. 使用加斜线

                  var json = '<?= addslashes(json_encode($data)) ?>';
                  

                【讨论】:

                  猜你喜欢
                  • 2014-06-12
                  • 1970-01-01
                  • 2017-08-17
                  • 2014-05-31
                  • 2018-07-16
                  • 2012-07-01
                  • 2014-08-02
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多