JSON refactor - tidy up

This commit is contained in:
Rob Bygrave
2014-11-12 22:10:28 +13:00
parent 221272328c
commit eaee3a9c02
31 changed files with 935 additions and 1033 deletions
+1
View File
@@ -1391,6 +1391,7 @@ public final class Ebean {
/**
* Return the JsonContext for reading/writing JSON.
* @deprecated Please use #json instead.
*/
public static JsonContext createJsonContext() {
return json();
@@ -1120,6 +1120,7 @@ public interface EbeanServer {
/**
* Return the JsonContext for reading/writing JSON.
* @deprecated Please use #json instead.
*/
public JsonContext createJsonContext();
@@ -1,7 +1,7 @@
package com.avaje.ebean.text;
/**
* An exception occured typically in processing CSV, JSON or XML.
* An exception occurred typically in processing CSV, JSON or XML.
*
* @author rbygrave
*/
@@ -11,8 +11,6 @@ import com.fasterxml.jackson.core.JsonParser;
/**
* Converts objects to and from JSON format.
*
* @author rbygrave
*/
public interface JsonContext {
@@ -53,39 +51,39 @@ public interface JsonContext {
* Write the bean or collection in JSON format to the writer with default
* options.
*
* @param o
* @param value
* the bean or collection of beans to write
* @param writer
* used to write the json output to
*/
public void toJsonWriter(Object o, Writer writer) throws IOException;
public void toJson(Object value, Writer writer) throws IOException;
/**
* With additional options to specify JsonValueAdapter and
* JsonWriteBeanVisitor's.
*
* @param o
* @param value
* the bean or collection of beans to write
* @param writer
* used to write the json output to
* @param options
* additional options to control the JSON output
*/
public void toJsonWriter(Object o, Writer writer, JsonWriteOptions options) throws IOException;
public void toJson(Object value, Writer writer, JsonWriteOptions options) throws IOException;
/**
* Convert a bean or collection to json string using default options.
*/
public String toJsonString(Object o) throws IOException;
public String toJson(Object value) throws IOException;
/**
* Convert a bean or collection to json string.
*/
public String toJsonString(Object o, JsonWriteOptions options) throws IOException;
public String toJson(Object value, JsonWriteOptions options) throws IOException;
/**
* Return true if the type is known as an Entity or Xml type or a List Set or
* Map of known bean types.
* Return true if the type is known as an Entity bean or a List Set or
* Map of entity beans.
*/
public boolean isSupportedType(Type genericType);
@@ -1,72 +1,23 @@
package com.avaje.ebean.text.json;
import java.util.LinkedHashSet;
import java.util.Set;
import com.avaje.ebean.text.PathProperties;
/**
* Provides options for customising the JSON write process.
* <p>
* You can optionally provide a custom JsonValueAdapter to handle specific
* formatting for Date and DateTime types.
* </p>
* <p>
* You can optionally register JsonWriteBeanVisitors to customise the processing
* of the beans as they are processed and <strong>add raw JSON
* elements</strong>.
* </p>
* <p>
* You can explicitly state which properties to include in the JSON output for
* the root level and each path.
* </p>
*
* <pre class="code">
* // find some customers ...
*
* List&lt;Customer&gt; list = Ebean.find(Customer.class).select(&quot;id, name, status, shippingAddress&quot;)
* .fetch(&quot;billingAddress&quot;,
* &quot;line1, city&quot;).fetch(&quot;billingAddress.country&quot;, &quot;*&quot;).fetch(&quot;contacts&quot;, &quot;firstName,email&quot;)
* .order().desc(&quot;id&quot;)
* .findList();
*
* JsonContext json = Ebean.createJsonContext();
*
* JsonWriteOptions writeOptions = new JsonWriteOptions();
* writeOptions.setRootPathVisitor(new JsonWriteBeanVisitor&lt;Customer&gt;() {
*
* public void visit(Customer bean, JsonWriter ctx) {
* System.out.println(&quot;write visit customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummyCust&quot;, &quot;34&quot;);
* ctx.appendKeyValue(&quot;smallCustObject&quot;, &quot;{\&quot;a\&quot;:34,\&quot;b\&quot;:\&quot;asdasdasd\&quot;}&quot;);
* }
* });
*
* writeOptions.setPathProperties(&quot;contacts&quot;, &quot;firstName,id&quot;);
* writeOptions.setPathVisitor(&quot;contacts&quot;, new JsonWriteBeanVisitor&lt;Contact&gt;() {
*
* public void visit(Contact bean, JsonWriter ctx) {
* System.out.println(&quot;write additional custom json on customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummy&quot;, &quot; 3400&quot; + bean.getId() + &quot;&quot;);
* ctx.appendKeyValue(&quot;smallObject&quot;, &quot;{\&quot;contactA\&quot;:34,\&quot;contactB\&quot;:\&quot;banana\&quot;}&quot;);
* }
*
* });
*
* // output as a JSON string with pretty formatting
* String s = json.toJsonString(list, true, writeOptions);
* String s = json.toJson(list, true, writeOptions);
*
* </pre>
*
* @see JsonContext#toList(Class, String, JsonReadOptions)
*
* @author rbygrave
*
*/
public class JsonWriteOptions {
protected String callback;
protected PathProperties pathProperties;
/**
@@ -82,94 +33,6 @@ public class JsonWriteOptions {
return o;
}
/**
* This creates and returns a copy of these options.
* <p>
* Note that it assumes that the JsonWriteBeanVisitor (if defined) are
* immutable and any JsonWriteBeanVisitor instances are shared between the
* original and the copy.
* </p>
*/
public JsonWriteOptions copy() {
JsonWriteOptions copy = new JsonWriteOptions();
copy.callback = callback;
copy.pathProperties = pathProperties;
return copy;
}
/**
* Return a JSONP callback function.
*/
public String getCallback() {
return callback;
}
/**
* Set a JSONP callback function.
*/
public JsonWriteOptions setCallback(String callback) {
this.callback = callback;
return this;
}
/**
* Set the properties to include in the JSON output for the given path.
*
* @param propertiesToInclude
* The set of properties to output
*/
public JsonWriteOptions setPathProperties(String path, Set<String> propertiesToInclude) {
if (pathProperties == null) {
pathProperties = new PathProperties();
}
pathProperties.put(path, propertiesToInclude);
return this;
}
/**
* Set the properties to include in the JSON output for the given path.
*
* @param propertiesToInclude
* Comma delimited list of properties to output
*/
public JsonWriteOptions setPathProperties(String path, String propertiesToInclude) {
return setPathProperties(path, parseProps(propertiesToInclude));
}
/**
* Set the properties to include in the JSON output for the root level.
*
* @param propertiesToInclude
* Comma delimited list of properties to output
*/
public JsonWriteOptions setRootPathProperties(String propertiesToInclude) {
return setPathProperties(null, parseProps(propertiesToInclude));
}
/**
* Set the properties to include in the JSON output for the root level.
*
* @param propertiesToInclude
* The set of properties to output
*/
public JsonWriteOptions setRootPathProperties(Set<String> propertiesToInclude) {
return setPathProperties(null, propertiesToInclude);
}
private Set<String> parseProps(String propertiesToInclude) {
LinkedHashSet<String> props = new LinkedHashSet<String>();
String[] split = propertiesToInclude.split(",");
for (int i = 0; i < split.length; i++) {
String s = split[i].trim();
if (s.length() > 0) {
props.add(s);
}
}
return props;
}
/**
* Set the Map of properties to include by path.
*/
@@ -19,31 +19,10 @@
* .order().desc(&quot;id&quot;)
* .findList();
*
* JsonContext json = Ebean.createJsonContext();
*
* JsonWriteOptions writeOptions = new JsonWriteOptions();
* writeOptions.setRootPathVisitor(new JsonWriteBeanVisitor&lt;Customer&gt;() {
*
* public void visit(Customer bean, JsonWriter ctx) {
* System.out.println(&quot;write visit customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummyCust&quot;, &quot;34&quot;);
* ctx.appendKeyValue(&quot;smallCustObject&quot;, &quot;{\&quot;a\&quot;:34,\&quot;b\&quot;:\&quot;asdasdasd\&quot;}&quot;);
* }
* });
*
* writeOptions.setPathProperties(&quot;contacts&quot;, &quot;firstName,id&quot;);
* writeOptions.setPathVisitor(&quot;contacts&quot;, new JsonWriteBeanVisitor&lt;Contact&gt;() {
*
* public void visit(Contact bean, JsonWriter ctx) {
* System.out.println(&quot;write additional custom json on customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummy&quot;, &quot; 3400&quot; + bean.getId() + &quot;&quot;);
* ctx.appendKeyValue(&quot;smallObject&quot;, &quot;{\&quot;contactA\&quot;:34,\&quot;contactB\&quot;:\&quot;banana\&quot;}&quot;);
* }
*
* });
*
* // output as a JSON string with pretty formatting
* String s = json.toJsonString(list, true, writeOptions);
* JsonContext json = Ebean.json();
*
* // output as a JSON string
* String jsonOutput = json.toJson(list);
*
* </pre>
*/
@@ -1,14 +1,14 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.text.TextException;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson.WriteBean;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.IOException;
public class BeanDescriptorJsonHelp<T> {
private final BeanDescriptor<T> desc;
@@ -56,7 +56,7 @@ public class BeanDescriptorJsonHelp<T> {
return null;
}
if (JsonToken.START_OBJECT != token) {
throw new IOException("Unexpected token "+token+" - expecting start_object at: "+parser.getCurrentLocation());
throw new JsonParseException("Unexpected token "+token+" - expecting start_object", parser.getCurrentLocation());
}
if (desc.inheritInfo == null) {
@@ -69,7 +69,7 @@ public class BeanDescriptorJsonHelp<T> {
token = parser.nextToken();
if (token != JsonToken.FIELD_NAME) {
String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?";
throw new TextException(msg);
throw new JsonParseException(msg, parser.getCurrentLocation());
}
String propName = parser.getCurrentName();
@@ -82,7 +82,7 @@ public class BeanDescriptorJsonHelp<T> {
return jsonReadProperties(parser, bean);
}
String msg = "Error reading inheritance discriminator, expected property ["+discColumn+"] but got [" + propName + "] ?";
throw new TextException(msg);
throw new JsonParseException(msg, parser.getCurrentLocation());
}
String discValue = parser.nextTextValue();
File diff suppressed because it is too large Load Diff
@@ -1,23 +1,8 @@
package com.avaje.ebeaninternal.server.text.json;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.json.EJson;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebean.text.TextException;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebean.text.json.JsonWriteOptions;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
@@ -25,16 +10,15 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.util.ParamTypeHelper;
import com.avaje.ebeaninternal.util.ParamTypeHelper.ManyType;
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.*;
import java.io.*;
import java.lang.reflect.Type;
import java.util.*;
import java.util.Map.Entry;
/**
* Default implementation of JsonContext.
*
* @author rbygrave
*/
public class DJsonContext implements JsonContext {
@@ -69,7 +53,7 @@ public class DJsonContext implements JsonContext {
private <T> T toBean(Class<T> cls, JsonParser parser) throws IOException {
BeanDescriptor<T> d = getDecriptor(cls);
BeanDescriptor<T> d = getDescriptor(cls);
return d.jsonRead(parser, null);
}
@@ -84,30 +68,25 @@ public class DJsonContext implements JsonContext {
private <T> List<T> toList(Class<T> cls, JsonParser src) throws IOException {
try {
BeanDescriptor<T> d = getDecriptor(cls);
BeanDescriptor<T> d = getDescriptor(cls);
List<T> list = new ArrayList<T>();
List<T> list = new ArrayList<T>();
JsonToken event = src.nextToken();
if (event != JsonToken.START_ARRAY) {
throw new JsonParseException("Expecting start_array event but got " + event ,src.getCurrentLocation());
}
do {
T bean = d.jsonRead(src, null);
if (bean == null) {
break;
} else {
list.add(bean);
}
} while (true);
return list;
} catch (RuntimeException e) {
throw new TextException("Error parsing " + src, e);
JsonToken event = src.nextToken();
if (event != JsonToken.START_ARRAY) {
throw new JsonParseException("Expecting start_array event but got " + event ,src.getCurrentLocation());
}
do {
T bean = d.jsonRead(src, null);
if (bean == null) {
break;
} else {
list.add(bean);
}
} while (true);
return list;
}
public Object toObject(Type genericType, String json) throws IOException {
@@ -122,7 +101,7 @@ public class DJsonContext implements JsonContext {
return toList(info.getBeanType(), json);
default:
throw new TextException("Type " + manyType + " not supported");
throw new IOException("Type " + manyType + " not supported");
}
}
@@ -138,26 +117,30 @@ public class DJsonContext implements JsonContext {
return toList(info.getBeanType(), json);
default:
throw new TextException("Type " + manyType + " not supported");
throw new IOException("Type " + manyType + " not supported");
}
}
public void toJsonWriter(Object o, Writer writer) throws IOException {
toJsonWriter(o, writer, null);
public void toJson(Object o, Writer writer) throws IOException {
toJson(o, writer, null);
}
public void toJsonWriter(Object o, Writer writer, JsonWriteOptions options) throws IOException {
public void toJson(Object o, Writer writer, JsonWriteOptions options) throws IOException {
JsonGenerator generator = createGenerator(writer);
toJsonInternal(o, generator, options);
generator.close();
}
public String toJsonString(Object o) throws IOException {
public String toJson(Object o) throws IOException {
return toJsonString(o, null);
}
public String toJsonString(Object o, JsonWriteOptions options) throws IOException {
public String toJson(Object o, JsonWriteOptions options) throws IOException {
return toJsonString(o, options);
}
private String toJsonString(Object o, JsonWriteOptions options) throws IOException {
StringWriter writer = new StringWriter(500);
JsonGenerator gen = createGenerator(writer);
toJsonInternal(o, gen, options);
@@ -173,7 +156,7 @@ public class DJsonContext implements JsonContext {
} else if (o instanceof Number) {
gen.writeNumber(((Number) o).doubleValue());
} else if (o instanceof Boolean) {
gen.writeBoolean(((Boolean) o).booleanValue());
gen.writeBoolean((Boolean) o);
} else if (o instanceof String) {
gen.writeString((String) o);
@@ -186,7 +169,7 @@ public class DJsonContext implements JsonContext {
toJsonFromCollection((Collection<?>) o, null, gen, options);
} else if (o instanceof EntityBean) {
BeanDescriptor<?> d = getDecriptor(o.getClass());
BeanDescriptor<?> d = getDescriptor(o.getClass());
WriteJson writeJson = createWriteJson(gen, options);
d.jsonWrite(writeJson, (EntityBean)o, null);
}
@@ -197,7 +180,7 @@ public class DJsonContext implements JsonContext {
return new WriteJson(server, gen, pathProps);
}
private <T> void toJsonFromCollection(Collection<T> c, String key, JsonGenerator gen, JsonWriteOptions options) throws IOException {
private <T> void toJsonFromCollection(Collection<T> collection, String key, JsonGenerator gen, JsonWriteOptions options) throws IOException {
if (key != null) {
gen.writeFieldName(key);
@@ -206,11 +189,9 @@ public class DJsonContext implements JsonContext {
WriteJson writeJson = createWriteJson(gen, options);
Iterator<T> it = c.iterator();
while (it.hasNext()) {
T t = it.next();
BeanDescriptor<?> d = getDecriptor(t.getClass());
d.jsonWrite(writeJson, (EntityBean)t, null);
for (T bean : collection) {
BeanDescriptor<?> d = getDescriptor(bean.getClass());
d.jsonWrite(writeJson, (EntityBean) bean, null);
}
gen.writeEndArray();
}
@@ -234,7 +215,7 @@ public class DJsonContext implements JsonContext {
toJsonFromCollection((Collection<?>) value, key, gen, options);
} else if (value instanceof EntityBean) {
BeanDescriptor<?> d = getDecriptor(value.getClass());
BeanDescriptor<?> d = getDescriptor(value.getClass());
d.jsonWrite(writeJson,(EntityBean) value, key);
} else {
@@ -245,7 +226,7 @@ public class DJsonContext implements JsonContext {
gen.writeEndObject();
}
private <T> BeanDescriptor<T> getDecriptor(Class<T> cls) {
private <T> BeanDescriptor<T> getDescriptor(Class<T> cls) {
BeanDescriptor<T> d = server.getBeanDescriptor(cls);
if (d == null) {
throw new RuntimeException("No BeanDescriptor found for " + cls);
@@ -1,10 +1,5 @@
package com.avaje.ebeaninternal.server.text.json;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
@@ -13,6 +8,10 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.util.ArrayStack;
import com.fasterxml.jackson.core.JsonGenerator;
import java.io.IOException;
import java.util.Collection;
import java.util.Set;
public class WriteJson {
private final SpiEbeanServer server;
@@ -36,11 +35,7 @@ public class WriteJson {
}
public boolean isParentBean(Object bean) {
if (parentBeans.isEmpty()) {
return false;
} else {
return parentBeans.contains(bean);
}
return !parentBeans.isEmpty() && parentBeans.contains(bean);
}
public void pushParentBeanMany(Object parentBean) {
@@ -61,15 +56,6 @@ public class WriteJson {
pathStack.pop();
}
public Set<String> getIncludeProperties() {
if (pathProperties == null) {
return null;
} else {
return pathProperties.get(pathStack.peekWithNull());
}
}
public WriteBean createWriteBean(BeanDescriptor<?> desc, EntityBean bean) {
if (pathProperties == null) {
@@ -135,7 +121,6 @@ public class WriteJson {
// render all the properties and invoke lazy loading if required
BeanProperty[] props = desc.propertiesNonTransient();
for (int j = 0; j < props.length; j++) {
System.out.println("bean "+ currentBean+" prop:"+props[j]);
if (isIncludeProperty(props[j])) {
props[j].jsonWrite(writeJson, currentBean);
}
@@ -163,20 +148,17 @@ public class WriteJson {
beginAssocMany(name);
Iterator<?> it = c.iterator();
while (it.hasNext()) {
EntityBean o = (EntityBean) it.next();
BeanDescriptor<?> d = getDecriptor(o.getClass());
d.jsonWrite(this, o, null);
for (Object bean : c) {
BeanDescriptor<?> d = getDescriptor(bean.getClass());
d.jsonWrite(this, (EntityBean)bean, null);
}
endAssocMany();
}
private <T> BeanDescriptor<T> getDecriptor(Class<T> cls) {
private <T> BeanDescriptor<T> getDescriptor(Class<T> cls) {
BeanDescriptor<T> d = server.getBeanDescriptor(cls);
if (d == null) {
String msg = "No BeanDescriptor found for " + cls;
throw new RuntimeException(msg);
throw new RuntimeException("No BeanDescriptor found for " + cls);
}
return d;
}
@@ -42,13 +42,13 @@ public class ScalarTypeByte extends ScalarTypeBase<Byte> {
}
@Override
public void jsonWrite(JsonGenerator ctx, String name, Object value) {
throw new TextException("Not supported");
public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException {
throw new IOException("Not supported");
}
@Override
public Object jsonRead(JsonParser ctx, JsonToken event) {
throw new TextException("Not supported");
public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException {
throw new IOException("Not supported");
}
public String formatValue(Byte t) {