如何使用jq将此字符串转换为JSON?

时间:2016-11-21 14:41:01

标签: json bash shell parsing jq

如何使用jq将以下行转换为JSON格式?

payment_test_service1_api[host] HOST1
payment_test_service1_api[username] USERNAME1
payment_test_service1_api[password] PASSWORD1

以这种JSON格式:

"payment_test" {
    "service1" : 
    {
      "api": {
        "host": "HOST1",
        "username": "USERNAME1",
        "password": "PASSWORD1"
      }
    }
}

3 个答案:

答案 0 :(得分:7)

以下比@ chepner的解决方案更通用(它使用add,因此不限于三个键值对),也许稍微简单一些:

$ jq -Rn 'inputs | match("\\[(.*)\\] (.*)")
    | .captures | {(.[0].string): (.[1].string)} ' tmp.txt |
  jq -s '{payment_test: { service1: { api: add }}} '

现在很容易看到如何消除对jq的第二次调用:

jq -Rn '[ inputs | match("\\[(.*)\\] (.*)")
          | .captures | {(.[0].string): (.[1].string)}]
        | {payment_test: { service1: { api: add }}} ' tmp.txt

使用命名捕获变量

改进是使用命名捕获变量。 jq提供了一个过滤器capture,用于从捕获变量创建对象;它可以与from_entries一起使用,如下所示:

[inputs | capture("\\[(?<key>.*)\\] (?<value>.*)")]
| from_entries
| {payment_test: { service1: { api: . }}} 

使用这种技术,字符串&#34; payment_test_service1_api&#34;也可以解析,但OP对此的期望尚不清楚,所以也许最好留给另一个Q&amp; A.

答案 1 :(得分:4)

更新:请参阅@peak's answer,了解我无法实现的概括。

这远非最佳解决方案,但证明可以从任意文本生成一些JSON。

jq -Rrn 'inputs | match("\\[(.*)\\] (.*)") | .captures[]|.string ' tmp.txt |
  jq -Rn '{
    payment_test: {
      service1: {
        api: {
          "\(input)": input,
          "\(input)": input,
          "\(input)": input
        }
      }
    }
  }
'

以下是每个命令的细分:

  1. 第一个读取输入作为原始文本(-R),在过滤器内逐行(-n连同inputs函数),并输出与括号中的文本对应的原始文本(-r)和括号后面的其余部分。

    一次在输入的一个过滤器上运行第一个命令以查看完整过滤器的工作原理是有益的。 (也就是说,按顺序运行以下各项:

    1. jq -Rrn 'inputs' tmp.txt
    2. jq -Rrn 'inputs | match(...)' tmp.txt
    3. jq -Rrn 'inputs | match(...) | .captures' tmp.txt
    4. jq -Rrn 'inputs | match(...) | .captures[] | .string' tmp.txt
    5. 第二个命令只是读取每一行,再次作为原始文本并显式使用input,并从每个部分构建所需的对象。

    6. 应该可以将此组合成一个jq的调用;我在更复杂的命令方面的技能是有限的,但这证明你可以使用简单的部分拼凑出一个解决方案。

答案 2 :(得分:0)

每行的第一部分类似于结果中的对象结构。如果你能想出一个以明确的方式解析它的方案,你可以将它们视为路径然后直接设置值。

假设只有三个段只有第一个段可能包含下划线,您可以这样做:

$ jq -Rn 'reduce (inputs
        | [match("(\\S+)_([^_]+)_([^_]+)\\[([^\\]]+)\\] (\\S+)").captures[].string]
    ) as $p
    ({}; setpath($p[:-1]; $p[-1]))' input.txt