如何让 Ruby JSON 解析器忽略 json_class?

发布于 2024-10-15 07:15:50 字数 125 浏览 7 评论 0原文

我有一个序列化的 JSON 字符串(实际上是 Chef 角色定义),它有一个 json_class 键,使 ruby​​ JSON 解析器尝试强制它成为 Chef::Role 对象。如何让解析器忽略这个键并简单地反序列化为正常的哈希值?

I have a serialized JSON String (chef role definition actually) and it has a json_class key, making the ruby JSON parser try to force it to be a Chef::Role object. How can I make the parser ignore this key and just simply deserialize into a normal Hash?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

遮云壑 2024-10-22 07:15:50

我刚刚遇到了同样的问题,并通过阅读 JSON gem 的源代码找到了答案 - 只需在尝试进行解析之前取消设置 JSON.create_id:

JSON.create_id = nil
JSON.parse('{ "json_class": "Chef::Role" }').class => Hash

编辑: 请注意,自 gem 版本 1.7 以来(当我输入此更新时,1.8.0 是最新的),不再需要上述 hack。 JSON#parse 现在忽略 json_class,并且应使用 JSON#load 来解组转储对象。

I just had the same problem, and found the answer by reading the source of the JSON gem - just unset JSON.create_id before trying to do the parse:

JSON.create_id = nil
JSON.parse('{ "json_class": "Chef::Role" }').class => Hash

EDIT: Note that since version 1.7 of the gem (1.8.0 is current as I type this update), the above hack is no longer necessary. JSON#parse now ignores json_class, and JSON#load should be used instead for unmarshalling dumped objects.

Oo萌小芽oO 2024-10-22 07:15:50

“json_class”键用于指示 json 应该被解组为哪个对象。它是通过 JSON.dump 添加的。在 JSON 的最新版本中,JSON.parse 将忽略“json_class”,解组为哈希。而 JSON.load 将解组到指定的对象(在您的情况下,为 Chef::Role)。

JSON.parse('{ "json_class": "Chef::Role" }').class => Hash
JSON.load('{ "json_class": "Chef::Role" }').class => Chef::Role

The "json_class" key is there to indicate what Object the json should be un-marshaled as. It is added by JSON.dump. In more recent versions of JSON, JSON.parse will ignore "json_class", un-marshaling to a Hash. While JSON.load will un-marshal to the indicated Object (in your case, a Chef::Role).

JSON.parse('{ "json_class": "Chef::Role" }').class => Hash
JSON.load('{ "json_class": "Chef::Role" }').class => Chef::Role
甜味拾荒者 2024-10-22 07:15:50

可以在解析之前对字符串执行gsub,并将该特定键更改为其他内容。

展示前后的 JSON 示例怎么样?

You could do a gsub on the string before parsing, and change that particular key to something else.

How about showing a sample of the JSON before and after?

何止钟意 2024-10-22 07:15:50

根据文档,马克·里德的解决方案当然应该有效。但是当我在 Vagrantfile 中尝试此操作时:

JSON.create_id = nil
vagrant_json = JSON.parse(Pathname(__FILE__).dirname.join('nodes', "#{node_name}.json").read)

config.vm.provision :chef_solo do |chef|
  chef.cookbooks_path = ["cookbooks", "site-cookbooks"]
  chef.roles_path = "roles"
  chef.data_bags_path = "data_bags"
  chef.node_name = node_name
  chef.run_list = vagrant_json.delete('run_list')
  chef.json = vagrant_json
end

vagrant_json.class 是一个哈希,但每当 node.json 文件包含 "json_class": "Chef::Node" 时,它仍然在内部保留 json_class 值入口。然后,当使用哈希设置最后一行中的 Chef.json 值时,再次使用 json 类对其进行解释(奇怪的是,结果是一个空的运行列表。)

这是有效的。相同的想法,但技巧稍差一些:

vagrant_json = JSON.parse(Pathname(__FILE__).dirname.join('nodes', "#{node_name}.json").read)
vagrant_json['json_class'] = nil  # <== This worked

config.vm.provision :chef_solo do |chef|
  chef.cookbooks_path = ["cookbooks", "site-cookbooks"]
  chef.roles_path = "roles"
  chef.data_bags_path = "data_bags"
  chef.node_name = node_name
  chef.run_list = vagrant_json.delete('run_list')
  chef.json = vagrant_json
end

此代码用于从 Chef 节点文件设置 json 属性和运行列表,无论有或没有 "json_class": "Chef::Node" 条目。

总之,关于从 JSON.parse 获取哈希,前面的答案似乎完全正确,但如果您不从该哈希中删除 json_class 对,以后仍然可能会出现问题,就像在本例中一样。

According to the docs, Mark Reed's solution should certainly work. But when I tried this in a Vagrantfile:

JSON.create_id = nil
vagrant_json = JSON.parse(Pathname(__FILE__).dirname.join('nodes', "#{node_name}.json").read)

config.vm.provision :chef_solo do |chef|
  chef.cookbooks_path = ["cookbooks", "site-cookbooks"]
  chef.roles_path = "roles"
  chef.data_bags_path = "data_bags"
  chef.node_name = node_name
  chef.run_list = vagrant_json.delete('run_list')
  chef.json = vagrant_json
end

the vagrant_json.class was a Hash, but it still retained the json_class value internally whenever the node.json file contained the "json_class": "Chef::Node" entry. Then when using the hash to set the chef.json value in the last line, it was again interpreted using the json class (and the result was, oddly, an empty run list.)

Here's what worked. Same idea, but a bit less finesse:

vagrant_json = JSON.parse(Pathname(__FILE__).dirname.join('nodes', "#{node_name}.json").read)
vagrant_json['json_class'] = nil  # <== This worked

config.vm.provision :chef_solo do |chef|
  chef.cookbooks_path = ["cookbooks", "site-cookbooks"]
  chef.roles_path = "roles"
  chef.data_bags_path = "data_bags"
  chef.node_name = node_name
  chef.run_list = vagrant_json.delete('run_list')
  chef.json = vagrant_json
end

This code worked to set both the json attributes and the run list from a Chef node file, with or without the "json_class": "Chef::Node" entry.

In summary, the previous answer does seem completely correct in relation to getting a hash from JSON.parse, but if you don't remove the json_class pair from that hash, there can still be trouble later, as in this case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文