1

我正在向第三方 API 编写客户端,它们以奇怪的格式提供数据。起初,它可能看起来像 JSON,但事实并非如此,我对如何处理它有点困惑。

它是一种基于键值的格式(很像 JSON)。

  • 键与它们的值用“=”分隔。
  • 键和值用双引号括起来。
  • 字典以“{”开头,以“}”结尾。
  • 数组以 '(' 开头并以 ')' 结尾
  • 行以 ';' 结尾 (数组内容除外)和行尾字符(\ri think)。
  • 有时,字符串中似乎有 unicode(像 \U2623 这样的东西表示 BioHazard 标志)。

这种格式可能是什么?我应该使用预制的 gem 来解析它,还是应该构建自己的解析器?

{ "anArray" = (
  "100",
  "200",
  "300"
  );
  "aDictionary" = {
    "aString" = "Something";
  };
}

编辑这种格式似乎是 Apple 的属性列表,但它既不是 XML,也不是二进制......这是有道理的,因为 API 来自 WebObjects Web 服务。我会尝试使用 CFPropertyList gem 来解析它,如果有更好的解决方案,请告诉我。

编辑 2这是NextSTEP 属性列表

4

2 回答 2

1

这是使用基于StringScanner的自定义解析器的可靠答案。它允许空白是可选的,允许在列表中的最后一项之后使用尾随逗号,并允许在最后一个字典键/值对之后省略分号。它允许最外层的项目是字典、数组或字符串。它实际上允许任何类型的合法字符串内容,包括括号和花括号以及转义文本,例如\n.

在行动中看到:

p parse('{ "array" = ( "1", "2", ( "3", "4" ) ); "hash"={ "key"={ "more"="oh}]yes;!"; }; }; }')
#=> {"array"=>["1", "2", ["3", "4"]], "hash"=>{"key"=>{"more"=>"oh}]yes;!"}}}

puts parse('("Escaped \"Quotes\" Allowed", "And Unicode \u2623 OK")')
#=> Escaped "Quotes" Allowed
#=> And Unicode ☣ OK

编码:

require 'strscan'
def parse(str)
  ss, getstr, getary, getdct = StringScanner.new(str)
  getvalue = ->{
    if    ss.scan /\s*\{\s*/   then getdct[]
    elsif ss.scan /\s*\(\s*/   then getary[]
    elsif str = getstr[]       then str
    elsif ss.scan /\s*[)}]\s*/ then nil end
  }
  getstr = ->{
    if str=ss.scan(/\s*"(?:[^"\\]|\\u\d+|\\.)*"\s*/i)
      eval str.gsub(/([^\\](?:\\\\)*)#(?=[{@$])/,'\1\#')
    end
  }
  getary = ->{
    [].tap do |a|
      while v=getvalue[]
        a << v
        ss.scan /\s*,\s*/
      end
    end
  }
  getdct = ->{
    {}.tap do |h|
      while key = getstr[]
        ss.scan /\s*=\s*/
        if value=getvalue[] then h[key]=value; ss.scan(/\s*;\s*/) end
        end
      end
    end
  }
  getvalue[]
end

作为将来从头开始构建自己的解析器的替代方案,您可能还想研究Treetop Ruby 库。


编辑:我已经getstr用一个应该防止在eval. 有关更多详细信息,请参阅“评估不带插值的字符串”。在行动中看到:

@secret = "OH NO!"
$secret = "OH NO!"
@@secret = "OH NO!"
puts parse('"\"#{:NOT&&:very}\" bad. \u262E\n#@secret \\#$secret \\\\#@@secret"')
于 2013-05-22T03:13:56.980 回答
1

这是一个非常快速和肮脏的 hack,它将语法转换为有效的 Ruby,然后对其进行评估。请注意,这可能很危险。更重要的是,这会将键和值内的所有括号转换为方括号。

def parse(str)
  eval(
    str
      .gsub( /" = (?=[({"])/, '" => ' )      # Dictionary separators become =>
      .gsub( /(?<=[)}"]); (?=[)}"])/, ', ' ) # Dictionary semicolons become ,
      .tr( '()', '[]' )                      # ALL parens become square brackets
  )
end

p parse('{ "anArray" = ( "100", "200", "300" ); "aDictionary" = { "aString" = "Something"; }; }')
#=> {"anArray"=>["100", "200", "300"], "aDictionary"=>{"aString"=>"Something"}}
于 2013-05-22T02:25:21.620 回答