如何在 bash 中将 json 响应转换为 yaml

Posted

技术标签:

【中文标题】如何在 bash 中将 json 响应转换为 yaml【英文标题】:How to convert a json response into yaml in bash 【发布时间】:2019-04-18 08:09:02 【问题描述】:

我使用 jq 从 json 文件中读取数据。我想将结果附加到一个 yaml 文件中,但我没有让它工作。我对 shell 编程很陌生。我的目标是将“用户”附加到 yaml 文件中现有的“用户”数组中。

这是我的 json 文件:

#$DEFAULTS_FILE

"users":
  [
    "name":"pi",
      "gecos": "Hypriot Pirate",
      "sudo":"ALL=(ALL) NOPASSWD:ALL",
      "shell": "/bin/bash",
      "groups":"users,docker,video",
      "plain_text_passwd":"pi",
      "lock_passwd":"false",
      "ssh_pwauth":"true",
      "chpasswd": "expire": false
    ,
    "name":"admin",
      "gecos": "Hypriot Pirate",
      "sudo":"ALL=(ALL) NOPASSWD:ALL",
      "shell": "/bin/bash",
      "primary-group": "users",
      "groups":"users,docker,adm,dialout,audio,plugdev,netdev,video",
      "ssh-import-id":"None",
      "plain_text_passwd":"pi",
      "lock_passwd":"true",
      "ssh_pwauth":"true",
      "chpasswd": "expire: false",
      "ssh-authorized-keys": ["ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local"]
    
  ]
  

我用那个过滤它:

cat $DEFAULTS_FILE | jq .users

我不知道如何将该 json 转换为 yaml。

我的预期结果应该是:

users:
  - name:                pi
    gecos:               "Hypriot Pirate"
    sudo:                ALL=(ALL) NOPASSWD:ALL
    shell:               /bin/bash
    groups:              users,docker,video
    plain_text_passwd:   pi
    lock_passwd:         false
    ssh_pwauth:          true
    chpasswd:  expire:  false 
  - name:                admin
    primary-group:       users
    shell:               /bin/bash
    sudo:                ALL=(ALL) NOPASSWD:ALL
    groups:              users,docker,adm,dialout,audio,plugdev,netdev,video
    ssh-import-id:       None

我尝试使用名为yq 的第二个工具,它类似于jq,可以编写yaml 文件。但我没有积极的进展。

编辑

我知道我可以将内容添加到 yaml 中:

yq w -i "my.yml" "users[+]" "some content"

但我不知道如何将我的 json 合并到其中。

任何帮助或提示都会很好,在此先感谢您...

【问题讨论】:

你可以使用python/perl 内置YAMLJSON 模块 - commandlinefu.com/commands/view/12218/convert-yaml-to-json 正好相反 Bash 本身并不是一个很好的平台。找到一个现有的工具,或者写一个,例如Python。根据您的首选语言,在 Stack Overflow 上找到现有问题并提供建议应该不难; here's a search for Python 请遵循minimal reproducible example 准则。特别是,一个示例 my.yml(即输入)会很有帮助。 yq r input.json 应该可以完成这项工作。 【参考方案1】:

yqjq 的 yaml 包装器

使用 yq 版本 4.8.0:

cat $DEFAULTS_FILE | yq e -P -

eeval 分别处理文件。 eaeval-all 将首先合并文件。 -P--prettyPrint YAML 输出 - 来自标准输入

注意:你也可以采用其他方式(yaml 到 json)yq e -j file.yaml

使用 yq 版本 3.3.2:

cat $DEFAULTS_FILE | yq r -P -

r阅读 -P --prettyPrint - 来自标准输入

【讨论】:

你是个疯子!谢谢老哥! 这个答案需要修改。您的yq 示例是mikefarah/yq 的实现,它不是jq 的包装器。见我的answer 如果使用mikefarah/yq V4,需要使用如下命令:yq eval '.. style= ""' sample.jsoncat sample.json | yq eval '.. style= ""' -。见mikefarah.gitbook.io/yq/usage/convert 其实你只需要cat $DEFAULTS_FILE | yq -y 对于mikefarah/yq 版本4,yq eval -P 是正确的语法。【参考方案2】:
function yaml_validate 
  python -c 'import sys, yaml, json; yaml.safe_load(sys.stdin.read())'


function yaml2json 
  python -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin.read())))'


function yaml2json_pretty 
  python -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin.read()), indent=2, sort_keys=False))'


function json_validate 
  python -c 'import sys, yaml, json; json.loads(sys.stdin.read())'


function json2yaml 
  python -c 'import sys, yaml, json; print(yaml.dump(json.loads(sys.stdin.read())))'

http://github.com/frgomes/bash-scripts 的更多 Bash 技巧

【讨论】:

鉴于yqjq 可能是多余的,但做得非常好,并且可能有一些很好的应用程序。 哈!碰巧的是,我刚刚在 gpc ai 笔记本中安装yq 时遇到了错误。所以,我想这些会派上用场的! 也许是最好的答案,因为它不会让你安装yq 很棒的 bash 脚本。 yq 太臃肿了。谢谢。【参考方案3】:

我不确定您使用什么规则来获得预期结果。您似乎在随机应用不同的规则来转换值的方式。

据我了解,标量值只是按原样输出(使用潜在编码),对象作为键/值对输出,数组对象输出每个项目都带有 -。缩进关联什么是什么的一部分。

因此,如果您要使用 jq,请根据这些规则:

def yamlify:
    (objects | to_entries[] | (.value | type) as $type |
        if $type == "array" then
            "\(.key):", (.value | yamlify)
        elif $type == "object" then
            "\(.key):", "    \(.value | yamlify)"
        else
            "\(.key):\t\(.value)"
        end
    )
    // (arrays | select(length > 0)[] | [yamlify] |
        "  - \(.[0])", "    \(.[1:][])"
    )
    // .
    ;

然后要使用它,请将其添加到您的 .jq 文件并使用它:

$ jq -r yamlify input.json
users:
  - name:       pi
    gecos:      Hypriot Pirate
    sudo:       ALL=(ALL) NOPASSWD:ALL
    shell:      /bin/bash
    groups:     users,docker,video
    plain_text_passwd:  pi
    lock_passwd:        false
    ssh_pwauth: true
    chpasswd:
        expire: false
  - name:       admin
    gecos:      Hypriot Pirate
    sudo:       ALL=(ALL) NOPASSWD:ALL
    shell:      /bin/bash
    primary-group:      users
    groups:     users,docker,adm,dialout,audio,plugdev,netdev,video
    ssh-import-id:      None
    plain_text_passwd:  pi
    lock_passwd:        true
    ssh_pwauth: true
    chpasswd:   expire: false
    ssh-authorized-keys:
      - ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local

这是对齐值的另一个变体

def yamlify2:
    (objects | to_entries | (map(.key | length) | max + 2) as $w |
        .[] | (.value | type) as $type |
        if $type == "array" then
            "\(.key):", (.value | yamlify2)
        elif $type == "object" then
            "\(.key):", "    \(.value | yamlify2)"
        else
            "\(.key):\(" " * (.key | $w - length))\(.value)"
        end
    )
    // (arrays | select(length > 0)[] | [yamlify2] |
        "  - \(.[0])", "    \(.[1:][])"
    )
    // .
    ;
$ jq -r yamlify2 input.json
users:
  - name:               pi
    gecos:              Hypriot Pirate
    sudo:               ALL=(ALL) NOPASSWD:ALL
    shell:              /bin/bash
    groups:             users,docker,video
    plain_text_passwd:  pi
    lock_passwd:        false
    ssh_pwauth:         true
    chpasswd:
        expire:  false
  - name:                 admin
    gecos:                Hypriot Pirate
    sudo:                 ALL=(ALL) NOPASSWD:ALL
    shell:                /bin/bash
    primary-group:        users
    groups:               users,docker,adm,dialout,audio,plugdev,netdev,video
    ssh-import-id:        None
    plain_text_passwd:    pi
    lock_passwd:          true
    ssh_pwauth:           true
    chpasswd:             expire: false
    ssh-authorized-keys:
      - ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local

【讨论】:

真的很酷。喜欢它,不幸的是,它会因多行字符串值而中断.. 基于@Jeff Mercado 的代码,我添加了一些代码来支持多行字符串和单引号的转义。请参阅我的回答“jq 中的解决方案(没有其他工具)”。【参考方案4】:

yq eval -P

带有mikefarah/yq 4.0 版(2020 年 12 月发布),可通过大多数类 Unix 操作系统包管理器安装:通过 Homebrew for macOS (brew install yq)、Debian 和 apt (apt install yq)、Alpine 和 @987654328 @(apk add yq)等

见Working with JSON。

要读取 json,只需传入一个 json 文件而不是 yaml,它就可以工作 - 因为 json 是 yaml 的一个子集。但是,您可能希望使用 Style Operator 或 --prettyPrint/-P 标志来使看起来更像一个惯用的 yaml 文档。

【讨论】:

【参考方案5】:

我使用 ruby​​ 将我的 json 内容写入 yaml。

至于你的例子,可以这样实现:

cat $DEFAULTS_FILE | jq .users | ruby -ryaml -rjson -e 'puts YAML.dump(JSON.parse(STDIN.read))' > my.yml

【讨论】:

虽然我无法忍受 ruby​​,但我的机器已经安装了它。我能够创建一个别名并将其传递给该别名。谢谢,这很有帮助!【参考方案6】:

我建议使用yq-y 选项

$ pip3 install yq # requires jq

$ cat in.json | yq -y
users:
  - name: pi
    gecos: Hypriot Pirate
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    groups: users,docker,video
    plain_text_passwd: pi
    lock_passwd: 'false'
    ssh_pwauth: 'true'
    chpasswd:
      expire: false
  - name: admin
    gecos: Hypriot Pirate
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    primary-group: users
    groups: users,docker,adm,dialout,audio,plugdev,netdev,video
    ssh-import-id: None
    plain_text_passwd: pi
    lock_passwd: 'true'
    ssh_pwauth: 'true'
    chpasswd: 'expire: false'
    ssh-authorized-keys:
      - ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local

【讨论】:

【参考方案7】:

另一个单线:

python -c 'import yaml, sys; print(yaml.dump(yaml.load(open(sys.argv[1])), default_flow_style=False))' input.json

(利用有效的 json 也是有效的 yaml 的事实)

还有 yaml 到 json:

python -c 'import yaml, json, sys; print(json.dumps(yaml.load(open(sys.argv[1])), indent=2))' input.yaml

【讨论】:

您的 yaml-to-json oneliner 失败并显示 found character '\t' that cannot start any token;这是相同的错误消息yq 报告,必须类似地工作。理查德戈麦斯的单线工作正常。【参考方案8】:

jq 中的解决方案(无需其他工具)

基于本文中@Jeff Mercado 的代码,我添加了对多行字符串和单引号转义的支持。

# purpose: converts Json to Yaml
# remarks:
#   You can use 'yq -y' to convert json to yaml, but ...
#     * this function can be used several times within a single jq program
#     * this function may be faster than using yq
#     * maybe yq is not available in your environment
#
# input: any Json
# output: json converted to yaml
def toYaml:
   def handleMultilineString($level):
      reduce ([match("\n+"; "g")]                       # find groups of '\n'
              | sort_by(-.offset))[] as $match
             (.; .[0:$match.offset + $match.length] +
                 "\n\("    " * $level)" +               # add one extra '\n' for every group of '\n's. Add indention for each new line
                 .[$match.offset + $match.length:]);

   def toYamlString($level):
      if type == "string"
      then handleMultilineString($level)
           | sub("'"; "''"; "g")           # escape single quotes
           | "'\(.)'"                      # wrap in single quotes
      else .
      end;

   def _toYaml($level):
      (objects | to_entries[] |
          if (.value | type) == "array" then
              "\(.key):", (.value | _toYaml($level))
          elif (.value | type) == "object" then
              "\(.key):", "\("    ")\(.value | _toYaml($level))"
          else
              "\(.key): \(.value | toYamlString($level))"
          end
      )
      // (arrays | select(length > 0)[] | [_toYaml($level)] |
          "  - \(.[0])", "\("    ")\(.[1:][])"
      )
      // .;

   _toYaml(1);

示例用法

File 'containsMultilineStrings.json'


  "response": 
    "code": 200,
    "message": "greeting\nthat's all folks\n\n\n"
  

jq -r 'toYaml' < containsMultilineStrings.json

response:
    code: 200
    message: 'greeting

    that''s all folks



    '

jq -r 'toYaml' containsMultilineStrings.json | yq(往返)


  "response": 
    "code": 200,
    "message": "greeting\nthat's all folks\n\n\n"
  

测试

您可以通过将 json 转换为 yaml 并使用 yq 转换回 json 来测试函数 toYaml 的正确性。

FILE='containsMultilineStrings.json'; diff <(cat "$FILE") <(jq -r 'toYaml' $FILE | yq)

性能

与使用 yq 相比,快速基准测试显示函数 toYaml 的运行时间减少。 在我的电脑上,我测量了:

time for i in 1..100; do yq -y > /dev/null < containsMultilineStrings.json; done

8.4 秒

time for i in 1..100; do jq -r 'toYaml' > /dev/null containsMultilineStrings.json; done

3.4 秒

【讨论】:

【参考方案9】:

另一种选择是使用gojq。它是 jq 的一个端口,支持读写 yaml。它可以通过 GitHub 版本、自制软件和零安装来安装。您的问题的命令是:

cat test.json | gojq --yaml-output > test.yaml

【讨论】:

以上是关于如何在 bash 中将 json 响应转换为 yaml的主要内容,如果未能解决你的问题,请参考以下文章

在 bash 中将 CSV 转换为 JSON

如何将afnetworking json字典转换为字符串,然后在IOS中将字符串转换为字典?

在节点中将 JSONP 响应字符串转换为 JSON

如何在Javascript中将字符串转换为json

如何在Nodejs中将json对象数组转换为String?

如何在 Retrofit 库中将 YAML 响应正文解析为 POJO?