Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion core/src/main/java/com/alibaba/fastjson2/JSONPathFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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.*;
Expand Down Expand Up @@ -1315,7 +1316,7 @@ public boolean apply(Object fieldValue) {

static final class NameIntInSegment
extends NameFilter {
private final long[] values;
private long[] values;
private final boolean not;

public NameIntInSegment(
Expand Down Expand Up @@ -1393,6 +1394,37 @@ public boolean apply(Object fieldValue) {

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);
Comment on lines +1412 to +1415
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里可以去掉对Stream的使用么?

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ public Object eval(Object root) {
((JSONPathFilter.NameFilter) segment).excludeArray();
}
}
segment.convert(root);
segment.eval(context);
}

Expand Down
191 changes: 109 additions & 82 deletions core/src/main/java/com/alibaba/fastjson2/JSONPathParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@ JSONPathSegment parseFilter() {

String fieldName = null;
long hashCode = 0;
boolean hasOperator = false;
if (at) {
if (jsonReader.ch == '[') {
JSONPathSegment segment = parseArrayAccess();
Expand Down Expand Up @@ -687,106 +688,114 @@ JSONPathSegment parseFilter() {
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);
}
}

JSONPathFilter.Operator operator = null;
Function function = null;
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 (!hasOperator) {
if (fieldName == null) {
hashCode = jsonReader.readFieldNameHashCodeUnquote();
fieldName = jsonReader.getFieldName();
}

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 (parentheses) {
if (jsonReader.nextIfMatch(')')) {
if (filterNests > 0) {
filterNests--;
}
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
}
}
}

if (fieldName2 == null && !parentheses
&& (jsonReader.ch == ']' || jsonReader.ch == '|' || jsonReader.ch == '&')
) {
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
}
while (jsonReader.ch == '.') {
jsonReader.next();
long hash = jsonReader.readFieldNameHashCodeUnquote();
String str = jsonReader.getFieldName();

JSONPathFilter.Operator operator = null;
Function function = null;
if (jsonReader.ch == '(') {
if (functionName == null) {
functionName = fieldName;
fieldName = null;
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;
}
}

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 (fieldName2 == null && !parentheses
&& (jsonReader.ch == ']' || jsonReader.ch == '|' || jsonReader.ch == '&')
) {
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
}

if (function != null) {
jsonReader.next();
if (!jsonReader.nextIfMatch(')')) {
throw new JSONException("syntax error, function " + functionName);
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 == '?') {
if (function == null && 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 (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);
if (operator == null) {
if (parentheses && jsonReader.nextIfMatch(')')) {
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
}
operator = JSONPath.parseOperator(jsonReader);
}
} else {
operator = JSONPath.parseOperator(jsonReader);
}

Expand Down Expand Up @@ -820,12 +829,30 @@ JSONPathSegment parseFilter() {
}
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()) {
Expand Down Expand Up @@ -853,14 +880,14 @@ JSONPathSegment parseFilter() {
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"));
}

Expand Down
14 changes: 14 additions & 0 deletions core/src/main/java/com/alibaba/fastjson2/JSONPathSegment.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
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);
Expand Down Expand Up @@ -54,6 +56,18 @@ public void setLong(JSONPath.Context context, long value) {
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 {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public Object eval(Object root) {
}

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) {
Expand Down
12 changes: 12 additions & 0 deletions core/src/test/java/com/alibaba/fastjson2/issues/Issue117.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
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() {
Expand All @@ -23,4 +25,14 @@ public void test() {

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());
}
}