简体   繁体   English

在 python 中从 JSON 创建可逆的 YAML

[英]Create reversible YAML from JSON in python

While trying to create YAML from a JSON in python, using the PyYAML library, I am able to convert the JSON into YAML.在尝试从 Python 中的 JSON 创建 YAML 时,使用 PyYAML 库,我能够将 JSON 转换为 YAML。 However, in the YAML I receive as a result, all the brackets of JSON are unfolded whereas I want to retain few square brackets (lists) from JSON to converted YAML.但是,在我收到的 YAML 中,JSON 的所有括号都展开了,而我想保留从 JSON 到转换后的 YAML 的几个方括号(列表)。 How can I request this library call to not unfold lists from JSON into YAML, but rather retain it as a list?如何请求此库调用不将列表从 JSON 展开为 YAML,而是将其保留为列表?

A snapshot of my issue follows:我的问题的快照如下:

import yaml
import json

original_json = {'a': {'next': ['b'], 'prev': []},
 'b': {'next': ['c'], 'prev': ['a']},
 'c': {'next': ['d', 'e'], 'prev': ['b']},
 'd': {'next': [], 'prev': ['c']},
 'e': {'next': ['f'], 'prev': ['c']},
 'f': {'next': [], 'prev': ['e']}}

obtained_yaml = yaml.dump(yaml.load(json.dumps(original_json)), default_flow_style=False)

# obtained_yaml looks like
#
# a:
#   next:
#   - b
#   prev: []
# b:
#   next:
#   - c
#   prev:
#   - a
# c:
#   next:
#   - d
#   - e
#   prev:
#   - b
# d:
#   next: []
#   prev:
#   - c
# e:
#   next:
#   - f
#   prev:
#   - c
# f:
#   next: []
#   prev:
#   - e

# expected yaml should look like
#
# a:
#   next:
#   - ["b"]
#   prev: []
# b:
#   next:
#   - ["c"]
#   prev:
#   - ["a"]
# c:
#   next:
#   - ["d"]
#   - ["e"]
#   prev:
#   - ["b"]
# d:
#   next: []
#   prev:
#   - ["c"]
# e:
#   next:
#   - ["f"]
#   prev:
#   - ["c"]
# f:
#   next: []
#   prev:
#   - ["e"]

I tried few ways to solve this out but all that did not work in the way expected json should come out.我尝试了几种方法来解决这个问题,但所有这些都没有按照预期的 json 出现的方式工作。 Need suggestions on how to get it done.需要有关如何完成它的建议。

Yaml syntax defines a different list structure where members of a list are lines beginning at the same indentation level starting with a - (a dash and a space). Yaml 语法定义了一个不同的列表结构,其中列表的成员是从相同缩进级别开始的行,以- (破折号和空格)开头。 If you want to keep the brackets, you will need to cast your list into a str - But then you will lose the ability to reverse the YAML into JSON.如果你想保留括号,你需要将你的列表转换为str - 但是你将失去将 YAML 反转为 JSON 的能力。

Here's an example where you can see that even if you can get ["a"] into [["a"]] - YAML tranforms it into a double indented list:这是一个示例,您可以看到即使您可以将["a"]放入[["a"]] - YAML 将其转换为双缩进列表:

In [4]: import yaml
   ...: import json
   ...: import collections
   ...: original_json = {'a': {'next': ['b'], 'prev': []},
   ...:  'b': {'next': ['c'], 'prev': ['a']},
   ...:  'c': {'next': ['d', 'e'], 'prev': ['b']},
   ...:  'd': {'next': [], 'prev': ['c']},
   ...:  'e': {'next': ['f'], 'prev': ['c']},
   ...:  'f': {'next': [], 'prev': ['e']}}
   ...:
   ...: mod_json = collections.defaultdict(dict)
   ...: for k, v in original_json.items():
   ...:     mod_json[k]["next"] = [v["next"]]
   ...:     mod_json[k]["prev"] = [v["prev"]]
   ...: obtained_yaml = yaml.dump(yaml.load(json.dumps(mod_json)), default_flow_style=False)
   ...:
   ...:

In [5]: obtained_yaml
Out[5]: 'a:\n  next:\n  - - b\n  prev:\n  - []\nb:\n  next:\n  - - c\n  prev:\n  - - a\nc:\n  next:\n  - - d\n    - e\n  prev:\n  - - b\nd:\n  next:\n  - []\n  prev:\n  - - c\ne:\n  next:\n  - - f\n  prev:\n  - - c\nf:\n  next:\n  - []\n  prev:\n  - - e\n'

Only YAML 1.2 is a superset of JSON, YAML 1.1 is not and although YAML 1.2 was released in 2009, unfortunately PyYAML has not been updated since then.只有 YAML 1.2 是 JSON 的超集,YAML 1.1 不是,虽然 YAML 1.2 于 2009 年发布,但遗憾的是 PyYAML 自那时以来一直没有更新。 Your example is a JSON subset that is compatible with YAML 1.1, but in general it is not a good idea to try and use PyYAML for this.您的示例是与 YAML 1.1 兼容的 JSON 子集,但一般来说,尝试使用 PyYAML 不是一个好主意。

There are other native libraries for Python, another one is ruamel.yaml (disclaimer: I am the author of that package) and that implements YAML 1.2 and gives you full control over block vs flow style dumping of individual collections. Python 还有其他本机库,另一个是ruamel.yaml (免责声明:我是该包的作者),它实现了 YAML 1.2 并让您完全控制块与流样式转储单个集合。 Of course you still have the general YAML restriction that you cannot have a block style collection within a flow style collection).当然,您仍然有一般的 YAML 限制,即您不能在流样式集合中拥有块样式集合)。

PyYAML, and ruamel.yaml in non-round-trip-mode, only allow you to have all block, or all flow, or all block with leaf-nodes in flow style. PyYAML 和非往返模式下的 ruamel.yaml 只允许您拥有所有块,或所有流,或所有带有流样式的叶节点的块。 But the default, round-trip-mode, allows finer grained control using the .fa attribute on collections:但是默认的往返模式允许在集合上使用.fa属性进行更细粒度的控制:

import sys
import json
import ruamel.yaml


original_json = {'a': {'next': ['b'], 'prev': []},
 'b': {'next': ['c'], 'prev': ['a']},
 'c': {'next': ['d', 'e'], 'prev': ['b']},
 'd': {'next': [], 'prev': ['c']},
 'e': {'next': ['f'], 'prev': ['c']},
 'f': {'next': [], 'prev': ['e']}}

json_string = json.dumps(original_json)

yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=4, sequence=4, offset=2)
# yaml.preserve_quotes = True
data = yaml.load(json_string)

# the following sets flow-style for the root level mapping only
data.fa.set_block_style()
yaml.dump(data, sys.stdout)

which gives:这使:

a: {next: [b], prev: []}
b: {next: [c], prev: [a]}
c: {next: [d, e], prev: [b]}
d: {next: [], prev: [c]}
e: {next: [f], prev: [c]}
f: {next: [], prev: [e]}

you can of course recursively traverse your data structure and call .fa.set_block_style() depending on any criteria you want.您当然可以递归遍历您的数据结构并根据您想要的任何条件调用.fa.set_block_style()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM