如何用撇号而不是引号解码无效的json


How to json_decode invalid JSON with apostrophe instead of quotation mark

示例代码:

<?php
$json = "['foo', 'bar']";
var_dump( json_decode($json) );

它适用于PHP 5.5.3,但对于较低版本的PHP失败

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

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

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

沙盒

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

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

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

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

这种方法在两个方面比h2oooooo的函数更稳健:

  • 它通过对双引号应用额外的转义来保留单引号字符串中出现的双引号。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
        )
)

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

示例

<?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

NikiCs的答案已经很准确。您的输入似乎是手动生成的,因此在'单引号字符串中,您完全有可能收到未引号的"双引号。因此,建议使用regex断言,而不是简单的搜索和替换。

但也有一些userland JSON解析器支持更多的Javascript表达式语法。在这一点上,最好谈谈JSOL,JavaScript Object Literals。

PEARs服务_JSON

Services_JSON可以解码:

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

无需额外选项,只需= (new Services_JSON)->decode($jsol);

upgradedphp中的up_json_decode()

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

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
)

其他选项

  • @ArtisticPhoenix的JasonDecoder确实支持未加引号的键和文字,尽管没有'引用的字符串。然而,它很容易理解或扩展。

  • YAML(1.2)是JSON的超集,大多数解析器都支持无引号键或单引号字符串。另请参阅PHP YAML Parsers

显然,userland中的任何JSOL标记器/解析器都比预处理格式错误的JSON慢得多。如果您希望Web服务不再有任何问题,请改用regex/quote转换。

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

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

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

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

如果你知道PHP 5.5++会优雅地解析这个JSON,我会通过PHP5.5+web服务器上的代理脚本来传输web服务响应,这会为较低版本净化响应,也就是说只有echo json_encode(json_decode($response));这是一种稳定可靠的方法。

如果您通过配置值使web服务URL可配置,则它将通过访问代理来适用于较低版本,而通过直接访问web服务来适用于较高版本。

一个快速的解决方案可以是str_replace("'","'"",$string)。这取决于很多因素,但我认为你可以试一试。

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

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

这可能是一个良好的开端。