【问题标题】:Parsing JSON without quoted keys解析没有引用键的 JSON
【发布时间】:2011-01-04 20:17:03
【问题描述】:

我知道在 JSON 中,键应该用双引号引起来。但是,我使用的数据源没有引用它们,这导致 Ruby JSON 解析器引发错误。有没有办法执行“非严格”解析?

例子:

>> JSON.parse('{name:"hello", age:"23"}')
JSON::ParserError: 618: unexpected token at '{name:"hello", age:"23"}'
    from /Library/Ruby/Gems/1.8/gems/json-1.1.7/lib/json/common.rb:122:in `parse' 
    from /Library/Ruby/Gems/1.8/gems/json-1.1.7/lib/json/common.rb:122:in `parse'
    from (irb):5
>> JSON.parse('{"name":"hello", "age":"23"}')
=> {"name"=>"hello", "age"=>"23"}
>> 

(我尝试使用正则表达式在解析之前添加引号,但无法使其完全正常工作)。

【问题讨论】:

  • 如果不是 JSON,就不是 JSON。它可能表面上看起来很像,但正确的解决方案是修复源代码,以便它实际上给 JSON 提供了一些看起来有点像 JSON 但不是的东西。
  • 不幸的是我无法控制来源,它来自第三方。
  • 我认为你所拥有的是 YAML,而不是 JSON。所以使用 YAML 解析器。
  • google.com/ig/calculator?hl=en&q=100AUD=?USD 例如需要他所要求的。 @floyd 有以下解决方案 - 并且可以接受
  • Ruby 的 JSON 库有非严格模式吗? IIRC,Perl 的 JSON CPAN 模块将解析它,因为它允许像上面那样使用 Javascript-but-not-JSON。

标签: ruby json


【解决方案1】:

如果除此之外的数据格式非常好,一个简单的正则表达式就可以做到:

irb(main):009:0> '{name:"hello", age:"23"}'.gsub(/([a-z]+):/, '"\1":')
=> "{\"name\":\"hello\", \"age\":\"23\"}"

【讨论】:

  • '{name:"hello", age:"23"}'.gsub(/([\w]+):/, '"\1":') 让它更健壮一点!
  • 如果值是时间戳,则失败得很惨。例如 { name: "Hello", time: "12:59:59" }
【解决方案2】:

我对第三方数据馈送也有同样的问题,但我的返回一个更复杂的类似 JSON 的响应,gsub 解决方案无法处理。经过一些研究,这些数据源实际上是 JavaScript 对象文字,不需要引用键。

为了解决这个问题,我添加了 execjs gem 并安装了 node.js(therubyracer gem 可能也可以工作)。完成后,以下内容将返回正确解析的 ruby​​ 哈希。

ExecJS.eval('{name:"hello", age:"23"}')
 => {"name"=>"hello", "age"=>"23"}

【讨论】:

    【解决方案3】:

    有趣的是,您的示例是有效的 ruby​​ 1.9 哈希语法。如果你的数据真的这么简单(键名中没有空格或其他特殊字符),并且你可以在安全的上下文中处理它,你可以eval它。

    irb(main):001:0> eval '{name:"hello", age:"23"}'
    => {:name=>"hello", :age=>"23"}
    

    这为您提供了符号作为键,因此如果您需要将它们转换为字符串,请进行后期处理:

    irb(main):002:0> eval('{name:"hello", age:"23"}').reduce({}) {|h,(k,v)| h[k.to_s] = v; h}
    => {"name"=>"hello", "age"=>"23"}
    

    【讨论】:

    • 谢谢你,虽然因为我使用的是 1.8.7,但目前这不是一个选项。
    • 非常干净的解决方案!谢谢。利用 Google 提供的这些信息比寻找另一个宝石要干净得多。
    • 这可能非常危险...(例如,如果您从服务器获取{a: 1}; `rm -rf /`
    • 旧线程,但@ghayes 关心:eval("[..]".gsub("`", "'"))
    【解决方案4】:
    gsub(/(\w+)\s*:/, '"\1":')
    

    效果比

    gsub(/([a-z]+):/, '"\1":')
    

    如果有空格或大写字母,则失败。

    【讨论】:

      【解决方案5】:

      (回答我自己的问题)弗洛伊德发布的 sn-p 与我尝试的类似 - 它失败了,因为我的一些字符串包含冒号。但我坚持了下来,找到了解决办法:

      gsub(/([\{|\,}])\s*([a-zA-Z]+):/, '\1 "\2":')
      

      【讨论】:

      • 问题是你的正则表达式也会替换引用值中的“key=”实例,这是不想要的。
      【解决方案6】:

      这就是我必须解决的方法:

      JSON.parse(broken_json_string.gsub(/'([^']+)':/, '"\1":'))
      

      上面的一些假设键只包含字母;我们的一些包含下划线、空格等。更容易说“任何不是单引号的字符”(在我们的例子中,所有键都用单引号括起来)。

      【讨论】:

        猜你喜欢
        • 2013-03-27
        • 2018-06-08
        • 1970-01-01
        • 1970-01-01
        • 2019-01-29
        • 1970-01-01
        • 2020-03-26
        • 2019-08-05
        • 2020-01-17
        相关资源
        最近更新 更多