处理 JSON 序列化变化的正确方法

Proper way to deal with variations in JSON serialization

提问人:sutee 提问时间:10/7/2008 最后编辑:John Millikinsutee 更新时间:8/24/2013 访问量:3309

问:

我有一个使用 Python 的 SimpleJSON 序列化 JSON 的 Web 服务,以及一个使用 Google 的可视化 API 的 javascript/客户端。当我尝试使用 Google Data Table 的 Query 方法读取 JSON 响应时,我收到“无效标签”错误。

我注意到 Google 电子表格输出的 JSON 在对象键周围没有引号。我尝试在没有引号的情况下阅读 JSON,这很有效。我想知道使用以下命令将 SimpleJSON 输出读入 Google 数据的最佳方法是什么

query = new google.visualization.Query("http://www.myuri.com/api/").

我可以使用正则表达式来删除引号,但这似乎很草率。我尝试过的 javascript JSON 解析库不会在没有引号的情况下读取 JSON 语法。

这里有一些很好的背景阅读材料:围绕对象键的引号:

http://simonwillison.net/2006/Oct/11/json/

JSON 序列化 google-visualization simplejson

评论


答:

3赞 John Millikin 10/7/2008 #1

您确定 Google API 需要 JSON 吗?根据我的经验,Google 的 API 往往不会像你所描述的那样被大规模破坏——可能是他们实际上期待一种不同的格式,只是类似于 JSON。


进一步浏览会发现以 Google 期望的格式检索数据的说明:

例如,要获取 dataSourceUrl 在 Google 电子表格中,执行 以后:

  1. 在电子表格中,选择单元格区域。
  2. 从菜单中选择“插入”,然后选择“小工具”。
  3. 通过单击右上角的选择器打开小工具的菜单。
  4. 选择菜单选项“获取数据源 URL”。

我这样做了,并在浏览器中打开了URL。它返回的数据肯定不是 JSON:

google.visualization.Query.setResponse(
{requestId:'0',status:'ok',signature:'1464883469881501252',
table:{cols: [{id:'A',label:'',type:'t',pattern:''},
{id:'B',label:'',type:'t',pattern:''}],
rows: [[{v:'a'},{v:'h'}],[{v:'b'},{v:'i'}],[{v:'c'},{v:'j'}],[{v:'d'},{v:'k'}],[{v:'e'},{v:'l'}],[{v:'f'},{v:'m'}],[{v:'g'},{v:'n'}]]}});

看起来结果旨在由浏览器直接执行。尝试修改代码以执行如下操作:

# old
return simplejson.dumps ({"requestId": 1, "status": "ok", ...})

# new
json = simplejson.dumps ({"requestId": 1, "status": "ok", ...})
return "google.visualization.Query.setResponse(%r);" % json

评论

0赞 sutee 10/7/2008
我尝试用Query.setResponse()包装json。无效标签错误消失了,但现在有些东西在此过程中超时了。我执行query.send,并在回调中执行响应。DataTable() 中。看起来它在那里超时了。不过,我认为这是正确的方向。
1赞 Ates Goral 10/7/2008 #2

“invalid label”错误通常是由于 JSON 字符串上的盲 eval() 导致属性名称被误认为标签(因为它们具有相同的语法 -- “foo:”)。

eval("{ foo: 42, bar: 43 }"); // Results in invalid label

快速补救措施是确保您的 JSON 字符串有括号括大括号:

eval("({ foo: 42, bar: 43 })"); // Works

尝试将 JSON 字符串括在括号中,以查看“无效标签”错误是否消失。

评论

0赞 John Millikin 10/7/2008
将 JSON 包装在 parens 中会使其无效。
0赞 Ates Goral 10/7/2008
这就是我说“尝试”的原因。这是一个故障排除步骤,用于尝试查看“无效标签”错误是否与它有关。
0赞 Ates Goral 4/20/2009
此外,它只是一个预处理步骤;不应在原始 JSON 中包含括号。无论如何,这是一个有争议的问题,因为你一开始就不应该使用 eval() :)
0赞 nickl- 10/19/2012 #3

事实证明:mod:json也会在单引号中的字符串中窒息。不过,这将解决问题:

在 python 中将 JavaScript 对象解析为 JSON:

溶液:

>>> from re import sub
>>> import json
>>> js = "{ a: 'a' }"
>>> json.loads(sub("'", '"', sub('\s(\w+):', r' "\1":', js)))
{u'a': u'a'}

编辑:(边缘案例已审查)

因此,有人提出,建议的解决方案无法应对所有情况,特别是类似

例如,{foo: “a sentence: right here!”} 将更改为 {“foo”: “a ”sentence“: right here!”}
– 杰森 S Apr 12 at 18:03

为了解决这个问题,我们只需要确保我们实际上使用的是一个键,而不仅仅是字符串中的冒号,所以我们在魔术后面做了一些观察,以暗示逗号(,)或大括号({)的存在,以确保我们正确,如下所示:

字符串中的冒号:

>>> js = "{foo: 'a sentence: right here!'}"
>>> json.loads(sub("'", '"', sub('(?<={|,)\s*(\w+):', r' "\1":', js)))
{u'foo': u'a sentence: right here!'}

这当然与执行以下操作相同:

>>> js = "{foo: 'a sentence: right here!'}"
>>> json.loads(sub('(?<={|,)\s*(\w+):', r' "\1":', js).replace("'",'"'))
{u'foo': u'a sentence: right here!'} 

但后来我指出,这不是唯一的缺陷,因为引号呢:

如果我们还担心转义引号,我们将不得不更具体地说明字符串的构成。第一个引号将跟在大括号({)、空格(\s)或冒号(:)后面,而最后一个匹配的引号将出现在逗号(,)或右大括号(})之前,那么我们可以将介于两者之间的所有内容视为同一字符串的一部分,如下所示:

字符串中的附加引号:

>>> js = "{foo: 'a sentence: it\'s right here!'}"
>>> json.loads(
...     sub("(?<=\s|{|:)'(.*?)'(?=,|})", 
...         r'"\1"', 
...         sub('(?<={|,)\s*(\w+):', r' "\1":', js))
...     )
{u'foo': u"a sentence: it's right here!"}

关注这个领域,因为更多的边缘案例被揭示和解决。你能发现另一个吗?

或者对于更复杂的东西,一个真实世界的例子,如:npm view

从:

{ name: 'chuck',
      description: 'Chuck Norris joke dispenser.',
      'dist-tags': { latest: '0.0.3' },
      versions: '0.0.3',
      maintainers: 'qard ',
      time: { '0.0.3': '2011-08-19T22:00:54.744Z' },
      author: 'Stephen Belanger ',
      repository: 
           { type: 'git',
             url: 'git://github.com/qard/chuck.git' },
          version: '0.0.3',
          dependencies: { 'coffee-script': '>= 1.1.1' },
          keywords: 
               [ 'chuck',
                 'norris',
                 'jokes',
                 'funny',
                 'fun' ],
              bin: { chuck: './bin/chuck' },
              main: 'index',
              engines: { node: '>= 0.4.1 < 0.5.0' },
              devDependencies: {},
              dist: 
                   { shasum: '3af700056794400218f99b7da1170a4343f355ec',
                     tarball: 'http://registry.npmjs.org/chuck/-/chuck-0.0.3.tgz' },
                  scripts: {},
                  directories: {},
                  optionalDependencies: {} }

自:

{u'author': u'Stephen Belanger ',
     u'bin': {u'chuck': u'./bin/chuck'},
     u'dependencies': {u'coffee-script': u'>= 1.1.1'},
     u'description': u'Chuck Norris joke dispenser.',
     u'devDependencies': {},
     u'directories': {},
     u'dist': {u'shasum': u'3af700056794400218f99b7da1170a4343f355ec',
      u'tarball': u'http://registry.npmjs.org/chuck/-/chuck-0.0.3.tgz'},
     u'dist-tags': {u'latest': u'0.0.3'},
     u'engines': {u'node': u'>= 0.4.1 < 0.5.0'},
     u'keywords': [u'chuck', u'norris', u'jokes', u'funny', u'fun'],
     u'main': u'index',
     u'maintainers': u'qard ',
     u'name': u'chuck',
     u'optionalDependencies': {},
     u'repository': {u'type': u'git', u'url': u'git://github.com/qard/chuck.git'},
     u'scripts': {},
     u'time': {u'0.0.3': u'2011-08-19T22:00:54.744Z'},
     u'version': u'0.0.3',
     u'versions': u'0.0.3'}

对我有用=)

nJoy!

评论

0赞 Jason S 4/13/2013
我不喜欢对人们投反对票,但是如果JSON对象中有一个与您的正则表达式匹配的字符串,则您的解决方案会出现错误,例如 将被更改为无法正确解析。{foo: "a sentence: right here!"}{"foo": "a "sentence": right here!"}
0赞 nickl- 4/22/2013
如果我们吹毛求疵,也许你应该指出我完全无视现有的双引号或转义引号的存在。考虑到,按顺序看到三个引文的结果会很有趣。正如您所建议的那样,这些都不否定概念证明。尽管如此,我还是会答应的......只要时间允许,我就会修复提到的缺陷,希望你能回报你,把消极的行动扭转成更积极的东西。=)
0赞 Jason S 4/23/2013
绝对!发表评论提醒我+我会相应地投票。