ChatGPT解决这个技术问题 Extra ChatGPT

如何使用 jq 合并来自 2 个文件的 2 个 JSON 对象?

我在 shell 脚本中使用 jq 工具 (jq-json-processor) 来解析 json。

我有 2 个 json 文件,想将它们合并到一个唯一的文件中

这里是文件的内容:

文件 1

{
    "value1": 200,
    "timestamp": 1382461861,
    "value": {
        "aaa": {
            "value1": "v1",
            "value2": "v2"
        },
        "bbb": {
            "value1": "v1",
            "value2": "v2"
        },
        "ccc": {
            "value1": "v1",
            "value2": "v2"
        }
    }
}

文件2

{
    "status": 200,
    "timestamp": 1382461861,
    "value": {
        "aaa": {
            "value3": "v3",
            "value4": 4
        },
        "bbb": {
            "value3": "v3"
        },      
        "ddd": {
            "value3": "v3",
            "value4": 4
        }
    }
}

预期结果

{
    "value": {
        "aaa": {
            "value1": "v1",
            "value2": "v2",
            "value3": "v3",
            "value4": 4
        },
        "bbb": {
            "value1": "v1",
            "value2": "v2",
            "value3": "v3"
        },
        "ccc": {
            "value1": "v1",
            "value2": "v2"
        },
        "ddd": {
            "value3": "v3",
            "value4": 4
        }
    }
}

我尝试了很多组合,但我得到的唯一结果是以下,这不是预期的结果:

{
  "ccc": {
    "value2": "v2",
    "value1": "v1"
  },
  "bbb": {
    "value2": "v2",
    "value1": "v1"
  },
  "aaa": {
    "value2": "v2",
    "value1": "v1"
  }
}
{
  "ddd": {
    "value4": 4,
    "value3": "v3"
  },
  "bbb": {
    "value3": "v3"
  },
  "aaa": {
    "value4": 4,
    "value3": "v3"
  }
}

使用此命令:

jq -s '.[].value' file1 file2
你试过jsontool吗? github.com/trentm/json
要使用 json 执行此操作,请使用:cat f1 f2 | json --deep-merge
您在哪里/如何获得 json @xer0x ?
@Gus 哦,要获得 json 工具,请转到 github.com/trentm/json

J
Jonathan Allard

从 1.4 开始,现在可以使用 * 运算符。当给定两个对象时,它将递归地合并它们。例如,

jq -s '.[0] * .[1]' file1 file2

重要提示:注意 -s (--slurp) 标志,它将文件放在同一个数组中。

会让你:

{
  "value1": 200,
  "timestamp": 1382461861,
  "value": {
    "aaa": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3",
      "value4": 4
    },
    "bbb": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3"
    },
    "ccc": {
      "value1": "v1",
      "value2": "v2"
    },
    "ddd": {
      "value3": "v3",
      "value4": 4
    }
  },
  "status": 200
}

如果您还想摆脱其他键(如您的预期结果),一种方法是:

jq -s '.[0] * .[1] | {value: .value}' file1 file2

或者可能更有效(因为它不合并任何其他值):

jq -s '.[0].value * .[1].value | {value: .}' file1 file2

注意-s 标志很重要,因为没有它,两个对象就不在数组中。
@SimoKinnunen 在这里我们合并两个 json 文件。是否可以有 1 个 json 变量和其他 json 文件。我试过了,但它似乎不适合我!
如果单个文件按键排序,是否可以在结果文件中保留顺序?
@SimoKinnunen 我正在使用这个命令:“jq -s 'add' config.json other/*.json”但是当我执行“cat config.json”时它没有被合并。如何将输出放回文件?
请注意,{value: .value} 可以缩短为 {value}
u
user2259432

使用 jq -s add

$ echo '{"a":"foo","b":"bar"} {"c":"baz","a":0}' | jq -s add
{
  "a": 0,
  "b": "bar",
  "c": "baz"
}

这会将所有 JSON 文本从标准输入读取到一个数组中(jq -s 会这样做),然后它会“减少”它们。

add 定义为 def add: reduce .[] as $x (null; . + $x);,它遍历输入数组/对象的值并将它们相加。对象添加 == 合并。)


是否可以使用这种方法进行递归合并(* 运算符)?
@DaveFoster 是的,类似 reduce .[] as $x ({}; . * $x) - 另请参阅 jrib’s answer
j
jrib

这是一个在任意数量的对象上递归工作的版本(使用 *):

echo '{"A": {"a": 1}}' '{"A": {"b": 2}}' '{"B": 3}' |\
  jq --slurp 'reduce .[] as $item ({}; . * $item)'

{
  "A": {
    "a": 1,
    "b": 2
  },
  "B": 3
}

很好的答案。合并目录中的所有文件时效果很好:jq -s 'reduce .[] as $item ({}; . * $item)' *.json
我的 json 内容是一个对象数组,所以这对我有用:echo $JSON_ONE $JSON_TWO | jq -s 'flatten | group_by(keys[]) | map(reduce .[] as $item ({}; . * $item))'
F
FiloSottile

谁知道你是否还需要它,但这是解决方案。

一旦您选择了 --slurp 选项,就很容易了!

--slurp/-s:
    Instead of running the filter for each JSON object in the input,
    read the entire input stream into a large array and run the filter just once.

然后 + 运算符将执行您想要的操作:

jq -s '.[0] + .[1]' config.json config-user.json

(注意:如果你想合并内部对象而不是仅仅用右文件覆盖左边的文件,你需要手动完成)


谢谢......关于最右边的输入如何优先的非常有用的一点。
d
dngray

这可用于合并命令中指定的任意数量的文件:

jq -rs 'reduce .[] as $item ({}; . * $item)' file1.json file2.json file3.json ... file10.json

或者这个对于任意数量的文件

jq -rs 'reduce .[] as $item ({}; . * $item)' ./*.json


p
peak

首先,{"value": .value} 可以缩写为 {value}。

其次,--argfile 选项(在 jq 1.4 和 jq 1.5 中可用)可能很有趣,因为它避免了使用 --slurp 选项。

把这些放在一起,两个文件中的两个对象可以按照指定的方式组合起来,如下所示:

$ jq -n --argfile o1 file1 --argfile o2 file2 '$o1 * $o2 | {value}'

'-n' 标志告诉 jq 不要从标准输入读取,因为输入来自此处的 --argfile 选项。

关于 --argfile 的注意事项

jq 手册不推荐使用 --argfile,因为它的语义很重要:如果指定的输入文件恰好包含一个 JSON 实体,则按原样读取该实体;否则,流中的项目将包装在一个数组中。

如果您对使用 --argfile 感到不舒服,您可能希望考虑几种替代方法。这样做时,请确保使用 --slurpfile 不会导致 -s 命令行选项在后者用于多个文件时效率低下。


jq 已弃用 --slurpfile--argile
p
pmf

到目前为止,没有任何解决方案或评论考虑使用 input 访问第二个文件。使用它会不必要地构建额外的结构来提取,例如使用 --slurp(或 -s)选项时的包罗万象的数组,这在几乎所有其他方法中都有。

要在顶层合并两个文件,只需使用 +input 中的第二个文件添加到 . 中的第一个文件:

jq '. + input' file1.json file2.json

要在所有级别上递归合并两个文件,请使用 * 作为运算符执行相同操作:

jq '. * input' file1.json file2.json

也就是说,要递归合并您的两个文件,同时将两个对象缩减为 value 字段,请先使用 {value} 过滤它们:

jq '{value} * (input | {value})' file1.json file2.json
{
  "value": {
    "aaa": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3",
      "value4": 4
    },
    "bbb": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3"
    },
    "ccc": {
      "value1": "v1",
      "value2": "v2"
    },
    "ddd": {
      "value3": "v3",
      "value4": 4
    }
  }
}

Demo

请注意,仅在 合并之后减少的解决方案(如 . * input | {value})会在代码中更短,但会再次恢复“构建额外的结构以从中提取”的无用性,这可能会产生大量如果零件最终被切断,开销会变大。

为了对两个以上的文件进行操作,或者相应地使用 input 多次,或者使用 inputs 以编程方式迭代所有文件,如

jq 'reduce inputs as $i (.; . * $i)' file*.json

请注意,无论哪种情况,第一个文件总是通过输入上下文 . 访问,而 input(s) 只处理 剩余 文件,即从第二个文件开始(当然,除非 --null-input或给出 -n 选项)。