dictknifeのdiffに変更部分見比べられるような機能を追加した

github.com

dictknifeのdiffに変更部分見比べられるような機能を追加した。ある意味昨日の以下の記事の続き。

例えば以下の様な2つのJSONがあるとする

00.json

{
  "name": "foo",
  "age": 20
}

01.json

{
  "name": "bar",
  "age": 21
  "nickname": "B"
}

これを普通にdiffを取ると考えると、通常のdiffコマンドと同様の形式での出力ということになる。

$ dictknife diff 00.json 01.json
--- 00.json
+++ 01.json
@@ -1,4 +1,5 @@
 {
-  "name": "foo",
-  "age": 20
+  "name": "bar",
+  "age": 21,
+  "nickname": "B"
 }

今回欲しいのはこれじゃなかった気がした。

例えば、 left, right, right - left と言うような表記

2つのファイル中の項目の数値的な差分が知りたいと言うことがある。例えば、年齢に着目したい場合。このような時に通常の文字列ベースのdiffでは見づらい。そこで --output-format で別のフォーマットで出力できるようにした。

$ dictknife diff 00.json 01.json -o dict
[
  {
    "name": "name",
    "00.json": "foo",
    "01.json": "bar",
    "diff": "- f- o- o+ b+ a+ r"
  },
  {
    "name": "age",
    "00.json": 20,
    "01.json": 21,
    "diff": 1
  },
  {
    "name": "nickname",
    "00.json": null,
    "01.json": "B",
    "diff": null
  }
]

元の値それぞれと比較後の値の3つのデータをそれぞれの項目(dictで言えばkey)毎に表示してくれるようにしたdiff。

加えて比較は以下の様に動く

  • int,floatの場合には数値的な差をとる
  • noneが含まれていたらnone(NA)
  • それ以外はdifflib.ndiff

この表現に昨日のmarkdownでの出力を組み合わせると良い感じに併記して表現ができるようになる。

$ dictknife diff 00.json 01.json -o dict | dictknife cat -i json -o md

結果(比較の表)

name 00.json 01.json diff
name foo bar - f- o- o+ b+ a+ r
age 20 21 1
nickname null B null

文字列の比較が見慣れたunified diff(udiff)の形式ではない理由は1単語毎のdiffを見たい場合にはどのような差分があったか分かりづらかったため。ちなみに完全一致の場合には比較結果は空白文字列としている。

これはこれで処理毎にコマンドがわかれていて、パイプでつなげれば望みの表現が手に入るという形できれい。ただ、めんどくさいのでoutput formatにmdを渡せるようにした。以下でも良い。

$ dictknife diff 00.json 01.json -o md

nestした表現

nestした表現にも対応している。例えば以下の様な表現。motherは共通でfatherが異なるような関係。

$ dictknife diff 020*.json 021*.json -o md

結果(比較の表)。

name 020person.json 021person.json diff
type person person
name foo bar - f- o- o+ b+ a+ r
age 20 21 1
father/type rel rel
father/name X Z - X+ Z
mother/type rel rel
mother/name Y Y

nestした構造の部分は"/"で繋げた形で表記されている(listの場合はfoo/0/nameみたいな形になる)。

差分が存在しない行は省略するようなオプションを用意しても良いかもしれない。

このときの020person.jsonと021person.jsonはそれぞれ以下の様な形。

020person.json

{
  "type": "person",
  "name": "foo",
  "age": 20,
  "father": {
    "type": "rel",
    "name": "X"
  },
  "mother": {
    "type": "rel",
    "name": "Y"
  }
}

021person.json

{
  "type": "person",
  "name": "bar",
  "age": 21,
  "father": {
    "type": "rel",
    "name": "Z"
  },
  "mother": {
    "type": "rel",
    "name": "Y"
  }
}

別の例

各教科のテストの点数を比べるみたいな時に便利

$ dictknife diff  -S -o md xxx.json yyy.json
name xxx.json yyy.json diff
A 70 60 -10
B 70 60 -10
C 70 45 -25
D 70 70 0
E 70 80 10