【问题标题】:How to json_decode invalid JSON with apostrophe instead of quotation mark如何使用撇号而不是引号对无效的 JSON 进行 json_decode
【发布时间】:2013-12-19 08:19:06
【问题描述】:

示例代码:

<?php

$json = "['foo', 'bar']";

var_dump( json_decode($json) );

它适用于 PHP 5.5.3,但它不适用于较低的 PHP 版本

它在我的机器上运行 PHP 5.5.3 但在其他地方都失败了。

我知道这是不正确的 JSON,但我的网络服务给了我带有 ' 符号和 " 的 JSON

['foo', "bar", {'test': "crazy \"markup\""}]

Sandbox

如何在 PHP 5.3 中解析带撇号的 JSON 数据? 显然我要解析的原始 JSON 更复杂。

(我无法在生产服务器上升级我的 PHP,也无法从 web 服务获取正确的 JSON)

【问题讨论】:

  • 恕我直言,ff PHP/5.5.3 解析无效 JSON 这可能是一个错误。
  • 您的代码示例似乎不适用于任何版本,3v4l.org/Hl99u
  • 如果字符串包含"',损坏的Web 服务会返回什么?您是否让服务知道他们提供了错误的 json?
  • @AnthonySterling 适用于PHP 5.5.3-1ubuntu2 (cli)
  • 不幸的是无效数据是一个大问题,如果可以的话,我的 0,02 美分是与提供者交谈,我也遇到了 XML 问题,不得不进行大量文本解析来修复它们的无效格式(例如,他们使用非转义 & 符号)。

标签: php json


【解决方案1】:

您可以使用(并且可能修改/扩展)一个库来从提供的 JSON 构建一个 AST,并将单引号替换为双引号。

https://github.com/Seldaek/jsonlint/blob/master/src/Seld/JsonLint/Lexer.php

可能是一个好的开始。

【讨论】:

    【解决方案2】:

    这是一个简单的解析器,可以为您修正引号。如果遇到不在双引号" 中的' 引号,它会认为它是错误的并替换该引号内的双引号,并将引号括起来变成双引号:

    示例

    <?php
        function fixJSON($json) {
            $newJSON = '';
    
            $jsonLength = strlen($json);
            for ($i = 0; $i < $jsonLength; $i++) {
                if ($json[$i] == '"' || $json[$i] == "'") {
                    $nextQuote = strpos($json, $json[$i], $i + 1);
                    $quoteContent = substr($json, $i + 1, $nextQuote - $i - 1);
                    $newJSON .= '"' . str_replace('"', "'", $quoteContent) . '"';
                    $i = $nextQuote;
                } else {
                    $newJSON .= $json[$i];
                }
            }
    
            return $newJSON;
        }
    
        $brokenJSON = "['foo', {\"bar\": \"hel'lo\", \"foo\": 'ba\"r'}]";
        $fixedJSON = fixJSON( $brokenJSON );
    
        var_dump($fixedJSON);
    
        print_r( json_decode( $fixedJSON ) );
    ?>
    

    输出

    string(41) "["foo", {"bar": "hel'lo", "foo": "ba'r"}]"
    Array
    (
        [0] => foo
        [1] => stdClass Object
            (
                [bar] => hel'lo
                [foo] => ba'r
            )
    
    )
    

    DEMO

    【讨论】:

      【解决方案3】:

      一个快速的解决方案可能是str_replace("'","\"",$string)。这取决于很多事情,但我认为您可以尝试一下。

      【讨论】:

        【解决方案4】:

        一种解决方案是使用 NodeJS 构建代理。 NodeJS 会很好地处理错误的 JSON 并返回一个干净的版本:

        johan:~ # node
        > JSON.stringify(['foo', 'bar']);
        '["foo","bar"]'
        

        也许可以编写一个简单的 Node 脚本,接受 JSON 数据作为 STDIN 并将经过验证的 JSON 返回到 STDOUT。这样你就可以从 PHP 中调用它了。

        缺点是您的服务器需要 NodeJS。不确定这对您来说是否有问题。

        【讨论】:

          【解决方案5】:

          这里有一个解决这个问题的替代方案:

          function fixJSON($json) {
              $regex = <<<'REGEX'
          ~
              "[^"\\]*(?:\\.|[^"\\]*)*"
              (*SKIP)(*F)
            | '([^'\\]*(?:\\.|[^'\\]*)*)'
          ~x
          REGEX;
          
              return preg_replace_callback($regex, function($matches) {
                  return '"' . preg_replace('~\\\\.(*SKIP)(*F)|"~', '\\"', $matches[1]) . '"';
              }, $json);
          }
          

          这种方法在两个方面比h2ooooooo的功能更健壮:

          • 它通过对双引号应用额外的转义来保留单引号字符串中出现的双引号。 h2o 的变体会用双引号代替它们,从而改变字符串的值。
          • 它将正确处理转义的双引号 \",h2o 的版本似乎进入了无限循环。

          测试:

          $brokenJSON = <<<'JSON'
          ['foo', {"bar": "hel'lo", "foo": 'ba"r ba\"z', "baz": "wor\"ld ' test"}]
          JSON;
          
          $fixedJSON = fixJSON($brokenJSON);
          $decoded = json_decode($fixedJSON);
          
          var_dump($fixedJSON);
          print_r($decoded);
          

          输出:

          string(74) "["foo", {"bar": "hel'lo", "foo": "ba\"r ba\"z", "baz": "wor\"ld ' test"}]"
          Array
          (
              [0] => foo
              [1] => stdClass Object
                  (
                      [bar] => hel'lo
                      [foo] => ba"r ba"z
                      [baz] => wor"ld ' test
                  )
          )
          

          【讨论】:

          • 这似乎不适用于嵌套对象。
          • 它不处理案例,例如\' 在单引号字符串中。这将失败:{'a':'\''}
          【解决方案6】:

          如果您知道 PHP 5.5.+ 将优雅地解析此 JSON,我将通过 PHP5.5+ Web 服务器上的代理脚本传输 Web 服务响应,该脚本会清理较低版本的响应 - 仅意味着 echo json_encode(json_decode($response)) ;这是一种稳定可靠的方法。

          如果您通过配置值使 Web 服务 URL 可配置,则它可以通过访问代理在较低版本中工作,通过直接访问 Web 服务在较高版本中工作。

          【讨论】:

            【解决方案7】:

            NikiCs´ answer 已经出现了。您的输入似乎是手动生成的,因此在 ' 单引号字符串中,您完全有可能收到未引用的 " 双引号。因此,建议使用正则表达式 assertion 而不是简单的搜索和替换。

            但也有一些用户态 JSON 解析器支持更多的 Javascript 表达式语法。在这一点上,最好谈谈 JSOL,JavaScript 对象文字。

            Services_JSON

            Services_JSON 可以解码:

            • 不带引号的对象键
            • 和用单引号括起来的字符串。

            不需要其他选项,只需= (new Services_JSON)-&gt;decode($jsol);

            up_json_decode() 在upgradephp

            这实际上是作为没有 JSON 扩展的早期 PHP 版本的后备。它重新实现了 PHP json_decode()。但是还有upgrade.php.prefixed 版本,您可以在这里使用它。
            它引入了一个额外的标志JSON_PARSE_JAVASCRIPT

            up_json_decode($jsol, false, 512, JSON_PARSE_JAVASCRIPT);
            

            我完全忘记了在文档中提到这一点,但它也支持单引号字符串。
            例如:

            { num: 123, "key": "value", 'single': 'with \' and unquoted " dbls' } 
            

            将解码为:

            stdClass Object
            (
                [num] => 123
                [key] => value
                [single] => with ' and unquoted " double quotes
            )
            

            其他选项

            • JasonDecoder by @ArtisticPhoenix 确实支持不带引号的键和文字,但不支持 ' 带引号的字符串。但是,它很容易理解或扩展。

            • YAML (1.2) 是 JSON 的超集,大多数 parsers 支持不带引号的键或单引号的字符串。另见PHP YAML Parsers

            显然,用户空间中的任何 JSOL 标记器/解析器都比仅预处理格式错误的 JSON 慢得多。如果您不希望您的网络服务有更多问题,请转而使用正则表达式/引号转换。

            【讨论】:

              猜你喜欢
              • 2021-09-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多