我是 python 的新手,所以我正在构建一个简单的程序来将 YAML 解析为 JSON 以及将 JSON 解析为 YAML。
yaml2json
在一行中将 YAML 转换为 JSON,但 JSON 验证器认为它是正确的。
到目前为止,这是我的代码:
def parseyaml(inFileType, outFileType):
infile = input('Please enter a {} filename to parse: '.format(inFileType))
outfile = input('Please enter a {} filename to output: '.format(outFileType))
with open(infile, 'r') as stream:
try:
datamap = yaml.safe_load(stream)
with open(outfile, 'w') as output:
json.dump(datamap, output)
except yaml.YAMLError as exc:
print(exc)
print('Your file has been parsed.\n\n')
def parsejson(inFileType, outFileType):
infile = input('Please enter a {} filename to parse: '.format(inFileType))
outfile = input('Please enter a {} filename to output: '.format(outFileType))
with open(infile, 'r') as stream:
try:
datamap = json.load(stream)
with open(outfile, 'w') as output:
yaml.dump(datamap, output)
except yaml.YAMLError as exc:
print(exc)
print('Your file has been parsed.\n\n')
原始 YAML 与新 YAML 的示例
原文:
inputs:
webTierCpu:
type: integer
minimum: 2
default: 2
maximum: 5
title: Web Server CPU Count
description: The number of CPUs for the Web nodes
新:
inputs:
dbTierCpu: {default: 2, description: The number of CPUs for the DB node, maximum: 5,
minimum: 2, title: DB Server CPU Count, type: integer}
它看起来不像是对所有 JSON 进行解码,所以我不确定下一步应该去哪里......
最佳答案
您的文件正在丢失其格式,因为原始的 dump
例程
默认情况下以 YAML 流式写入所有叶节点,而您的输入是 block 式
一路走来。
您还丢失了键的顺序,首先是因为 JSON 解析器
使用 dict,其次是因为 dump
对输出进行排序。
如果您查看中间 JSON,您已经看到键顺序是
在那一点上消失了。要保留它,请使用新的 API 加载您的 YAML
并有一个特殊的 JSON 编码器作为转储的替代品,可以
处理加载 YAML 的 Mapping
的子类,类似于
this例子
来自标准 Python 文档。
假设您的 YAML 存储在 input.yaml
中:
import sys
import json
from collections.abc import Mapping, Sequence
from collections import OrderedDict
import ruamel.yaml
# if you instantiate a YAML instance as yaml, you have to explicitly import the error
from ruamel.yaml.error import YAMLError
yaml = ruamel.yaml.YAML() # this uses the new API
# if you have standard indentation, no need to use the following
yaml.indent(sequence=4, offset=2)
input_file = 'input.yaml'
intermediate_file = 'intermediate.json'
output_file = 'output.yaml'
class OrderlyJSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, Mapping):
return OrderedDict(o)
elif isinstance(o, Sequence):
return list(o)
return json.JSONEncoder.default(self, o)
def yaml_2_json(in_file, out_file):
with open(in_file, 'r') as stream:
try:
datamap = yaml.load(stream)
with open(out_file, 'w') as output:
output.write(OrderlyJSONEncoder(indent=2).encode(datamap))
except YAMLError as exc:
print(exc)
return False
return True
yaml_2_json(input_file, intermediate_file)
with open(intermediate_file) as fp:
sys.stdout.write(fp.read())
给出:
{
"inputs": {
"webTierCpu": {
"type": "integer",
"minimum": 2,
"default": 2,
"maximum": 5,
"title": "Web Server CPU Count",
"description": "The number of CPUs for the Web nodes"
}
}
}
您看到您的 JSON 具有适当的键顺序,我们也
需要在加载时保存。你可以在没有子类化的情况下做到这一点
任何东西,通过指定将 JSON 对象加载到的子类中
映射
,YAML 解析器通过提供object_pairs_hook
在内部使用。
from ruamel.yaml.comments import CommentedMap
def json_2_yaml(in_file, out_file):
with open(in_file, 'r') as stream:
try:
datamap = json.load(stream, object_pairs_hook=CommentedMap)
# if you need to "restore" literal style scalars, etc.
# walk_tree(datamap)
with open(out_file, 'w') as output:
yaml.dump(datamap, output)
except yaml.YAMLError as exc:
print(exc)
return False
return True
json_2_yaml(intermediate_file, output_file)
with open(output_file) as fp:
sys.stdout.write(fp.read())
哪些输出:
inputs:
webTierCpu:
type: integer
minimum: 2
default: 2
maximum: 5
title: Web Server CPU Count
description: The number of CPUs for the Web nodes
而且我希望这与您的原始输入足够相似以便可以接受。
注意事项:
在使用新 API 时,我倾向于使用
yaml
作为 API 的名称ruamel.yaml.YAML()
的实例,而不是from ruamel import yaml
。然而,这掩盖了yaml.YAMLError
的使用,因为 错误类不是YAML()
的属性
如果您正在开发此类内容,我建议您删除 至少来自实际功能的用户输入。它应该是 编写
parseyaml
和parsejson
来调用yaml_2_json
很简单。json_2_yaml
。原始 YAML 文件中的任何注释都将丢失,尽管 ruamel.yaml 可以加载它们。 JSON 最初确实允许注释,但现在 不在规范中,而且我知道没有任何解析器可以输出注释。
由于您的真实文件具有文字 block 标量,您必须使用一些魔法来取回它们。
包括以下遍历树的函数,递归到 dict 值和列表元素中,并将带有嵌入式换行符的任何行转换为一种类型,该类型将输出到 YAML 作为就地文字 block 样式标量(因此没有返回值):
from ruamel.yaml.scalarstring import PreservedScalarString, SingleQuotedScalarString
from ruamel.yaml.compat import string_types, MutableMapping, MutableSequence
def preserve_literal(s):
return PreservedScalarString(s.replace('\r\n', '\n').replace('\r', '\n'))
def walk_tree(base):
if isinstance(base, MutableMapping):
for k in base:
v = base[k] # type: Text
if isinstance(v, string_types):
if '\n' in v:
base[k] = preserve_literal(v)
elif '${' in v or ':' in v:
base[k] = SingleQuotedScalarString(v)
else:
walk_tree(v)
elif isinstance(base, MutableSequence):
for idx, elem in enumerate(base):
if isinstance(elem, string_types):
if '\n' in elem:
base[idx] = preserve_literal(elem)
elif '${' in elem or ':' in elem:
base[idx] = SingleQuotedScalarString(elem)
else:
walk_tree(elem)
然后做
walk_tree(datamap)
从 JSON 加载数据后。
对于以上所有内容,您的 Wordpress.yaml
文件中应该只有一行不同。
关于Python YAML 到 JSON 到 YAML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51914505/