在 json 嵌入式 YAML 文件中 - 使用 Python 仅替换 json 值

In a json embedded YAML file - replace only json values using Python

我有一个 YAML 文件如下:

api: v1
hostname: abc
metadata:
  name: test
  annotations: {
    "ip" : "1.1.1.1",
    "login" : "fad-login",
    "vip" : "1.1.1.1",
    "interface" : "port1",
    "port" : "443"
  }

我正在尝试从文件中读取此数据,仅替换 ipvip 的值并将其写回文件。

我试过的是:

open ("test.yaml", w) as f:
    yaml.dump(object, f) #this does not help me since it converts the entire file to YAML

还有 json.dump() 也不起作用,因为它将整个文件转换为 JSON。它需要采用相同的格式,但需要更新值。我该怎么做?

你拥有的不是嵌入了 JSON 的 YAML,它是 YAML annotations 的一些值 采用 YAML 流样式(它是 JSON 的超集,因此与它非常相似)。

这将是 嵌入了 YAML JSON:

api: v1
hostname: abc
metadata:
  name: test
  annotations: |
    {
      "ip" : "1.1.1.1",
      "login" : "fad-login",
      "vip" : "1.1.1.1",
      "interface" : "port1",
      "port" : "443"
    }

此处 annotations 的值是一个字符串,您可以将其传递给 JSON 解析器。

您可以只加载文件,修改它并转储。这将改变布局 flow-style 部分,但这不会影响任何以下解析器:

import sys
import ruamel.yaml

file_in = Path('input.yaml')
    
yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
yaml.width = 1024
data = yaml.load(file_in)
annotations = data['metadata']['annotations']
annotations['ip'] = type(annotations['ip'])('4.3.2.1')
annotations['vip'] = type(annotations['vip'])('1.2.3.4')
yaml.dump(data, sys.stdout)

给出:

api: v1
hostname: abc
metadata:
  name: test
  annotations: {"ip": "4.3.2.1", "login": "fad-login", "vip": "1.2.3.4", "interface": "port1", "port": "443"}

type(annotations['vip'])() 确定输出中的替换字符串具有相同的 引用原文。

ruamel.yaml 目前不保留流样式中的换行符 mapping/sequence。 如果这必须以最小的机会返回到某个存储库,您可以这样做:

import sys
import ruamel.yaml

file_in = Path('input.yaml')

def rewrite_closing_curly_brace(s):
    res = []
    for line in s.splitlines():
        if line and line[-1] == '}':
            res.append(line[:-1])
            idx = 0
            while line[idx] == ' ':
                idx += 1
            res.append(' ' * (idx - 2) + '}')
            continue
        res.append(line)
    return '\n'.join(res) + '\n'
    
yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
yaml.width = 15
data = yaml.load(file_in)
annotations = data['metadata']['annotations']
annotations['ip'] = type(annotations['ip'])('4.3.2.1')
annotations['vip'] = type(annotations['vip'])('1.2.3.4')
yaml.dump(data, sys.stdout, transform=rewrite_closing_curly_brace)

给出:

api: v1
hostname: abc
metadata:
  name: test
  annotations: {
    "ip": "4.3.2.1",
    "login": "fad-login",
    "vip": "1.2.3.4",
    "interface": "port1",
    "port": "443"
  }

此处的 15 宽度当然高度依赖于您的文件,如果它们可能会影响其他行 更长。在那种情况下,您可以将其省略,然后进行包装 rewrite_closing_curly_brace() 确实拆分并缩进了整个流样式部分。

请注意,您的原始输出和转换后的输出是无效的 YAML, 为了向后兼容,ruamel.yaml 接受了它。根据 YAML 规范右大括号应比 annotation

的开头缩进更多