如何解析不规范的JSON数据?

@Ta 2022-06-05发布,2022-06-05修改 17296点击

解析某网站的API数据,该数据表面上以JSON形式返回,实际上像是人工直接拼接而成,如:

<?php
$chapters = ['第一章 开始', '第二章 过程', '第三章 结束'];
echo '[';
foreach ($chapters as $id => $name)
    echo '{"id":' . $id . ',"name":"' . $name . '"},';
echo ']';
[{"id":0,"name":"第一章 开始"},{"id":1,"name":"第二章 中间"},{"id":2,"name":"第三章 结束"},]

大部分时候问题不大(处理末尾逗号即可),但出错的时候是真的棘手。搜集了一些出错的例子(简化):

[
    {"name": "第五卷:"世家"子弟"},         # 这种情况出现最多,没转义『"』
    {"name": "天下大乱(上)【\x06】"},    # 出现了 ASCII 0-31 的字符,json 规范是不允许的
    {"name": "\&quot;小白兔\&quot;少爷"},  # 转义了不该转义的字符
    {"name": "番外篇~\(≧▽≦)/~"},        # 同上
    {"name": "权力的Chun\药"},             # 同上
    {"name": 饮马江湖"},                   # 搞不明白这是怎么出现的。是标题含有“退格符”(\x08),吃掉上一个『"』吗?
    {"name": style="color:Gray;"},         # ???
    {"name": ""},                          # 最后一项末尾不能有『,』
]
回复列表(45|显示机器人聊天)
  • @Ta / 2022-06-05 / /

    先求助万能虎,@老虎会游泳

    有没有可能用正则规范它们

  • @Ta / 2022-06-05 / /

    我觉得正则规范它们可能是开发/运行效率最高的一个

    自己写解析器,怕又慢又不保证正确

  • @Ta / 2022-06-05 / /

    去除末尾逗号的正则,我已经写了(前提是其他内容都是正确的

    模式:("(?:\\?.)*?")|,\s*([]}])
    替换:\1\2

  • @Ta / 2022-06-05 / /

    截取?

  • @Ta / 2022-06-05 / /

    说起这个json,我原生js获取正常,拿这个uniapp写,搞了一天都没解析成,睡前搜到了几篇文章,明天看看

  • @Ta / 2022-06-05 / /

    @20263

    • 截取啥?
    • uniapp。。是小程序的吗?不支持JSON.parse()
  • @Ta / 2022-06-06 / /
    哪个网站?
  • @Ta / 2022-06-06 / /
    遇到不规范的json真恼火
  • @Ta / 2022-06-06 / /

    @无名啊,如果是合法的JS代码,在JS里可以用eval解析:

    var obj = eval('return ' + json);
    

    就能完成解析。

    不要忘记JSON的全称:JavaScript Object Notation,JavaScript对象表示法。

    合法的JSON一定是合法的JavaScript对象。

    不合法的JSON,既然网站在用,那浏览器肯定能解析,所以通常也是合法的JavaScript对象。

  • @Ta / 2022-06-06 / /

    @淡然@20263,如果运行环境是浏览器JavaScript,并且不怕任意代码执行漏洞,那就使用eval解析嘛。发送给浏览器的看起来像JSON但实际不是的,都是JSON的前身:JavaScript对象表达式。

    正是因为Web程序员都喜欢向前端输出JavaScript对象表达式来实现信息传递,所以有人对JavaScript对象表达式进行了规范,添加了语法限制,于是得到了我们如今使用的JSON:JavaScript对象表示法(JavaScript Object Notation)。

  • @Ta / 2022-06-06 / /

    @20263@无名啊@淡然,当然对于引号不匹配这种问题,eval也无能为力,也只会得到错误。此时,如果想可靠地提取信息,可以考虑放弃格式分析,直接进行正则表达式内容匹配:

    var json = `[
        {"name": "第五卷:"世家"子弟"},         # 这种情况出现最多,没转义『"』
        {"name": "天下大乱(上)【\x06】"},    # 出现了 ASCII 0-31 的字符,json 规范是不允许的
        {"name": "\&quot;小白兔\&quot;少爷"},  # 转义了不该转义的字符
        {"name": "番外篇~\(≧▽≦)/~"},        # 同上
        {"name": "权力的Chun\药"},             # 同上
        {"name": 饮马江湖"},                   # 搞不明白这是怎么出现的。是标题含有“退格符”(\x08),吃掉上一个『"』吗?
        {"name": style="color:Gray;"},         # ???
        {"name": ""},                          # 最后一项末尾不能有『,』
    ]`;
    
    var results = json.matchAll(/\{"name":\s*"?(.*?)"?\},/gm);
    
    for (var obj of results) {
        console.log(obj[1]);
    }
    

    Screenshot_20220606_123323.jpg

  • @Ta / 2022-06-06 / /

    PHP版:

    <?php
    $json = <<<EOF
    [
        {"name": "第五卷:"世家"子弟"},         # 这种情况出现最多,没转义『"』
        {"name": "天下大乱(上)【\x06】"},    # 出现了 ASCII 0-31 的字符,json 规范是不允许的
        {"name": "\&quot;小白兔\&quot;少爷"},  # 转义了不该转义的字符
        {"name": "番外篇~\(≧▽≦)/~"},        # 同上
        {"name": "权力的Chun\药"},             # 同上
        {"name": 饮马江湖"},                   # 搞不明白这是怎么出现的。是标题含有“退格符”(\x08),吃掉上一个『"』吗?
        {"name": style="color:Gray;"},         # ???
        {"name": ""},                          # 最后一项末尾不能有『,』
    ]
    EOF;
    
    preg_match_all('/\{"name":\s*"?(.*?)"?\},/m', $json, $results, PREG_SET_ORDER);
    
    foreach ($results as $obj) {
        echo $obj[1], "\n";
    }
    

    Screenshot_20220606_125243.jpg

  • @Ta / 2022-06-06 / /

    @无名啊,鉴于内容不是合法的JS代码(引号不匹配),所以正则表达式可能是最佳方案。

    \{"name":\s*"?(.*?)"?\},
    

    11楼和12楼的效果非常好。


    https://regex101.com/

    Screenshot_20220606_125858_com.android.chrome.jpg

  • @Ta / 2022-06-06 / /

    @无名啊,你的真实数据在name旁边还有其他字段,得修改一下正则表达式。

  • @Ta / 2022-06-06 / /

    @老虎会游泳,直接提取name不太够,真实数据结构大概是:

    • API结果信息
    • 数据
      • 书本信息
        • 卷1信息
          • 章节1信息
          • 章节2信息
        • 卷2信息
          • ……

    书本、卷、章节信息都希望得以保留(重新生成目录所需嘛)

  • @Ta / 2022-06-06 / /

    @无名啊,顺便一提,你无法解析的时候,那个网站的客户端估计也是无法解析的,你可以去试试

  • @Ta / 2022-06-06 / /

    @无名啊,所以,可以考虑采用社会工程学解决这个问题,向他们反馈app有Bug,让他们自己修复

  • @Ta / 2022-06-06 / /

    @无名啊,name可以用正则表达式得到,其他字段当然也可以,只是表达式变得更复杂而已。

添加新回复
回复需要登录