diff --git a/src/main/java/com/alibaba/fastjson/JSONPath.java b/src/main/java/com/alibaba/fastjson/JSONPath.java index 66d644ce6..f36dfb14a 100644 --- a/src/main/java/com/alibaba/fastjson/JSONPath.java +++ b/src/main/java/com/alibaba/fastjson/JSONPath.java @@ -214,6 +214,74 @@ public class JSONPath implements ObjectSerializer { skipWhitespace(); + if (op == BinaryCompareOperator.IN || op == BinaryCompareOperator.NOT_IN) { + final boolean not = (op == BinaryCompareOperator.NOT_IN); + accept('('); + + List valueList = new ArrayList(); + { + Object value = readValue(); + valueList.add(value); + + for (;;) { + skipWhitespace(); + if (ch != ',') { + break; + } + next(); + skipWhitespace(); + + value = readValue(); + valueList.add(value); + } + + accept(')'); + if (predicateFlag) { + accept(')'); + } + accept(']'); + } + + boolean isInt = true; + boolean isString = true; + for (Object item : valueList) { + if (item == null) { + if (isInt) { + isInt = false; + } + continue; + } + + Class clazz = item.getClass(); + if (isInt + && !(clazz == Byte.class || clazz == Short.class || clazz == Integer.class || clazz == Long.class)) { + isInt = false; + } + + if (isString && clazz != String.class) { + isString = false; + } + } + + if (isInt) { + long[] values = new long[valueList.size()]; + for (int i = 0; i < values.length; ++i) { + values[i] = ((Number) valueList.get(i)).longValue(); + } + + return new IntInSegement(propertyName, values, not); + } + + if (isString) { + String[] values = new String[valueList.size()]; + valueList.toArray(values); + + return new StringInSegement(propertyName, values, not); + } + + throw new UnsupportedOperationException(); + } + if (ch == '\'' || ch == '"') { String strValue = readString(); if (predicateFlag) { @@ -284,19 +352,8 @@ public class JSONPath implements ObjectSerializer { return new StringCompareFilterAccessSegement(propertyName, strValue, op); } - if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) { - int beginIndex = pos - 1; - if (ch == '+' || ch == '-') { - next(); - } - - while (ch >= '0' && ch <= '9') { - next(); - } - - int endIndex = pos - 1; - String text = path.substring(beginIndex, endIndex); - long value = Long.parseLong(text); + if (isDigitFirst(ch)) { + long value = readLongValue(); if (predicateFlag) { accept(')'); @@ -344,6 +401,48 @@ public class JSONPath implements ObjectSerializer { return buildArraySegement(text); } + protected long readLongValue() { + int beginIndex = pos - 1; + if (ch == '+' || ch == '-') { + next(); + } + + while (ch >= '0' && ch <= '9') { + next(); + } + + int endIndex = pos - 1; + String text = path.substring(beginIndex, endIndex); + long value = Long.parseLong(text); + return value; + } + + protected Object readValue() { + if (isDigitFirst(ch)) { + return readLongValue(); + } + + if (ch == '"' || ch == '\'') { + return readString(); + } + + if (ch == 'n') { + String name = readName(); + + if ("null".equals(name)) { + return null; + } else { + throw new JSONPathException(path); + } + } + + throw new UnsupportedOperationException(); + } + + static boolean isDigitFirst(char ch) { + return ch == '-' || ch == '+' || (ch >= '0' && ch <= '9'); + } + protected BinaryCompareOperator readOp() { BinaryCompareOperator op = null; if (ch == '=') { @@ -383,6 +482,8 @@ public class JSONPath implements ObjectSerializer { op = BinaryCompareOperator.NOT_LIKE; } else if ("rlike".equalsIgnoreCase(name)) { op = BinaryCompareOperator.NOT_RLIKE; + } else if ("in".equalsIgnoreCase(name)) { + op = BinaryCompareOperator.NOT_IN; } else { throw new UnsupportedOperationException(); } @@ -391,6 +492,8 @@ public class JSONPath implements ObjectSerializer { op = BinaryCompareOperator.LIKE; } else if ("rlike".equalsIgnoreCase(name)) { op = BinaryCompareOperator.RLIKE; + } else if ("in".equalsIgnoreCase(name)) { + op = BinaryCompareOperator.IN; } else { throw new UnsupportedOperationException(); } @@ -742,6 +845,67 @@ public class JSONPath implements ObjectSerializer { } } + static class IntInSegement extends FilterAccessSegement { + + private final String propertyName; + private final long[] values; + private final boolean not; + + public IntInSegement(String propertyName, long[] values, boolean not){ + this.propertyName = propertyName; + this.values = values; + this.not = not; + } + + @Override + public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) { + Object propertyValue = path.getPropertyValue(item, propertyName, false); + + if (propertyValue == null) { + return false; + } + + if (propertyValue instanceof Number) { + long longPropertyValue = ((Number) propertyValue).longValue(); + for (long value : values) { + if (value == longPropertyValue) { + return !not; + } + } + } + + return not; + } + } + + static class StringInSegement extends FilterAccessSegement { + + private final String propertyName; + private final String[] values; + private final boolean not; + + public StringInSegement(String propertyName, String[] values, boolean not){ + this.propertyName = propertyName; + this.values = values; + this.not = not; + } + + @Override + public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) { + Object propertyValue = path.getPropertyValue(item, propertyName, false); + + for (String value : values) { + if (value == propertyValue) { + return !not; + } else if (value != null && value.equals(propertyValue)) { + return !not; + } + } + + return not; + } + } + static class IntCompareFilterAccessSegement extends FilterAccessSegement { private final String propertyName; @@ -938,7 +1102,7 @@ public class JSONPath implements ObjectSerializer { } static enum BinaryCompareOperator { - EQ, NE, GT, GE, LT, LE, LIKE, NOT_LIKE, RLIKE, NOT_RLIKE + EQ, NE, GT, GE, LT, LE, LIKE, NOT_LIKE, RLIKE, NOT_RLIKE, IN, NOT_IN } static abstract class FilterAccessSegement implements Segement { diff --git a/src/test/java/com/alibaba/json/bvt/path/JSONPath_field_access_filter_in_int.java b/src/test/java/com/alibaba/json/bvt/path/JSONPath_field_access_filter_in_int.java new file mode 100644 index 000000000..f7751fbe1 --- /dev/null +++ b/src/test/java/com/alibaba/json/bvt/path/JSONPath_field_access_filter_in_int.java @@ -0,0 +1,102 @@ +package com.alibaba.json.bvt.path; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.fastjson.JSONPath; + +public class JSONPath_field_access_filter_in_int extends TestCase { + + public void test_list_in() throws Exception { + JSONPath path = new JSONPath("[id in (1001)]"); + + List entities = new ArrayList(); + entities.add(new Entity(1001, "ljw2083")); + entities.add(new Entity(1002, "wenshao")); + entities.add(new Entity(1003, "yakolee")); + entities.add(new Entity(1004, null)); + + List result = (List) path.eval(entities); + Assert.assertEquals(1, result.size()); + Assert.assertSame(entities.get(0), result.get(0)); + } + + public void test_list_not_in() throws Exception { + JSONPath path = new JSONPath("[id not in (1001)]"); + + List entities = new ArrayList(); + entities.add(new Entity(1001, "ljw2083")); + entities.add(new Entity(1002, "wenshao")); + entities.add(new Entity(1003, "yakolee")); + entities.add(new Entity(1004, null)); + + List result = (List) path.eval(entities); + Assert.assertEquals(3, result.size()); + Assert.assertSame(entities.get(1), result.get(0)); + Assert.assertSame(entities.get(2), result.get(1)); + Assert.assertSame(entities.get(3), result.get(2)); + } + + public void test_list_in_2() throws Exception { + JSONPath path = new JSONPath("[id in (1001, 1003)]"); + + List entities = new ArrayList(); + entities.add(new Entity(1001, "ljw2083")); + entities.add(new Entity(1002, "wenshao")); + entities.add(new Entity(1003, "yakolee")); + entities.add(new Entity(1004, null)); + + List result = (List) path.eval(entities); + Assert.assertEquals(2, result.size()); + Assert.assertSame(entities.get(0), result.get(0)); + Assert.assertSame(entities.get(2), result.get(1)); + } + + public void test_list_in_3() throws Exception { + JSONPath path = new JSONPath("[id in (1001, 1003, 1004)]"); + + List entities = new ArrayList(); + entities.add(new Entity(1001, "ljw2083")); + entities.add(new Entity(1002, "wenshao")); + entities.add(new Entity(1003, "yakolee")); + entities.add(new Entity(1004, null)); + + List result = (List) path.eval(entities); + Assert.assertEquals(3, result.size()); + Assert.assertSame(entities.get(0), result.get(0)); + Assert.assertSame(entities.get(2), result.get(1)); + Assert.assertSame(entities.get(3), result.get(2)); + } + + public static class Entity { + + private Integer id; + private String name; + + public Entity(Integer id, String name){ + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } +} diff --git a/src/test/java/com/alibaba/json/bvt/path/JSONPath_field_access_filter_in_string.java b/src/test/java/com/alibaba/json/bvt/path/JSONPath_field_access_filter_in_string.java new file mode 100644 index 000000000..70cc50c1a --- /dev/null +++ b/src/test/java/com/alibaba/json/bvt/path/JSONPath_field_access_filter_in_string.java @@ -0,0 +1,102 @@ +package com.alibaba.json.bvt.path; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.fastjson.JSONPath; + +public class JSONPath_field_access_filter_in_string extends TestCase { + + public void test_list_in() throws Exception { + JSONPath path = new JSONPath("[name in ('ljw2083')]"); + + List entities = new ArrayList(); + entities.add(new Entity(1001, "ljw2083")); + entities.add(new Entity(1002, "wenshao")); + entities.add(new Entity(1003, "yakolee")); + entities.add(new Entity(1004, null)); + + List result = (List) path.eval(entities); + Assert.assertEquals(1, result.size()); + Assert.assertSame(entities.get(0), result.get(0)); + } + + public void test_list_not_in() throws Exception { + JSONPath path = new JSONPath("[name not in ('ljw2083')]"); + + List entities = new ArrayList(); + entities.add(new Entity(1001, "ljw2083")); + entities.add(new Entity(1002, "wenshao")); + entities.add(new Entity(1003, "yakolee")); + entities.add(new Entity(1004, null)); + + List result = (List) path.eval(entities); + Assert.assertEquals(3, result.size()); + Assert.assertSame(entities.get(1), result.get(0)); + Assert.assertSame(entities.get(2), result.get(1)); + Assert.assertSame(entities.get(3), result.get(2)); + } + + public void test_list_in_2() throws Exception { + JSONPath path = new JSONPath("[name in ('ljw2083', 'yakolee')]"); + + List entities = new ArrayList(); + entities.add(new Entity(1001, "ljw2083")); + entities.add(new Entity(1002, "wenshao")); + entities.add(new Entity(1003, "yakolee")); + entities.add(new Entity(1004, null)); + + List result = (List) path.eval(entities); + Assert.assertEquals(2, result.size()); + Assert.assertSame(entities.get(0), result.get(0)); + Assert.assertSame(entities.get(2), result.get(1)); + } + + public void test_list_in_3() throws Exception { + JSONPath path = new JSONPath("[name in ('ljw2083', 'yakolee',null)]"); + + List entities = new ArrayList(); + entities.add(new Entity(1001, "ljw2083")); + entities.add(new Entity(1002, "wenshao")); + entities.add(new Entity(1003, "yakolee")); + entities.add(new Entity(1004, null)); + + List result = (List) path.eval(entities); + Assert.assertEquals(3, result.size()); + Assert.assertSame(entities.get(0), result.get(0)); + Assert.assertSame(entities.get(2), result.get(1)); + Assert.assertSame(entities.get(3), result.get(2)); + } + + public static class Entity { + + private Integer id; + private String name; + + public Entity(Integer id, String name){ + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } +}