mirror of https://github.com/alibaba/fastjson2.git
fix enhance operator in and support nested json path, for issue #177
This commit is contained in:
parent
ab36a7104c
commit
483e9ac232
|
@ -7,6 +7,7 @@ import com.alibaba.fastjson2.writer.FieldWriter;
|
|||
import com.alibaba.fastjson2.writer.ObjectWriter;
|
||||
import com.alibaba.fastjson2.writer.ObjectWriterAdapter;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
@ -1315,7 +1316,7 @@ abstract class JSONPathFilter
|
|||
|
||||
static final class NameIntInSegment
|
||||
extends NameFilter {
|
||||
private final long[] values;
|
||||
private long[] values;
|
||||
private final boolean not;
|
||||
|
||||
public NameIntInSegment(
|
||||
|
@ -1393,6 +1394,37 @@ abstract class JSONPathFilter
|
|||
|
||||
return not;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convert(Object root) {
|
||||
JSONPath parentPath = getParentPath();
|
||||
if (parentPath != null) {
|
||||
Object eval = parentPath.eval(root);
|
||||
int length = 1;
|
||||
if (eval != null) {
|
||||
if (eval.getClass().isArray()) {
|
||||
length = Array.getLength(eval);
|
||||
values = new long[length];
|
||||
for (int i = 0; i < length; ++i) {
|
||||
values[i] = Long.parseLong(Array.get(eval, i).toString());
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(eval.getClass())) {
|
||||
Long[] longs = new ArrayList<>((Collection<?>) eval).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(v -> Long.parseLong(v.toString()))
|
||||
.toArray(Long[]::new);
|
||||
length = longs.length;
|
||||
values = new long[length];
|
||||
for (int i = 0; i < length; ++i) {
|
||||
values[i] = longs[i];
|
||||
}
|
||||
} else {
|
||||
values = new long[length];
|
||||
values[0] = Long.parseLong(eval.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final class NameName
|
||||
|
|
|
@ -188,6 +188,7 @@ final class JSONPathMulti
|
|||
((JSONPathFilter.NameFilter) segment).excludeArray();
|
||||
}
|
||||
}
|
||||
segment.convert(root);
|
||||
segment.eval(context);
|
||||
}
|
||||
|
||||
|
|
|
@ -640,6 +640,7 @@ class JSONPathParser {
|
|||
|
||||
String fieldName = null;
|
||||
long hashCode = 0;
|
||||
boolean hasOperator = false;
|
||||
if (at) {
|
||||
if (jsonReader.ch == '[') {
|
||||
JSONPathSegment segment = parseArrayAccess();
|
||||
|
@ -687,106 +688,114 @@ class JSONPathParser {
|
|||
return segment;
|
||||
}
|
||||
} else {
|
||||
jsonReader.next();
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldName == null) {
|
||||
hashCode = jsonReader.readFieldNameHashCodeUnquote();
|
||||
fieldName = jsonReader.getFieldName();
|
||||
}
|
||||
|
||||
if (parentheses) {
|
||||
if (jsonReader.nextIfMatch(')')) {
|
||||
if (filterNests > 0) {
|
||||
filterNests--;
|
||||
char ch = jsonReader.ch;
|
||||
if (ch == 'i' || ch == 'I') {
|
||||
hasOperator = true;
|
||||
} else {
|
||||
jsonReader.next();
|
||||
}
|
||||
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
|
||||
}
|
||||
}
|
||||
|
||||
String functionName = null;
|
||||
|
||||
long[] hashCode2 = null;
|
||||
String[] fieldName2 = null;
|
||||
while (jsonReader.ch == '.') {
|
||||
jsonReader.next();
|
||||
long hash = jsonReader.readFieldNameHashCodeUnquote();
|
||||
String str = jsonReader.getFieldName();
|
||||
|
||||
if (jsonReader.ch == '(') {
|
||||
functionName = str;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hashCode2 == null) {
|
||||
hashCode2 = new long[]{hash};
|
||||
fieldName2 = new String[]{str};
|
||||
} else {
|
||||
hashCode2 = Arrays.copyOf(hashCode2, hashCode2.length + 1);
|
||||
hashCode2[hashCode2.length - 1] = hash;
|
||||
fieldName2 = Arrays.copyOf(fieldName2, fieldName2.length + 1);
|
||||
fieldName2[fieldName2.length - 1] = str;
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldName2 == null && !parentheses
|
||||
&& (jsonReader.ch == ']' || jsonReader.ch == '|' || jsonReader.ch == '&')
|
||||
) {
|
||||
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
|
||||
}
|
||||
|
||||
JSONPathFilter.Operator operator = null;
|
||||
Function function = null;
|
||||
if (jsonReader.ch == '(') {
|
||||
if (functionName == null) {
|
||||
functionName = fieldName;
|
||||
fieldName = null;
|
||||
String functionName = null;
|
||||
long[] hashCode2 = null;
|
||||
String[] fieldName2 = null;
|
||||
if (!hasOperator) {
|
||||
if (fieldName == null) {
|
||||
hashCode = jsonReader.readFieldNameHashCodeUnquote();
|
||||
fieldName = jsonReader.getFieldName();
|
||||
}
|
||||
|
||||
switch (functionName) {
|
||||
case "type":
|
||||
hashCode = 0;
|
||||
function = JSONPathFunction.TypeFunction.INSTANCE;
|
||||
break;
|
||||
case "size":
|
||||
hashCode = 0;
|
||||
function = JSONPathFunction.SizeFunction.INSTANCE;
|
||||
break;
|
||||
case "contains":
|
||||
hashCode = 0;
|
||||
operator = JSONPathFilter.Operator.CONTAINS;
|
||||
break;
|
||||
default:
|
||||
throw new JSONException("syntax error, function not support " + fieldName);
|
||||
}
|
||||
|
||||
if (function != null) {
|
||||
jsonReader.next();
|
||||
if (!jsonReader.nextIfMatch(')')) {
|
||||
throw new JSONException("syntax error, function " + functionName);
|
||||
if (parentheses) {
|
||||
if (jsonReader.nextIfMatch(')')) {
|
||||
if (filterNests > 0) {
|
||||
filterNests--;
|
||||
}
|
||||
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (function == null && jsonReader.ch == '[') {
|
||||
jsonReader.next();
|
||||
if (jsonReader.ch == '?') {
|
||||
while (jsonReader.ch == '.') {
|
||||
jsonReader.next();
|
||||
JSONPathFilter subFilter = (JSONPathFilter) parseFilter();
|
||||
function = new JSONPathFunction.FilterFunction(subFilter);
|
||||
} else {
|
||||
int index = jsonReader.readInt32Value();
|
||||
function = new JSONPathFunction.IndexValue(index);
|
||||
long hash = jsonReader.readFieldNameHashCodeUnquote();
|
||||
String str = jsonReader.getFieldName();
|
||||
|
||||
if (jsonReader.ch == '(') {
|
||||
functionName = str;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hashCode2 == null) {
|
||||
hashCode2 = new long[]{hash};
|
||||
fieldName2 = new String[]{str};
|
||||
} else {
|
||||
hashCode2 = Arrays.copyOf(hashCode2, hashCode2.length + 1);
|
||||
hashCode2[hashCode2.length - 1] = hash;
|
||||
fieldName2 = Arrays.copyOf(fieldName2, fieldName2.length + 1);
|
||||
fieldName2[fieldName2.length - 1] = str;
|
||||
}
|
||||
}
|
||||
if (!jsonReader.nextIfMatch(']')) {
|
||||
throw new JSONException("syntax error");
|
||||
}
|
||||
}
|
||||
if (operator == null) {
|
||||
if (parentheses && jsonReader.nextIfMatch(')')) {
|
||||
|
||||
if (fieldName2 == null && !parentheses
|
||||
&& (jsonReader.ch == ']' || jsonReader.ch == '|' || jsonReader.ch == '&')
|
||||
) {
|
||||
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
|
||||
}
|
||||
|
||||
if (jsonReader.ch == '(') {
|
||||
if (functionName == null) {
|
||||
functionName = fieldName;
|
||||
fieldName = null;
|
||||
}
|
||||
|
||||
switch (functionName) {
|
||||
case "type":
|
||||
hashCode = 0;
|
||||
function = JSONPathFunction.TypeFunction.INSTANCE;
|
||||
break;
|
||||
case "size":
|
||||
hashCode = 0;
|
||||
function = JSONPathFunction.SizeFunction.INSTANCE;
|
||||
break;
|
||||
case "contains":
|
||||
hashCode = 0;
|
||||
operator = JSONPathFilter.Operator.CONTAINS;
|
||||
break;
|
||||
default:
|
||||
throw new JSONException("syntax error, function not support " + fieldName);
|
||||
}
|
||||
|
||||
if (function != null) {
|
||||
jsonReader.next();
|
||||
if (!jsonReader.nextIfMatch(')')) {
|
||||
throw new JSONException("syntax error, function " + functionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (function == null && jsonReader.ch == '[') {
|
||||
jsonReader.next();
|
||||
if (jsonReader.ch == '?') {
|
||||
jsonReader.next();
|
||||
JSONPathFilter subFilter = (JSONPathFilter) parseFilter();
|
||||
function = new JSONPathFunction.FilterFunction(subFilter);
|
||||
} else {
|
||||
int index = jsonReader.readInt32Value();
|
||||
function = new JSONPathFunction.IndexValue(index);
|
||||
}
|
||||
if (!jsonReader.nextIfMatch(']')) {
|
||||
throw new JSONException("syntax error");
|
||||
}
|
||||
}
|
||||
if (operator == null) {
|
||||
if (parentheses && jsonReader.nextIfMatch(')')) {
|
||||
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
|
||||
}
|
||||
operator = JSONPath.parseOperator(jsonReader);
|
||||
}
|
||||
} else {
|
||||
operator = JSONPath.parseOperator(jsonReader);
|
||||
}
|
||||
|
||||
|
@ -820,12 +829,30 @@ class JSONPathParser {
|
|||
}
|
||||
case IN:
|
||||
case NOT_IN: {
|
||||
if (jsonReader.ch != '(') {
|
||||
JSONPathSegment segment;
|
||||
char ch = jsonReader.ch;
|
||||
if (ch == '$') {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int filterNests = 0;
|
||||
while (ch != ')' || filterNests != 0) {
|
||||
sb.append(ch);
|
||||
if (ch == '(') {
|
||||
filterNests++;
|
||||
} else if (ch == ')') {
|
||||
filterNests--;
|
||||
}
|
||||
jsonReader.next();
|
||||
ch = jsonReader.ch;
|
||||
}
|
||||
jsonReader.next();
|
||||
JSONPath parentPath = JSONPath.of(sb.toString());
|
||||
return new JSONPathFilter.NameIntInSegment(fieldName, hashCode, fieldName2, hashCode2, function, null, operator == JSONPathFilter.Operator.NOT_IN).setParentPath(parentPath);
|
||||
}
|
||||
if (!(ch == '(' || ch == '[')) {
|
||||
throw new JSONException(jsonReader.info("jsonpath syntax error"));
|
||||
}
|
||||
jsonReader.next();
|
||||
|
||||
JSONPathSegment segment;
|
||||
if (jsonReader.isString()) {
|
||||
List<String> list = new ArrayList<>();
|
||||
while (jsonReader.isString()) {
|
||||
|
@ -853,14 +880,14 @@ class JSONPathParser {
|
|||
throw new JSONException(jsonReader.info("jsonpath syntax error"));
|
||||
}
|
||||
|
||||
if (!jsonReader.nextIfMatch(')')) {
|
||||
if (!(jsonReader.nextIfMatch(')') || jsonReader.nextIfMatch(']'))) {
|
||||
throw new JSONException(jsonReader.info("jsonpath syntax error"));
|
||||
}
|
||||
if (jsonReader.ch == '&' || jsonReader.ch == '|' || jsonReader.ch == 'a' || jsonReader.ch == 'o') {
|
||||
filterNests--;
|
||||
segment = parseFilterRest(segment);
|
||||
}
|
||||
if (!jsonReader.nextIfMatch(')')) {
|
||||
if (!(jsonReader.nextIfMatch(')') || jsonReader.nextIfMatch(']'))) {
|
||||
throw new JSONException(jsonReader.info("jsonpath syntax error"));
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ import static com.alibaba.fastjson2.JSONB.Constants.BC_OBJECT_END;
|
|||
import static com.alibaba.fastjson2.JSONReader.EOI;
|
||||
|
||||
abstract class JSONPathSegment {
|
||||
private JSONPath parentPath;
|
||||
|
||||
public abstract void accept(JSONReader jsonReader, JSONPath.Context context);
|
||||
|
||||
public abstract void eval(JSONPath.Context context);
|
||||
|
@ -54,6 +56,18 @@ abstract class JSONPathSegment {
|
|||
set(context, value);
|
||||
}
|
||||
|
||||
public JSONPath getParentPath() {
|
||||
return parentPath;
|
||||
}
|
||||
|
||||
public JSONPathSegment setParentPath(JSONPath parentPath) {
|
||||
this.parentPath = parentPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void convert(Object root) {
|
||||
}
|
||||
|
||||
interface EvalSegment {
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ class JSONPathTwoSegment
|
|||
}
|
||||
|
||||
Context context1 = new Context(this, context0, second, null, 0);
|
||||
second.convert(root);
|
||||
second.eval(context1);
|
||||
Object contextValue = context1.value;
|
||||
if ((features & Feature.AlwaysReturnList.mask) != 0) {
|
||||
|
|
|
@ -6,6 +6,8 @@ import com.alibaba.fastjson2.JSONPath;
|
|||
import com.alibaba.fastjson2.JSONReader;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class Issue117 {
|
||||
@Test
|
||||
public void test() {
|
||||
|
@ -23,4 +25,14 @@ public class Issue117 {
|
|||
|
||||
System.out.println(times);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2() {
|
||||
String text = ("{code:1,msg:'Hello world',data:{list:[1,2,3,4,5], ary2:[{a:2},{a:[2]},{a:3,b:{c:'ddd'}}]}}");
|
||||
JSONObject object = JSON.parseObject(text, JSONReader.Feature.AllowUnQuotedFieldNames);
|
||||
assertEquals("[2]", JSONPath.eval(object, "$.data.list[?(@ in (2))]").toString());
|
||||
assertEquals("[2]", JSONPath.eval(object, "$.data.list[?(@ in [2])]").toString());
|
||||
assertEquals("[2]", JSONPath.eval(object, "$.data.list[?(@ in $..ary2[0].a)]").toString());
|
||||
assertEquals("[2]", JSONPath.eval(object, "$.data.list[?(@ in $..ary2[1].a)]").toString());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue