All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.skyscreamer.jsonassert.ArrayValueMatcher Maven / Gradle / Ivy

There is a newer version: 198
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

package org.skyscreamer.jsonassert;

import java.text.MessageFormat;

import org.json.JSONArray;
import org.json.JSONException;
import org.skyscreamer.jsonassert.comparator.JSONComparator;

/**
 * 

A value matcher for arrays. This operates like STRICT_ORDER array match, * however if expected array has less elements than actual array the matching * process loops through the expected array to get expected elements for the * additional actual elements. In general the expected array will contain a * single element which is matched against each actual array element in turn. * This allows simple verification of constant array element components and * coupled with RegularExpressionValueMatcher can be used to match specific * array element components against a regular expression pattern. As a convenience to reduce syntactic complexity of expected string, if the * expected object is not an array, a one element expected array is created * containing whatever is provided as the expected value.

* *

Some examples of typical usage idioms listed below.

* *

Assuming JSON to be verified is held in String variable ARRAY_OF_JSONOBJECTS and contains:

* *
{@code
 * {a:[{background:white, id:1, type:row},
 *     {background:grey,  id:2, type:row},
 *     {background:white, id:3, type:row},
 *     {background:grey,  id:4, type:row}]}
 * }
* *

then:

* *

To verify that the 'id' attribute of first element of array 'a' is '1':

* *
{@code
 * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
 * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0));
 * JSONAssert.assertEquals("{a:[{id:1}]}", ARRAY_OF_JSONOBJECTS,
 *     new CustomComparator(JSONCompareMode.LENIENT, customization));
 * }
 *
 * 

To simplify complexity of expected JSON string, the value "a:[{id:1}]}" may be replaced by "a:{id:1}}"

* *

To verify that the 'type' attribute of second and third elements of array 'a' is 'row':

* *
{@code
 * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
 * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1, 2));
 * JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS,
 *     new CustomComparator(JSONCompareMode.LENIENT, customization));
 * }
 * 
 * 

To verify that the 'type' attribute of every element of array 'a' is 'row':

* *
{@code
 * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
 * Customization customization = new Customization("a", new ArrayValueMatcher(comparator));
 * JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS,
 *     new CustomComparator(JSONCompareMode.LENIENT, customization));
 * }
 * 
 * 

To verify that the 'id' attribute of every element of array 'a' matches regular expression '\d+'. This requires a custom comparator to specify regular expression to be used to validate each array element, hence the array of Customization instances:

* *
{@code
 * // get length of array we will verify
 * int aLength = ((JSONArray)((JSONObject)JSONParser.parseJSON(ARRAY_OF_JSONOBJECTS)).get("a")).length();
 * // create array of customizations one for each array element
 * RegularExpressionValueMatcher regExValueMatcher =
 *     new RegularExpressionValueMatcher("\\d+");  // matches one or more digits
 * Customization[] customizations = new Customization[aLength];
 * for (int i=0; i regExArrayValueMatcher = new ArrayValueMatcher(regExComparator);
 * Customization regExArrayValueCustomization = new Customization("a", regExArrayValueMatcher);
 * CustomComparator regExCustomArrayValueComparator =
 *     new CustomComparator(JSONCompareMode.STRICT_ORDER, new Customization[] { regExArrayValueCustomization });
 * JSONAssert.assertEquals("{a:[{id:X}]}", ARRAY_OF_JSONOBJECTS, regExCustomArrayValueComparator);
 * }
 * 
 * 

To verify that the 'background' attribute of every element of array 'a' alternates between 'white' and 'grey' starting with first element 'background' being 'white':

* *
{@code
 * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
 * Customization customization = new Customization("a", new ArrayValueMatcher(comparator));
 * JSONAssert.assertEquals("{a:[{background:white},{background:grey}]}", ARRAY_OF_JSONOBJECTS,
 *     new CustomComparator(JSONCompareMode.LENIENT, customization));
 * }
 * 
 * 

Assuming JSON to be verified is held in String variable ARRAY_OF_JSONARRAYS and contains:

* * {a:[[6,7,8], [9,10,11], [12,13,14], [19,20,21,22]]} * *

then:

* *

To verify that the first three elements of JSON array 'a' are JSON arrays of length 3:

* *
{@code
 * JSONComparator comparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER);
 * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0, 2));
 * JSONAssert.assertEquals("{a:[[3]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization));
 * }
 *
 * 

NOTE: simplified expected JSON strings are not possible in this case as ArraySizeComparator does not support them.

* *

To verify that the second elements of JSON array 'a' is a JSON array whose first element has the value 9:

* *
{@code
 * JSONComparator innerComparator = new DefaultComparator(JSONCompareMode.LENIENT);
 * Customization innerCustomization = new Customization("a[1]", new ArrayValueMatcher(innerComparator, 0));
 * JSONComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, innerCustomization);
 * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1));
 * JSONAssert.assertEquals("{a:[[9]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization));
 * }
 *
 * 

To simplify complexity of expected JSON string, the value "{a:[[9]]}" may be replaced by "{a:[9]}" or "{a:9}"

* * @author Duncan Mackinder * */ public class ArrayValueMatcher implements LocationAwareValueMatcher { private final JSONComparator comparator; private final int from; private final int to; /** * Create ArrayValueMatcher to match every element in actual array against * elements taken in sequence from expected array, repeating from start of * expected array if necessary. * * @param comparator * comparator to use to compare elements */ public ArrayValueMatcher(JSONComparator comparator) { this(comparator, 0, Integer.MAX_VALUE); } /** * Create ArrayValueMatcher to match specified element in actual array * against first element of expected array. * * @param comparator * comparator to use to compare elements * @param index * index of the array element to be compared */ public ArrayValueMatcher(JSONComparator comparator, int index) { this(comparator, index, index); } /** * Create ArrayValueMatcher to match every element in specified range * (inclusive) from actual array against elements taken in sequence from * expected array, repeating from start of expected array if necessary. * * @param comparator * comparator to use to compare elements * @param from first element in actual array to compared * @param to last element in actual array to compared */ public ArrayValueMatcher(JSONComparator comparator, int from, int to) { assert comparator != null : "comparator null"; assert from >= 0 : MessageFormat.format("from({0}) < 0", from); assert to >= from : MessageFormat.format("to({0}) < from({1})", to, from); this.comparator = comparator; this.from = from; this.to = to; } @Override /* * NOTE: method defined as required by ValueMatcher interface but will never * be called so defined simply to indicate match failure */ public boolean equal(T o1, T o2) { return false; } @Override public boolean equal(String prefix, T actual, T expected, JSONCompareResult result) { if (!(actual instanceof JSONArray)) { throw new IllegalArgumentException("ArrayValueMatcher applied to non-array actual value"); } try { JSONArray actualArray = (JSONArray) actual; JSONArray expectedArray = expected instanceof JSONArray ? (JSONArray) expected: new JSONArray(new Object[] { expected }); int first = Math.max(0, from); int last = Math.min(actualArray.length() - 1, to); int expectedLen = expectedArray.length(); for (int i = first; i <= last; i++) { String elementPrefix = MessageFormat.format("{0}[{1}]", prefix, i); Object actualElement = actualArray.get(i); Object expectedElement = expectedArray.get((i - first) % expectedLen); comparator.compareValues(elementPrefix, expectedElement, actualElement, result); } // any failures have already been passed to result, so return true return true; } catch (JSONException e) { return false; } } }