diff --git a/build.gradle b/build.gradle index d1e8135c..623cba9f 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ ext { jsonSmart: 'net.minidev:json-smart:2.2.1', jacksonDatabind: 'com.fasterxml.jackson.core:jackson-databind:2.6.3', gson: 'com.google.code.gson:gson:2.3.1', + jettison: 'org.codehaus.jettison:jettison:1.3.7', jsonOrg: 'org.json:json:20140107', tapestryJson: 'org.apache.tapestry:tapestry-json:5.4.0', hamcrestCore: 'org.hamcrest:hamcrest-core:1.3', diff --git a/json-path/build.gradle b/json-path/build.gradle index 7fb1d20a..fa79910b 100644 --- a/json-path/build.gradle +++ b/json-path/build.gradle @@ -20,6 +20,7 @@ dependencies { compile libs.gson, optional compile libs.jsonOrg, optional compile libs.tapestryJson, optional + compile libs.jettison, optional testCompile libs.test } @@ -38,7 +39,7 @@ task distZip(type: Zip, dependsOn: assemble) { } from(project.configurations.compile) { into 'lib' - exclude { it.file.name.contains('gson') || it.file.name.contains('jackson') || it.file.name.contains('json-2') } + exclude { it.file.name.contains('gson') || it.file.name.contains('jackson') || it.file.name.contains('json-2') || it.file.name.contains('jettison') } } from(project.configurations.compile) { into 'lib-optional/jackson' @@ -48,6 +49,10 @@ task distZip(type: Zip, dependsOn: assemble) { into 'lib-optional/gson' include { it.file.name.contains('gson') } } + from(project.configurations.compile) { + into 'lib-optional/jettison' + include { it.file.name.contains('jettison') } + } from(project.configurations.compile) { into 'lib-optional/jsonOrg' include { it.file.name.contains('json-2') } @@ -74,7 +79,7 @@ task distTar(type: Tar, dependsOn: assemble) { } from(project.configurations.compile) { into 'lib' - exclude { it.file.name.contains('gson') || it.file.name.contains('jackson') || it.file.name.contains('json-2')} + exclude { it.file.name.contains('gson') || it.file.name.contains('jackson') || it.file.name.contains('json-2') || it.file.name.contains('jettison') } } from(project.configurations.compile) { into 'lib-optional/jackson' @@ -84,6 +89,10 @@ task distTar(type: Tar, dependsOn: assemble) { into 'lib-optional/gson' include { it.file.name.contains('gson') } } + from(project.configurations.compile) { + into 'lib-optional/jettison' + include { it.file.name.contains('jettison') } + } from(project.configurations.compile) { into 'lib-optional/jsonOrg' include { it.file.name.contains('json-2') } diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JettisonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JettisonProvider.java new file mode 100644 index 00000000..110709b7 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JettisonProvider.java @@ -0,0 +1,432 @@ +/* + * Copyright 2011 the original author or authors. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jayway.jsonpath.spi.json; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import org.codehaus.jettison.json.JSONException; +import com.jayway.jsonpath.InvalidJsonException; + +public class JettisonProvider extends AbstractJsonProvider +{ + private static Object jettisonUnwrap(Object obj) + { + if( obj!=null && obj.equals(org.codehaus.jettison.json.JSONObject.NULL) ) + { + return null; + } + return obj; + } + + private static Object jettisonWrap(Object obj) + { + if( obj==null ) + { + return org.codehaus.jettison.json.JSONObject.NULL; + } + return obj; + } + + /** JSON Object implementation */ + private static class JettisonTokener extends org.codehaus.jettison.json.JSONTokener + { + public JettisonTokener(String s) + { + super(s); + } + @Override + protected JettisonObject newJSONObject() throws JSONException + { + return new JettisonObject(this); + } + @Override + protected JettisonArray newJSONArray() throws JSONException + { + return new JettisonArray(this); + } + } + + /** JSON Object implementation */ + private static class JettisonObject extends org.codehaus.jettison.json.JSONObject implements Iterable + { + private static final long serialVersionUID = 1L; + + private JettisonObject(JettisonTokener tokener) throws JSONException + { + super(tokener); + } + + private JettisonObject() + { + super(); + } + + @Override + public Iterator iterator() + { + return new JettisonObjectIterator(this); + } + } + /** JSON Array implementation */ + private static class JettisonArray extends org.codehaus.jettison.json.JSONArray implements Iterable + { + private static final long serialVersionUID = 2L; + + private JettisonArray(JettisonTokener tokener) throws JSONException + { + super(tokener); + } + + private JettisonArray() + { + super(); + } + + @Override + public Iterator iterator() + { + return new JettisonArrayIterator(this); + } + } + + private static class JettisonArrayIterator implements Iterator + { + private final org.codehaus.jettison.json.JSONArray jsonArray; + private int index = 0; + + private JettisonArrayIterator(org.codehaus.jettison.json.JSONArray jsonArray) + { + this.jsonArray = jsonArray; + } + + @Override + public boolean hasNext() + { + return index < jsonArray.length(); + } + + @Override + public Object next() + { + try + { + return jettisonUnwrap(jsonArray.get(index++)); + } + catch( org.codehaus.jettison.json.JSONException jsonException ) + { + throw new NoSuchElementException(jsonException.toString()); + } + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } + + } + + private static class JettisonObjectIterator implements Iterator + { + private final org.codehaus.jettison.json.JSONObject jsonObject; + private final Iterator jsonKeysIt; + + + private JettisonObjectIterator(org.codehaus.jettison.json.JSONObject jsonObject) + { + this.jsonObject = jsonObject; + this.jsonKeysIt = jsonObject.keys(); + } + + @Override + public boolean hasNext() + { + return jsonKeysIt.hasNext(); + } + + @Override + public Object next() + { + try + { + return jettisonUnwrap(jsonObject.get(String.valueOf(jsonKeysIt.next()))); + } + catch( org.codehaus.jettison.json.JSONException jsonException ) + { + throw new NoSuchElementException(jsonException.toString()); + } + } + + @Override + public void remove() + { + jsonKeysIt.remove(); + } + + } + + private Object parse(JettisonTokener JsonTokener) + { + try + { + char nextChar = JsonTokener.nextClean(); + JsonTokener.back(); + if (nextChar == '{') + { + return new JettisonObject(JsonTokener); + } + if (nextChar == '[') + { + return new JettisonArray(JsonTokener); + } + throw new JSONException("Invalid JSON"); + } + catch( org.codehaus.jettison.json.JSONException jsonException ) + { + throw new IllegalStateException(jsonException); + } + } + + @Override + public Object parse(String json) throws InvalidJsonException + { + return parse(new JettisonTokener(json)); + } + + @Override + public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException + { + try + { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int size; + while( (size=jsonStream.read(buffer))>0 ) + { + stream.write(buffer, 0, size); + } + return parse(new JettisonTokener(new String(stream.toByteArray(), charset))); + } + catch( IOException ioe ) + { + throw new InvalidJsonException(ioe); + } + } + + @Override + public String toJson(Object obj) + { + try + { + if( obj instanceof org.codehaus.jettison.json.JSONArray ) + { + return ((org.codehaus.jettison.json.JSONArray)obj).toString(2); + } + if( obj instanceof org.codehaus.jettison.json.JSONObject ) + { + return ((org.codehaus.jettison.json.JSONObject)obj).toString(2); + } + return String.valueOf(obj); + //return "\"" + String.valueOf(obj).replaceAll("\"", "\\\"") + "\""; + } + catch( org.codehaus.jettison.json.JSONException jsonException ) + { + throw new IllegalStateException(jsonException); + } + } + + @Override + public Object createMap() + { + return new JettisonObject(); + } + + @Override + public Iterable createArray() + { + return new JettisonArray(); + } + + @Override + public Object unwrap(Object obj) + { + return jettisonUnwrap(obj); + } + + @Override + public boolean isArray(Object obj) + { + return obj instanceof org.codehaus.jettison.json.JSONArray; + } + + @Override + public boolean isMap(Object obj) + { + return obj instanceof org.codehaus.jettison.json.JSONObject; + } + + @Override + public int length(Object obj) + { + if( obj instanceof org.codehaus.jettison.json.JSONArray ) + { + return ((org.codehaus.jettison.json.JSONArray) obj).length(); + } + if( obj instanceof org.codehaus.jettison.json.JSONObject ) + { + return ((org.codehaus.jettison.json.JSONObject)obj).length(); + } + if( obj instanceof String ) + { + return ((String)obj).length(); + } + return 0; + } + + @Override + public Iterable toIterable(final Object obj) + { + return new Iterable() + { + @Override + public Iterator iterator() + { + if( obj instanceof org.codehaus.jettison.json.JSONArray ) + { + return new JettisonArrayIterator((org.codehaus.jettison.json.JSONArray)obj); + } + if( obj instanceof org.codehaus.jettison.json.JSONObject ) + { + return new JettisonObjectIterator((org.codehaus.jettison.json.JSONObject)obj); + } + return Collections.emptyList().iterator(); + } + }; + + } + + public Collection getPropertyKeys(Object obj) + { + List keys = new ArrayList(length(obj)); + + if( obj instanceof org.codehaus.jettison.json.JSONArray ) + { + for (int i = 0; i < length(obj); i++) + { + keys.add(String.valueOf(i)); + } + } + if( obj instanceof org.codehaus.jettison.json.JSONObject ) + { + Iterator keysIt = ((org.codehaus.jettison.json.JSONObject)obj).keys(); + while (keysIt.hasNext()) + { + keys.add(String.valueOf(keysIt.next())); + } + } + return keys; + } + + + @Override + public Object getArrayIndex(Object obj, int index) + { + return jettisonUnwrap(((org.codehaus.jettison.json.JSONArray)obj).opt(index)); + } + + @Override public void setArrayIndex(Object array, int index, Object value) + { + if( !isArray(array) ) + { + throw new UnsupportedOperationException(); + } + + try + { + ((org.codehaus.jettison.json.JSONArray)array).put(index, jettisonWrap(value)); + } + catch( org.codehaus.jettison.json.JSONException jsonException ) + { + throw new IllegalArgumentException(jsonException); + } + } + + @Override + public Object getMapValue(Object obj, String key) + { + Object value = ((org.codehaus.jettison.json.JSONObject)obj).opt(key); + if( value==null ) + { + return com.jayway.jsonpath.spi.json.JsonProvider.UNDEFINED; + } + return jettisonUnwrap(value); + } + + @Override + public void setProperty(Object obj, Object key, Object value) + { + try + { + if( obj instanceof org.codehaus.jettison.json.JSONArray ) + { + int index = key instanceof Integer? (Integer) key : Integer.parseInt(key.toString()); + ((org.codehaus.jettison.json.JSONArray)obj).put(index, jettisonWrap(value)); + } + if( obj instanceof org.codehaus.jettison.json.JSONObject ) + { + ((org.codehaus.jettison.json.JSONObject)obj).put(String.valueOf(key), jettisonWrap(value)); + } + } + catch( org.codehaus.jettison.json.JSONException jsonException ) + { + throw new IllegalStateException(jsonException); + } + } + + @Override + public void removeProperty(Object obj, Object key) + { + try + { + if( obj instanceof org.codehaus.jettison.json.JSONArray ) + { + int index = key instanceof Integer? (Integer) key : Integer.parseInt(key.toString()); + if( index