如何使用 jq 对任意 JSON 进行完全排序?
How can I completely sort arbitrary JSON using jq?
我想比较两个 JSON 文本文件。不幸的是,它们是以任意顺序构造的,所以当它们在语义上相同时,我会得到差异。我想使用 jq (或其他)以任何类型的完整顺序对它们进行排序,以消除仅由于元素排序而导致的差异。
--sort-keys 解决了一半的问题,但它没有对数组进行排序。
我对 jq 非常无知,不知道如何编写保留所有数据的 jq 递归过滤器;任何帮助将不胜感激。
我意识到逐行 'diff' 输出不一定是比较两个复杂对象的最佳方式,但在这种情况下我知道这两个文件非常相似(几乎相同)并且行逐行差异对我来说很好。
回答了一个非常相似的问题,但没有打印差异。另外,我想保存排序的结果,所以我真正想要的只是一个过滤程序来排序 JSON.
这是一个使用通用函数 sorted_walk/1 的解决方案(如此命名的原因在下面的后记中描述)。
normalize.jq:
# Apply f to composite entities recursively using keys[], and to atoms
def sorted_walk(f):
. as $in
| if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ($in[$key] | sorted_walk(f)) } ) | f
elif type == "array" then map( sorted_walk(f) ) | f
else f
end;
def normalize: sorted_walk(if type == "array" then sort else . end);
normalize
使用 bash 的示例:
diff <(jq -S -f normalize.jq FILE1) <(jq -S -f normalize.jq FILE2)
POSTSCRIPT:walk/1
的内置定义在首次发布此回复后进行了修改:它现在使用 keys_unsorted
而不是 keys
。
I want to diff two JSON text files.
使用 jd
和 -set
选项:
无输出表示无差别
$ jd -set A.json B.json
差异显示为 @ 路径和 + 或 -。
$ jd -set A.json C.json
@ ["People",{}]
+ "Carla"
输出差异也可以作为带有 -p
选项的补丁文件。
$ jd -set -o patch A.json C.json; jd -set -p patch B.json
{"City":"Boston","People":["John","Carla","Bryan"],"State":"MA"}
我很惊讶这不是一个更受欢迎的 question/answer。我还没有看到任何其他 json 深度排序解决方案。也许每个人都喜欢一遍又一遍地解决同一个问题。
这是 的包装器,将其包装到 shell 脚本中,该脚本可在管道中运行或使用文件参数。
#!/usr/bin/env bash
# json normalizer function
# Recursively sort an entire json file, keys and arrays
# jq --sort-keys is top level only
# Alphabetize a json file's dict's such that they are always in the same order
# Makes json diff'able and should be run on any json data that's in source control to prevent excessive diffs from dict reordering.
[ "${DEBUG}" ] && set -x
TMP_FILE="$(mktemp)"
trap 'rm -f -- "${TMP_FILE}"' EXIT
cat > "${TMP_FILE}" <<-EOT
# Apply f to composite entities recursively using keys[], and to atoms
def sorted_walk(f):
. as $in
| if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ($in[$key] | sorted_walk(f)) } ) | f
elif type == "array" then map( sorted_walk(f) ) | f
else f
end;
def normalize: sorted_walk(if type == "array" then sort else . end);
normalize
EOT
# Don't pollute stdout with debug output
[ "${DEBUG}" ] && cat $TMP_FILE > /dev/stderr
if [ "" ] ; then
jq -S -f ${TMP_FILE}
else
jq -S -f ${TMP_FILE} < /dev/stdin
fi
我想比较两个 JSON 文本文件。不幸的是,它们是以任意顺序构造的,所以当它们在语义上相同时,我会得到差异。我想使用 jq (或其他)以任何类型的完整顺序对它们进行排序,以消除仅由于元素排序而导致的差异。
--sort-keys 解决了一半的问题,但它没有对数组进行排序。
我对 jq 非常无知,不知道如何编写保留所有数据的 jq 递归过滤器;任何帮助将不胜感激。
我意识到逐行 'diff' 输出不一定是比较两个复杂对象的最佳方式,但在这种情况下我知道这两个文件非常相似(几乎相同)并且行逐行差异对我来说很好。
这是一个使用通用函数 sorted_walk/1 的解决方案(如此命名的原因在下面的后记中描述)。
normalize.jq:
# Apply f to composite entities recursively using keys[], and to atoms
def sorted_walk(f):
. as $in
| if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ($in[$key] | sorted_walk(f)) } ) | f
elif type == "array" then map( sorted_walk(f) ) | f
else f
end;
def normalize: sorted_walk(if type == "array" then sort else . end);
normalize
使用 bash 的示例:
diff <(jq -S -f normalize.jq FILE1) <(jq -S -f normalize.jq FILE2)
POSTSCRIPT:walk/1
的内置定义在首次发布此回复后进行了修改:它现在使用 keys_unsorted
而不是 keys
。
I want to diff two JSON text files.
使用 jd
和 -set
选项:
无输出表示无差别
$ jd -set A.json B.json
差异显示为 @ 路径和 + 或 -。
$ jd -set A.json C.json
@ ["People",{}]
+ "Carla"
输出差异也可以作为带有 -p
选项的补丁文件。
$ jd -set -o patch A.json C.json; jd -set -p patch B.json
{"City":"Boston","People":["John","Carla","Bryan"],"State":"MA"}
我很惊讶这不是一个更受欢迎的 question/answer。我还没有看到任何其他 json 深度排序解决方案。也许每个人都喜欢一遍又一遍地解决同一个问题。
这是
#!/usr/bin/env bash
# json normalizer function
# Recursively sort an entire json file, keys and arrays
# jq --sort-keys is top level only
# Alphabetize a json file's dict's such that they are always in the same order
# Makes json diff'able and should be run on any json data that's in source control to prevent excessive diffs from dict reordering.
[ "${DEBUG}" ] && set -x
TMP_FILE="$(mktemp)"
trap 'rm -f -- "${TMP_FILE}"' EXIT
cat > "${TMP_FILE}" <<-EOT
# Apply f to composite entities recursively using keys[], and to atoms
def sorted_walk(f):
. as $in
| if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ($in[$key] | sorted_walk(f)) } ) | f
elif type == "array" then map( sorted_walk(f) ) | f
else f
end;
def normalize: sorted_walk(if type == "array" then sort else . end);
normalize
EOT
# Don't pollute stdout with debug output
[ "${DEBUG}" ] && cat $TMP_FILE > /dev/stderr
if [ "" ] ; then
jq -S -f ${TMP_FILE}
else
jq -S -f ${TMP_FILE} < /dev/stdin
fi