使用jq或替代命令行工具来区分JSON文件

Ame*_*ina 64 diff json jq

是否有任何命令行实用程序可用于查找两个JSON文件是否与in-dictionary-key和within-list-element排序的不变性相同?

这可以用jq其他等效工具完成吗?

例子:

这两个JSON文件是相同的

A:

{
  "People": ["John", "Bryan"],
  "City": "Boston",
  "State": "MA"
}
Run Code Online (Sandbox Code Playgroud)

B:

{
  "People": ["Bryan", "John"],
  "State": "MA",
  "City": "Boston"
}
Run Code Online (Sandbox Code Playgroud)

但是这两个JSON文件是不同的:

A:

{
  "People": ["John", "Bryan", "Carla"],
  "City": "Boston",
  "State": "MA"
}
Run Code Online (Sandbox Code Playgroud)

C:

{
  "People": ["Bryan", "John"],
  "State": "MA",
  "City": "Boston"
}
Run Code Online (Sandbox Code Playgroud)

那将是:

$ some_diff_command A.json B.json

$ some_diff_command A.json C.json
The files are not structurally identical
Run Code Online (Sandbox Code Playgroud)

Eri*_*rik 68

原则上,如果你有权访问bash或其他一些高级shell,你可以做类似的事情

cmp <(jq -cS . A.json) <(jq -cS . B.json)
Run Code Online (Sandbox Code Playgroud)

使用子进程.这将使用排序键和浮点的一致表示格式化json.这些是我能想到的两个原因,即为什么具有相同内容的json将以不同方式打印.因此,之后进行简单的字符串比较将导致适当的测试.可能还值得注意的是,如果你不能使用bash,你可以使用临时文件得到相同的结果,它只是不那么干净.

这并没有完全回答你的问题,因为在你说出你想要的问题的方式["John", "Bryan"]["Bryan", "John"]相同的比较.由于json不具有集合的概念,只有列表,因此应将其视为不同的.订单对于列表很重要.如果您希望它们进行相同的比较,您将不得不编写一些自定义比较,为此,您需要通过相等来定义您的意思.订单是否适合所有列表或只有一些?重复元素怎么样?或者,如果您希望它们被表示为一个集合,并且元素是字符串,您可以将它们放在像这样的对象中{"John": null, "Bryan": null}.在比较平等时,顺序无关紧要.

更新

从评论讨论:如果你想更好地了解为什么json不一样,那么

diff <(jq -S . A.json) <(jq -S . B.json)
Run Code Online (Sandbox Code Playgroud)

将产生更多可解释的输出.vimdiff根据口味可能比较差异.

  • 一个更清晰的视觉形式 IMO 是 `vimdiff &lt;(jq -S . a.json) &lt;(jq -S . b.json)` (6认同)

小智 27

由于jq的比较已经比较了对象而没有考虑键排序,所以剩下的就是在比较对象之前对对象内的所有列表进行排序.假设你的两个文件被命名为a.jsonb.json,在夜间最新JQ:

jq --argfile a a.json --argfile b b.json -n '($a | (.. | arrays) |= sort) as $a | ($b | (.. | arrays) |= sort) as $b | $a == $b'
Run Code Online (Sandbox Code Playgroud)

该程序应返回"true"或"false",具体取决于对象是否相等,使用您要求的相等定义.

编辑:(.. | arrays) |= sort在某些边缘情况下,构造实际上并不像预期的那样工作.这个GitHub问题解释了为什么并提供了一些替代方案,例如:

def post_recurse(f): def r: (f | select(. != null) | r), .; r; def post_recurse: post_recurse(.[]?); (post_recurse | arrays) |= sort
Run Code Online (Sandbox Code Playgroud)

应用于上面的jq调用:

jq --argfile a a.json --argfile b b.json -n 'def post_recurse(f): def r: (f | select(. != null) | r), .; r; def post_recurse: post_recurse(.[]?); ($a | (post_recurse | arrays) |= sort) as $a | ($b | (post_recurse | arrays) |= sort) as $b | $a == $b'
Run Code Online (Sandbox Code Playgroud)

  • @SimonErnestoCardenasZarate 如果您仍然遇到此问题,您可能需要使用 `--argjson` 参数 (2认同)

Joe*_*ett 13

使用jd-set选项:

没有输出意味着没有区别.

$ jd -set A.json B.json
Run Code Online (Sandbox Code Playgroud)

差异显示为@路径和+或 - .

$ jd -set A.json C.json

@ ["People",{}]
+ "Carla"
Run Code Online (Sandbox Code Playgroud)

输出差异也可以用作带有-p选项的补丁文件.

$ jd -set -o patch A.json C.json; jd -set -p patch B.json

{"City":"Boston","People":["John","Carla","Bryan"],"State":"MA"}
Run Code Online (Sandbox Code Playgroud)

https://github.com/josephburnett/jd#command-line-usage

  • 这是一个圣杯工具,用于对“json”(和转换后的“yaml”)配置进行测试,以了解为什么一个人的配置与其他人的配置相比不起作用。 (4认同)
  • 我只为 Linux 构建。但既然你问了,我已经交叉编译了最新版本:https://github.com/josephburnett/jd/releases/tag/v1.4.0。下载 jd-amd64-darwin ,它应该可以在 OSX 上运行。 (4认同)
  • 所以被低估了,这应该是一种轻罪。提供实际的“diff”格式兼容输出。惊人的。 (3认同)
  • 在 MacOS 上使用 Homebrew:`brew install jd` (3认同)
  • 您可以使用命令行工具,也可以使用Web工具:http://play.jd-tool.io/ (2认同)
  • 这就是“-set”标志的用途。如果包含它,列表中的顺序并不重要。如果不这样做,这很重要(默认)。 (2认同)

And*_*rew 13

从前两个答案中提取最佳答案以获得jq基于 json 的差异:

diff \
  <(jq -S 'def post_recurse(f): def r: (f | select(. != null) | r), .; r; def post_recurse: post_recurse(.[]?); (. | (post_recurse | arrays) |= sort)' "$original_json") \
  <(jq -S 'def post_recurse(f): def r: (f | select(. != null) | r), .; r; def post_recurse: post_recurse(.[]?); (. | (post_recurse | arrays) |= sort)' "$changed_json")
Run Code Online (Sandbox Code Playgroud)

这采用了/sf/answers/2235326411/中优雅的数组排序解决方案(它允许我们将数组视为集合)以及/sf/answers/2602287831/diff中的干净的 bash 重定向。 538507这解决了您想要比较两个 json 文件且数组内容的顺序不相关的情况。


pea*_*eak 6

这是使用通用函数walk/1的解决方案:

# Apply f to composite entities recursively, and to atoms
def walk(f):
  . as $in
  | if type == "object" then
      reduce keys[] as $key
        ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
  elif type == "array" then map( walk(f) ) | f
  else f
  end;

def normalize: walk(if type == "array" then sort else . end);

# Test whether the input and argument are equivalent
# in the sense that ordering within lists is immaterial:
def equiv(x): normalize == (x | normalize);
Run Code Online (Sandbox Code Playgroud)

例:

{"a":[1,2,[3,4]]} | equiv( {"a": [[4,3], 2,1]} )
Run Code Online (Sandbox Code Playgroud)

生产:

true
Run Code Online (Sandbox Code Playgroud)

并作为一个bash脚本包装:

#!/bin/bash

JQ=/usr/local/bin/jq
BN=$(basename $0)

function help {
  cat <<EOF

Syntax: $0 file1 file2

The two files are assumed each to contain one JSON entity.  This
script reports whether the two entities are equivalent in the sense
that their normalized values are equal, where normalization of all
component arrays is achieved by recursively sorting them, innermost first.

This script assumes that the jq of interest is $JQ if it exists and
otherwise that it is on the PATH.

EOF
  exit
}

if [ ! -x "$JQ" ] ; then JQ=jq ; fi

function die     { echo "$BN: $@" >&2 ; exit 1 ; }

if [ $# != 2 -o "$1" = -h  -o "$1" = --help ] ; then help ; exit ; fi

test -f "$1" || die "unable to find $1"
test -f "$2" || die "unable to find $2"

$JQ -r -n --argfile A "$1" --argfile B "$2" -f <(cat<<"EOF"
# Apply f to composite entities recursively, and to atoms
def walk(f):
  . as $in
  | if type == "object" then
      reduce keys[] as $key
        ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
  elif type == "array" then map( walk(f) ) | f
  else f
  end;

def normalize: walk(if type == "array" then sort else . end);

# Test whether the input and argument are equivalent
# in the sense that ordering within lists is immaterial:
def equiv(x): normalize == (x | normalize);

if $A | equiv($B) then empty else "\($A) is not equivalent to \($B)" end

EOF
)
Run Code Online (Sandbox Code Playgroud)

POSTSCRIPT:walk/1是jq> 1.5版本的内置版本,因此如果你的jq包含它,可以省略它,但是在jq脚本中冗余地包含它是没有害处的.

POST-POSTSCRIPT:walk最近更改了内置版本,因此它不再对对象中的键进行排序.具体来说,它使用keys_unsorted.对于手头的任务,keys应该使用版本.


Mai*_*kon 6

有这个答案在这里,将是有益的。

本质上,您可以使用 Gitdiff功能(即使对于非 Git 跟踪的文件),它还在输出中包含颜色:

git diff --no-index payload_1.json payload_2.json

  • 这对顺序很敏感,OP 想要忽略它 (4认同)
  • 这只是一个彩色文本差异(至少在 git 版本 2.30.2 中),它不理解 JSON 语义。您可以通过生成 JSON 数据的最小化副本(`jq -cSM`)并对其进行 git diff 来自行检查。 (2认同)