Python YAML 到 JSON 到 YAML

标签 python json yaml

我是 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()

  • 的属性
  • 如果您正在开发此类内容,我建议您删除 至少来自实际功能的用户输入。它应该是 编写 parseyamlparsejson 来调用 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/

相关文章:

sql-server - 使用 OPENJSON 时存储过程中关键字 'with' 附近的语法不正确

java - 从 Kubernetes 运行梁数据流作业

python - 用公式逻辑决定游戏结果

python - 使用 Keras 在图形模式下将张量转换为不规则张量

python - 如何在Python中将datetime.date对象转换为字符串

javascript - 使用 vanilla Javascript 将字符串列表发送到 ASP.net api Controller

python - 用 Django 打开 ID

python - Numpy 方法返回数组数组中数组出现的索引

ansible - 如何使用 regex_replace 有条件地替换列表中的文本?

java - 如何使用 Bukkit API 从 .yaml 文件加载树/索引?