Java を使って json データの差分を調べる必要が生じたので、便利なツールを探した。 Stackoverflow に JSONassert がよい、的な情報があったので、使ってみました。 とても便利だったので、紹介します。
例によって Groovy script で記述しますが、こんな簡単に使えます。
@Grab(group='org.skyscreamer', module='jsonassert', version='1.5.0')
import org.skyscreamer.jsonassert.JSONAssert
def expected = '{"price": 100}'
def actual = '{"price": 101}'
JSONAssert.assertEquals(expected, actual, false)
ここでは {"price": 100}
と {"price": 101}
の2つの json 文字列の比較、差分検査です。
実行すると
java.lang.AssertionError: price
Expected: 100
got: 101
100が期待されるところを 101 になっている ということで、メッセージもわかりやすい。
差分は知りたいが、 price については 値が異なっていても構わない という場合。
CustomComparator と RegularExpressionValueMatcher を使い price が数値でありさえすればOK を表現します。
@Grab(group='org.skyscreamer', module='jsonassert', version='1.5.0')
import org.skyscreamer.jsonassert.*
import org.skyscreamer.jsonassert.comparator.*
def expected = '{"price": 100}'
def actual = '{"price": 101}'
def comparator = new CustomComparator(
JSONCompareMode.LENIENT,
new Customization('price', new RegularExpressionValueMatcher<Object>("\\d+")))
JSONAssert.assertEquals(expected, actual, comparator)
これを実行するとエラーは出ません、つまりテストにパスします。
もし、 {"price": 101}
を {"price": "100yen"}
などに変更して実行すると:
java.lang.AssertionError: price: Constant expected pattern did not match value
Expected: \d+
got: 100yen
このように、きちんと差があるとエラーを出してくれます。
price は 金額差が 10円以内ならば 差がないこととして扱いたい というような場合を考えます。
先ほどは RegularExpressionValueMatcher という既存に用意されていた ValueMatcher を使いましたが、 要は org.skyscreamer.jsonassert.ValueMatcher インタフェースを実装した自前 ValueMatcher を書けばよい。
@Grab(group='org.skyscreamer', module='jsonassert', version='1.5.0')
import org.skyscreamer.jsonassert.*
import org.skyscreamer.jsonassert.comparator.*
class MyValueMatcher implements ValueMatcher<Integer> {
@Override
boolean equal(Integer actual, Integer expected){
return ( Math.abs(actual - expected) < 10 )
}
}
def expected = '{"price": 100}'
def actual = '{"price": 101}'
def comparator = new CustomComparator(
JSONCompareMode.LENIENT,
new Customization('price', new MyValueMatcher()))
JSONAssert.assertEquals(expected, actual, comparator)
これでテストパスします。
もし actual を {"price": 111}
にすると:
java.lang.AssertionError: price
Expected: 100
got: 111
のように差分ありのエラーがでます。
金額差の許容範囲をいろいろ変えたい場合は、 MyValuMatcher のコンストラクでその閾値を指定するなど。
なお CustomComparator に指定する Customization を複数登録したい場合は、 CustomComparator のコンストラクタに Customaization を複数列挙すればよい。このように:
def comparator = new CustomComparator(
JSONCompareMode.LENIENT,
new Customization('price', new MyValueMatcher<Integer>()),
new Customization('tax', new MyValueMatcher<Integer>()))
今日調べた範囲では、 ネストが深い json の比較・差分検出は難しいような感じですが、 その辺は自分で ネストを浅くした json を作って JSONassert していけばよいかと。