/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.hadoop.gateway.util; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeType; import org.junit.Assert; import org.junit.Test; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; public class JsonPathTest { @Test public void testCompileInvalidExpressions() { String[] paths = { null, "", "$.", "$[", "$[]", ".", "[", "$.[one]", "$.['one']", "(", ")", "@", "$?", "$(", "$)", "$@", "$?" }; for( String path : paths ) { try { JsonPath.compile( path ); fail( "Expected IllegalArgumentException" ); } catch( IllegalArgumentException e ) { } } } @Test public void testCompileUnsupportedExpressions() { String[] paths = { "$..book[-1:]", "$..book[,1]", "$..book[0,1]", "$..book[:2]", "$..book[0:7:2]", "$..book[(@.length-1)]", "$..book[?(@.isbn)]", "$..book[?(@.price<10)]" }; for( String path : paths ) { try { JsonPath.compile( path ); fail( "Expected IllegalArgumentException for " + path ); } catch( IllegalArgumentException e ) { // Expected. } } } @Test public void testCompileValidExpressions() { JsonPath.Expression expression; JsonPath.Segment[] segments; expression = JsonPath.compile( "$" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 1 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[0].getIndex(), is( -1 ) ); expression = JsonPath.compile( "$.one" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[1].getField(), is( "one" ) ); expression = JsonPath.compile( "$.'one'" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[1].getField(), is( "one" ) ); expression = JsonPath.compile( "$[one]" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[1].getField(), is( "one" ) ); expression = JsonPath.compile( "$['one']" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "one" ) ); assertThat( segments[1].getIndex(), is( -1 ) ); expression = JsonPath.compile( "$[7]" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.INDEX ) ); assertThat( segments[1].getField(), nullValue() ); assertThat( segments[1].getIndex(), is( 7 ) ) ; expression = JsonPath.compile( "$.7" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.INDEX ) ); assertThat( segments[1].getField(), nullValue() ); assertThat( segments[1].getIndex(), is( 7 ) ) ; expression = JsonPath.compile( "$['7']" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "7" ) ); assertThat( segments[1].getIndex(), is( -1 ) ) ; expression = JsonPath.compile( "$.'7'" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "7" ) ); assertThat( segments[1].getIndex(), is( -1 ) ) ; expression = JsonPath.compile( "$.*.field" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 3 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.WILD ) ); assertThat( segments[1].getField(), nullValue() ); assertThat( segments[2].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[2].getField(), is( "field" ) ); expression = JsonPath.compile( "$.**.field" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 3 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.GLOB ) ); assertThat( segments[1].getField(), is( nullValue() ) ); assertThat( segments[2].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[2].getField(), is( "field" ) ); expression = JsonPath.compile( "$[*][field]" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 3 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.WILD ) ); assertThat( segments[1].getField(), nullValue() ); assertThat( segments[2].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[2].getField(), is( "field" ) ); expression = JsonPath.compile( "$[**]['field']" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 3 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.GLOB ) ); assertThat( segments[1].getField(), nullValue() ); assertThat( segments[2].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[2].getField(), is( "field" ) ); expression = JsonPath.compile( "$..field" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 3 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.GLOB ) ); assertThat( segments[1].getField(), nullValue() ); assertThat( segments[2].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[2].getField(), is( "field" ) ); expression = JsonPath.compile( "$[one].two" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 3 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "one" ) ); assertThat( segments[2].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[2].getField(), is( "two" ) ); expression = JsonPath.compile( "$.one[two]" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 3 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "one") ); assertThat( segments[2].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[2].getField(), is( "two" ) ); expression = JsonPath.compile( "$..*" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 3 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[0].getField(), nullValue() ); assertThat( segments[0].getIndex(), is( -1 ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.GLOB ) ); assertThat( segments[1].getField(), nullValue() ); assertThat( segments[1].getIndex(), is( -1 ) ); assertThat( segments[2].getType(), is( JsonPath.Segment.Type.WILD ) ); assertThat( segments[2].getField(), nullValue() ); assertThat( segments[2].getIndex(), is( -1 ) ); } @Test public void testCompileQuotedExpressions() { JsonPath.Expression expression; JsonPath.Segment[] segments; expression = JsonPath.compile( "$.fieldName" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "fieldName" ) ); expression = JsonPath.compile( "$[fieldName]" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "fieldName" ) ); expression = JsonPath.compile( "$['fieldName']" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "fieldName" ) ); expression = JsonPath.compile( "$.field_name" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "field_name" ) ); expression = JsonPath.compile( "$['field_name']" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "field_name" ) ); expression = JsonPath.compile( "$[field_name]" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "field_name" ) ); expression = JsonPath.compile( "$['field-name']" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "field-name" ) ); expression = JsonPath.compile( "$[field-name]" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "field-name" ) ); expression = JsonPath.compile( "$.field-name" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "field-name" ) ); expression = JsonPath.compile( "$['field.name']" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "field.name" ) ); expression = JsonPath.compile( "$[field.name]" ); assertThat( expression, notNullValue() ); segments = expression.getSegments(); assertThat( segments, notNullValue() ); assertThat( segments.length, is( 2 ) ); assertThat( segments[0].getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( segments[1].getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( segments[1].getField(), is( "field.name" ) ); } @Test public void testEvaluateObjects() throws IOException { String json; JsonPath.Segment seg; List<JsonPath.Match> matches; JsonPath.Match match; JsonPath.Match parent; JsonNode root; JsonNode node; JsonPath.Expression expression; JsonFactory factory = new JsonFactory(); ObjectMapper mapper = new ObjectMapper( factory ); json = "{ \"field\" : \"value\" }"; root = mapper.readTree( json ); assertThat( root, notNullValue() ); expression = JsonPath.compile( "$.field" ); matches = expression.evaluate( root ); assertThat( matches, notNullValue() ); assertThat( matches.size(), is( 1 ) ); match = matches.get( 0 ); assertThat( matches, notNullValue() ); seg = matches.get( 0 ).getSegment(); assertThat( seg, notNullValue() ); assertThat( seg.getType(), is( JsonPath.Segment.Type.FIELD ) ); assertThat( seg.getField(), is( "field" ) ); node = match.getNode(); assertThat( node, notNullValue() ); assertThat( node.getNodeType(), is( JsonNodeType.STRING ) ); assertThat( node.asText(), is( "value" ) ); parent = match.getParent(); assertThat( parent, notNullValue() ); assertThat( parent.getNode(), sameInstance( root ) ); assertThat( parent.getParent(), nullValue() ); assertThat( parent.getSegment().getType(), is( JsonPath.Segment.Type.ROOT ) ); json = "{ \"outer\" : { \"inner\" : \"value\"} }"; root = mapper.readTree( json ); assertThat( root, notNullValue() ); expression = JsonPath.compile( "$.outer.inner" ); matches = expression.evaluate( root ); match = matches.get( 0 ); assertThat( match, notNullValue() ); assertThat( match.getField(), is( "inner" ) ); seg = match.getSegment(); assertThat( seg, notNullValue() ); assertThat( seg.getField(), is( "inner" ) ); assertThat( seg.getType(), is( JsonPath.Segment.Type.FIELD ) ); node = match.getNode(); assertThat( node, notNullValue() ); assertThat( node.asText(), is( "value" ) ); parent = match.getParent(); assertThat( parent, notNullValue() ); assertThat( parent.getField(), is( "outer") ); assertThat( parent.getNode().getNodeType(), is( JsonNodeType.OBJECT ) ); parent = parent.getParent(); assertThat( parent.getSegment().getType(), is( JsonPath.Segment.Type.ROOT ) ); assertThat( parent.getNode().getNodeType(), is( JsonNodeType.OBJECT ) ); json = "{ \"outer\" : { \"inner\" : \"value\"} }"; root = mapper.readTree( json ); assertThat( root, notNullValue() ); expression = JsonPath.compile( "$.*.inner" ); matches = expression.evaluate( root ); assertThat( matches.size(), is( 1 ) ) ; match = matches.get( 0 ); assertThat( match.getField(), is( "inner" ) ); assertThat( match.getNode().asText(), is( "value" ) ); } @Test public void testEvaluateArrays() throws IOException { String json; JsonPath.Segment seg; List<JsonPath.Match> matches; JsonPath.Match match; JsonPath.Match parent; JsonNode root; JsonNode node; JsonPath.Expression expression; JsonFactory factory = new JsonFactory(); ObjectMapper mapper = new ObjectMapper( factory ); json = "[ \"outer-array\", { \"nested-field\" : \"nested-object\" }, [ \"nested-array\" ] ]"; root = mapper.readTree( json ); assertThat( root, notNullValue() ); expression = JsonPath.compile( "$[0]" ); matches = expression.evaluate( root ); assertThat( matches.size(), is( 1 ) ) ; match = matches.get( 0 ); assertThat( match.getIndex(), is( 0 ) ); assertThat( match.getSegment().getIndex(), is( 0 ) ); assertThat( match.getNode().asText(), is( "outer-array" ) ); expression = JsonPath.compile( "$[1][nested-field]" ); matches = expression.evaluate( root ); assertThat( matches.size(), is( 1 ) ) ; match = matches.get( 0 ); assertThat( match.getField(), is( "nested-field" ) ); assertThat( match.getNode().asText(), is( "nested-object" ) ); expression = JsonPath.compile( "$[*][nested-field]" ); matches = expression.evaluate( root ); assertThat( matches.size(), is( 1 ) ) ; match = matches.get( 0 ); assertThat( match.getField(), is( "nested-field" ) ); assertThat( match.getNode().asText(), is( "nested-object" ) ); json = "{ \"array\" : [ { \"name\" : \"value-1\" }, { \"name\" : \"value-2 \"}, { \"name\" : \"value-3\" } ] }"; root = mapper.readTree( json ); expression = JsonPath.compile( "$.array.*.name" ); matches = expression.evaluate( root ); assertThat( matches.size(), is( 3 ) ) ; expression = JsonPath.compile( "$.array[*].name" ); matches = expression.evaluate( root ); assertThat( matches.size(), is( 3 ) ) ; expression = JsonPath.compile( "$.array[*][name]" ); matches = expression.evaluate( root ); assertThat( matches.size(), is( 3 ) ) ; expression = JsonPath.compile( "$.array" ); matches = expression.evaluate( root ); match = matches.get( 0 ); expression = JsonPath.compile( "$.*.name" ); matches = expression.evaluate( match.getNode() ); Assert.assertThat( matches.size(), is( 3 ) ); } @Test public void testGlobMatching() throws IOException { String json; JsonPath.Segment seg; List<JsonPath.Match> matches; JsonPath.Match parent; JsonNode root; JsonNode node; JsonPath.Expression expression; Set<String> matchValues; JsonFactory factory = new JsonFactory(); ObjectMapper mapper = new ObjectMapper( factory ); json = "{ \"field\" : \"value\" }"; root = mapper.readTree( json ); assertThat( root, notNullValue() ); expression = JsonPath.compile( "$..field" ); matches = expression.evaluate( root ); matchValues = new HashSet<String>(); assertThat( matches.size(), is( 1 ) ); for( JsonPath.Match match : matches ) { matchValues.add( match.getNode().asText() ); } assertThat( matchValues, hasItem( "value" ) ); json = "{ \"field-1\" : { \"field-1-1\" : { \"field-1-1-1\" : \"value-1-1-1\", \"field\" : \"value-A\" }, \"field\" : \"value-B\"}, \"field-2\" : { \"field-2-1\" : { \"field-2-1-1\" : \"value-2-1-1\", \"field\" : \"value-C\" }, \"field\" : \"value-D\" }, \"field\" : \"value-E\" }"; root = mapper.readTree( json ); assertThat( root, notNullValue() ); expression = JsonPath.compile( "$..field" ); matches = expression.evaluate( root ); assertThat( matches.size(), is( 5 ) ); matchValues = new HashSet<String>(); for( JsonPath.Match match : matches ) { matchValues.add( match.getNode().asText() ); } assertThat( matchValues, hasItem( "value-A" ) ); assertThat( matchValues, hasItem( "value-B" ) ); assertThat( matchValues, hasItem( "value-C" ) ); assertThat( matchValues, hasItem( "value-D" ) ); assertThat( matchValues, hasItem( "value-E" ) ); } }