diff --git a/jmespath-jackson-jr/pom.xml b/jmespath-jackson-jr/pom.xml new file mode 100644 index 0000000..7362efb --- /dev/null +++ b/jmespath-jackson-jr/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + jmespath-jackson-jr + JMESPath Jackson jr + A JMESPath implementation for Java + + + io.burt + jmespath + 0.5.2-SNAPSHOT + + + + + ${project.groupId} + jmespath-core + ${project.parent.version} + + + ${project.groupId} + jmespath-core + ${project.parent.version} + test-jar + test + + + com.fasterxml.jackson.jr + jackson-jr-objects + 2.14.2 + + + com.fasterxml.jackson.jr + jackson-jr-stree + 2.14.2 + + + + \ No newline at end of file diff --git a/jmespath-jackson-jr/src/main/java/io/burt/jmespath/jacksonjr/JacksonJrRuntime.java b/jmespath-jackson-jr/src/main/java/io/burt/jmespath/jacksonjr/JacksonJrRuntime.java new file mode 100644 index 0000000..749fe2a --- /dev/null +++ b/jmespath-jackson-jr/src/main/java/io/burt/jmespath/jacksonjr/JacksonJrRuntime.java @@ -0,0 +1,230 @@ +package io.burt.jmespath.jacksonjr; + +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.jr.ob.JSON; +import com.fasterxml.jackson.jr.stree.JacksonJrsTreeCodec; +import com.fasterxml.jackson.jr.stree.JrsArray; +import com.fasterxml.jackson.jr.stree.JrsBoolean; +import com.fasterxml.jackson.jr.stree.JrsNull; +import com.fasterxml.jackson.jr.stree.JrsNumber; +import com.fasterxml.jackson.jr.stree.JrsObject; +import com.fasterxml.jackson.jr.stree.JrsString; +import com.fasterxml.jackson.jr.stree.JrsValue; +import io.burt.jmespath.BaseRuntime; +import io.burt.jmespath.JmesPathType; +import io.burt.jmespath.RuntimeConfiguration; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class JacksonJrRuntime extends BaseRuntime { + private final JSON json; + + public JacksonJrRuntime() { + this(RuntimeConfiguration.defaultConfiguration()); + } + + public JacksonJrRuntime(RuntimeConfiguration configuration) { + this(configuration, JSON.builder() + .treeCodec(new JacksonJrsTreeCodec()) + .build()); + } + + public JacksonJrRuntime(RuntimeConfiguration configuration, JSON json) { + super(configuration); + this.json = json; + } + + @Override + public JrsValue parseString(String str) { + try { + return json.treeFrom(str); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private static class JrsArrayListWrapper extends AbstractList { + private final JrsArray array; + + JrsArrayListWrapper(JrsArray array) { + this.array = array; + } + + @Override + public JrsValue get(int index) { + return array.get(index); + } + + @Override + public int size() { + return array.size(); + } + } + + @Override + public List toList(JrsValue value) { + if (value == null) { + return Collections.emptyList(); + } else if (value.isArray()) { + return new JrsArrayListWrapper((JrsArray) value); + } else if (value.isObject()) { + JrsObject object = (JrsObject) value; + List list = new ArrayList<>(object.size()); + Iterator> iterator = object.fields(); + + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + list.add(entry.getValue()); + } + return list; + } else { + return Collections.emptyList(); + } + } + + @Override + public String toString(JrsValue value) { + if (JsonToken.VALUE_STRING.equals(value.asToken())) { + return value.asText(); + } else { + try { + return json.asString(value); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + + @Override + public Number toNumber(JrsValue value) { + if (value.isValueNode() && value.isNumber()) { + JrsNumber number = (JrsNumber) value; + return number.getValue(); + } else { + return null; + } + } + + @Override + public boolean isTruthy(JrsValue value) { + if (value.isContainerNode()) { + return value.size() > 0; + } else if (value.isValueNode()) { + switch (value.asToken()) { + case VALUE_STRING: + return !value.asText().isEmpty(); + case VALUE_FALSE: + case VALUE_NULL: + return false; + default: + return true; + } + } else { + return !value.isMissingNode(); + } + } + + @Override + public JmesPathType typeOf(JrsValue value) { + switch (value.asToken()) { + case START_ARRAY: + return JmesPathType.ARRAY; + case VALUE_EMBEDDED_OBJECT: + case START_OBJECT: + return JmesPathType.OBJECT; + case VALUE_STRING: + return JmesPathType.STRING; + case VALUE_NUMBER_INT: + case VALUE_NUMBER_FLOAT: + return JmesPathType.NUMBER; + case VALUE_TRUE: + case VALUE_FALSE: + return JmesPathType.BOOLEAN; + case VALUE_NULL: + return JmesPathType.NULL; + case NOT_AVAILABLE: + default: + throw new IllegalStateException(String.format("Unknown node type encountered: %s", value.asToken())); + } + } + + @Override + public JrsValue getProperty(JrsValue value, JrsValue name) { + if (JsonToken.VALUE_NULL.equals(value.asToken())) { + return JrsNull.instance(); + } else { + JrsValue node = value.get(name.asText()); + return node != null ? node : createNull(); + } + } + + @Override + public Collection getPropertyNames(JrsValue value) { + if (value.isObject()) { + List names = new ArrayList<>(value.size()); + Iterator fieldNames = value.fieldNames(); + while (fieldNames.hasNext()) { + names.add(createString(fieldNames.next())); + } + return names; + } else { + return Collections.emptyList(); + } + } + + @Override + public JrsValue createNull() { + return JrsNull.instance(); + } + + @Override + public JrsValue createArray(Collection elements) { + List values = new ArrayList<>(); + for (JrsValue node: elements) { + if (node == null) { + values.add(JrsNull.instance()); + } else { + values.add(node); + } + } + return new JrsArray(values); + + } + + @Override + public JrsValue createString(String str) { + return new JrsString(str); + } + + @Override + public JrsValue createBoolean(boolean b) { + return b ? JrsBoolean.TRUE : JrsBoolean.FALSE; + } + + @Override + public JrsValue createObject(Map obj) { + Map values = new HashMap<>(); + for (Map.Entry entry : obj.entrySet()) { + values.put(entry.getKey().asText(), entry.getValue()); + } + return new JrsObject(values); + } + + @Override + public JrsValue createNumber(double n) { + return new JrsNumber(n); + } + + @Override + public JrsValue createNumber(long n) { + return new JrsNumber(n); + } +} diff --git a/jmespath-jackson-jr/src/test/java/io/burt/jmespath/jacksonjr/JacksonJrComplianceTest.java b/jmespath-jackson-jr/src/test/java/io/burt/jmespath/jacksonjr/JacksonJrComplianceTest.java new file mode 100644 index 0000000..040f129 --- /dev/null +++ b/jmespath-jackson-jr/src/test/java/io/burt/jmespath/jacksonjr/JacksonJrComplianceTest.java @@ -0,0 +1,12 @@ +package io.burt.jmespath.jacksonjr; + +import com.fasterxml.jackson.jr.stree.JrsValue; +import io.burt.jmespath.Adapter; +import io.burt.jmespath.JmesPathComplianceTest; + +public class JacksonJrComplianceTest extends JmesPathComplianceTest { + private Adapter runtime = new JacksonJrRuntime(); + + @Override + protected Adapter runtime() { return runtime; } +} diff --git a/jmespath-jackson-jr/src/test/java/io/burt/jmespath/jacksonjr/JacksonJrTest.java b/jmespath-jackson-jr/src/test/java/io/burt/jmespath/jacksonjr/JacksonJrTest.java new file mode 100644 index 0000000..2c1004f --- /dev/null +++ b/jmespath-jackson-jr/src/test/java/io/burt/jmespath/jacksonjr/JacksonJrTest.java @@ -0,0 +1,14 @@ +package io.burt.jmespath.jacksonjr; + + +import com.fasterxml.jackson.jr.stree.JrsValue; +import io.burt.jmespath.Adapter; +import io.burt.jmespath.JmesPathRuntimeTest; +import io.burt.jmespath.RuntimeConfiguration; + +public class JacksonJrTest extends JmesPathRuntimeTest { + @Override + protected Adapter createRuntime(RuntimeConfiguration configuration) { + return new JacksonJrRuntime(configuration); + } +}