Skip to content

Commit

Permalink
jsonpath support multi-names exists filter
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed Jan 16, 2024
1 parent 5b78609 commit 9ea8c8d
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 0 deletions.
59 changes: 59 additions & 0 deletions core/src/main/java/com/alibaba/fastjson2/JSONPathFilter.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.alibaba.fastjson2;

import com.alibaba.fastjson2.util.Fnv;
import com.alibaba.fastjson2.util.IOUtils;
import com.alibaba.fastjson2.writer.FieldWriter;
import com.alibaba.fastjson2.writer.ObjectWriter;
Expand Down Expand Up @@ -1034,6 +1035,64 @@ public boolean apply(JSONPath.Context context, Object object) {
}
}

static final class NamesExistsFilter
extends JSONPathFilter {
final String[] names;
final long[] nameHashCodes;

public NamesExistsFilter(List<String> names) {
this.names = names.toArray(new String[0]);
long[] nameHashCodes = new long[this.names.length];
for (int i = 0; i < nameHashCodes.length; i++) {
nameHashCodes[i] = Fnv.hashCode64(this.names[i]);
}
this.nameHashCodes = nameHashCodes;
}

@Override
public void eval(JSONPath.Context context) {
Object first = context.parent == null
? context.root
: context.parent.value;

Object object = first;
for (int i = 0; i < names.length; i++) {
String name = names[i];
if (object instanceof Map) {
Map map = (Map) object;
Object value = map.get(name);
if (i == names.length - 1 || value == null) {
context.value = value != null ? first : null;
return;
}
object = value;
}
}
}

@Override
public void accept(JSONReader jsonReader, JSONPath.Context context) {
eval(context);
}

@Override
public String toString() {
StringBuilder buf = new StringBuilder("exists(");
for (int i = 0; i < names.length; i++) {
if (i != 0) {
buf.append(", ");
}
}
buf.append(')');
return buf.toString();
}

@Override
public boolean apply(JSONPath.Context context, Object object) {
throw new UnsupportedOperationException();
}
}

static final class NameIntBetweenSegment
extends NameFilter {
private final long begin;
Expand Down
18 changes: 18 additions & 0 deletions core/src/main/java/com/alibaba/fastjson2/JSONPathParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,24 @@ JSONPathSegment parseFilter() {
}
}
return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
} else if (jsonReader.ch == '.') {
List<String> names = new ArrayList<>();
names.add(fieldName);
do {
jsonReader.next();
fieldName = jsonReader.readFieldNameUnquote();
names.add(fieldName);
} while (jsonReader.ch == '.');

if (jsonReader.nextIfMatch(')')) {
if (parentheses) {
if (!jsonReader.nextIfMatch(')')) {
throw new JSONException(jsonReader.info("jsonpath syntax error"));
}
}
}

return new JSONPathFilter.NamesExistsFilter(names);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.alibaba.fastjson2.issues_2100;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONPath;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;

public class Issue2190 {
@Test
public void test() {
String str = "{\"code\":0,\"errMsg\":\"\",\"Data\":{\"is_focus\":1,\"user_info\":{\"userid\":\"13814\",\"nickname\":\"没事改昵称\"}}}";
JSONObject jsonObject = JSON.parseObject(str);
assertSame(jsonObject, JSONPath.eval(jsonObject, "[?exists(@.Data.user_info.userid)]"));
assertNull(JSONPath.eval(jsonObject, "[?exists(@.Data.user_info.userid1)]"));
assertNull(JSONPath.eval(jsonObject, "[?exists(@.Data.user_info1.userid)]"));
assertNull(JSONPath.eval(jsonObject, "[?exists(@.Data1.user_info.userid)]"));
}
}

0 comments on commit 9ea8c8d

Please sign in to comment.