diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index cce74e938..4f535a96e 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -1159,6 +1159,9 @@ public interface EbeanServer { /** * Return the JsonContext for reading/writing JSON. + *

+ * This instance is safe to be used concurrently by multiple threads and this method is cheap to call. + *

*/ public JsonContext json(); diff --git a/src/main/java/com/avaje/ebean/text/PathProperties.java b/src/main/java/com/avaje/ebean/text/PathProperties.java index bd3b17817..7ce4bcb25 100644 --- a/src/main/java/com/avaje/ebean/text/PathProperties.java +++ b/src/main/java/com/avaje/ebean/text/PathProperties.java @@ -19,9 +19,6 @@ import com.avaje.ebean.Query; * properties and applying that to both what to fetch (ORM query) and what to * render (JAX-RS JSON / XML). *

- * - * @author rbygrave - * */ public class PathProperties { diff --git a/src/main/java/com/avaje/ebean/text/json/JsonContext.java b/src/main/java/com/avaje/ebean/text/json/JsonContext.java index b79b3f0a8..4115a2401 100644 --- a/src/main/java/com/avaje/ebean/text/json/JsonContext.java +++ b/src/main/java/com/avaje/ebean/text/json/JsonContext.java @@ -1,5 +1,6 @@ package com.avaje.ebean.text.json; +import com.avaje.ebean.text.PathProperties; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; @@ -58,37 +59,68 @@ public interface JsonContext { public Object toObject(Type genericType, String json) throws JsonIOException; /** - * Write the bean or collection in JSON format to the writer with default - * options. - * - * @param value the bean or collection of beans to write - * @param writer used to write the json output to - * @throws JsonIOException When IOException occurs - */ - public void toJson(Object value, Writer writer) throws JsonIOException; - - /** - * With additional options to specify JsonValueAdapter and - * JsonWriteBeanVisitor's. - * - * @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 - * @throws JsonIOException When IOException occurs - */ - public void toJson(Object value, Writer writer, JsonWriteOptions options) throws JsonIOException; - - /** - * Convert a bean or collection to json string using default options. + * Return the bean or collection as JSON string. * * @throws JsonIOException When IOException occurs */ public String toJson(Object value) throws JsonIOException; /** + * Write the bean or collection in JSON format to the writer. + * + * @throws JsonIOException When IOException occurs + */ + public void toJson(Object value, Writer writer) throws JsonIOException; + + /** + * Write the bean or collection to the JsonGenerator. + * + * @throws JsonIOException When IOException occurs + */ + public void toJson(Object value, JsonGenerator generator) throws JsonIOException; + + /** + * Return the bean or collection as JSON string using PathProperties. + * + * @throws JsonIOException When IOException occurs + */ + public String toJson(Object value, PathProperties pathProperties) throws JsonIOException; + + /** + * Write the bean or collection as json to the writer using the PathProperties. + */ + public void toJson(Object value, Writer writer, PathProperties pathProperties) throws JsonIOException; + + /** + * Write the bean or collection to the JsonGenerator using the PathProperties. + */ + public void toJson(Object value, JsonGenerator generator, PathProperties pathProperties) throws JsonIOException; + + /** + * Deprecated in favour of using PathProperties by itself. + * Write json to the JsonGenerator using the JsonWriteOptions. + * + * @deprecated + */ + public void toJson(Object value, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException; + + /** + * Deprecated in favour of using PathProperties by itself. + * With additional options. + * + * @throws JsonIOException When IOException occurs + * + * @deprecated + */ + public void toJson(Object value, Writer writer, JsonWriteOptions options) throws JsonIOException; + + /** + * Deprecated in favour of using PathProperties by itself. * Convert a bean or collection to json string. * * @throws JsonIOException When IOException occurs + * + * @deprecated */ public String toJson(Object value, JsonWriteOptions options) throws JsonIOException; diff --git a/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java b/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java index f1c69023a..0e1c7fb1d 100644 --- a/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java +++ b/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java @@ -3,18 +3,14 @@ package com.avaje.ebean.text.json; import com.avaje.ebean.text.PathProperties; /** + * Deprecated in favour of just using PathProperties. + * * Provides options for customising the JSON write process. *

* You can explicitly state which properties to include in the JSON output for * the root level and each path. *

- * - *
-
- * // output as a JSON string with pretty formatting
- * String s = json.toJson(list, true, writeOptions);
- * 
- * 
+ * @deprecated */ public class JsonWriteOptions { @@ -24,12 +20,20 @@ public class JsonWriteOptions { * Parse and return a PathProperties from nested string format like * (a,b,c(d,e),f(g)) where "c" is a path containing "d" and "e" and "f" is a * path containing "g" and the root path contains "a","b","c" and "f". + * + * @see com.avaje.ebean.text.PathProperties#parse(String) */ public static JsonWriteOptions parsePath(String pathProperties) { - PathProperties p = PathProperties.parse(pathProperties); + return pathProperties(PathProperties.parse(pathProperties)); + } + + /** + * Construct JsonWriteOptions with the given pathProperties. + */ + public static JsonWriteOptions pathProperties(PathProperties pathProperties) { JsonWriteOptions o = new JsonWriteOptions(); - o.setPathProperties(p); + o.setPathProperties(pathProperties); return o; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java index 9dcd241fa..84e8e92b8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java @@ -138,14 +138,52 @@ public class DJsonContext implements JsonContext { } } - public void toJson(Object o, Writer writer) throws JsonIOException { - toJson(o, writer, null); + @Override + public void toJson(Object value, JsonGenerator generator) throws JsonIOException { + // generator passed in so don't close it + toJsonNoClose(value, generator, null); } + @Override + public void toJson(Object value, JsonGenerator generator, PathProperties pathProperties) throws JsonIOException { + // generator passed in so don't close it + toJsonNoClose(value, generator, JsonWriteOptions.pathProperties(pathProperties)); + } + @Override + public void toJson(Object o, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException { + // generator passed in so don't close it + toJsonNoClose(o, generator, options); + } + + @Override + public void toJson(Object o, Writer writer) throws JsonIOException { + // close generator + toJsonWithClose(o, createGenerator(writer), null); + } + + @Override + public String toJson(Object value, PathProperties pathProperties) throws JsonIOException { + return toJson(value, JsonWriteOptions.pathProperties(pathProperties)); + } + + @Override + public void toJson(Object o, Writer writer, PathProperties pathProperties) throws JsonIOException { + // close generator + toJsonWithClose(o, createGenerator(writer), JsonWriteOptions.pathProperties(pathProperties)); + } + + @Override public void toJson(Object o, Writer writer, JsonWriteOptions options) throws JsonIOException { + // close generator + toJsonWithClose(o, createGenerator(writer), options); + } + + /** + * Write to the JsonGenerator and close when complete. + */ + private void toJsonWithClose(Object o, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException { try { - JsonGenerator generator = createGenerator(writer); toJsonInternal(o, generator, options); generator.close(); } catch (IOException e) { @@ -153,19 +191,32 @@ public class DJsonContext implements JsonContext { } } + /** + * Write to the JsonGenerator and without closing it (as it was created externally). + */ + private void toJsonNoClose(Object o, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException { + try { + toJsonInternal(o, generator, options); + } catch (IOException e) { + throw new JsonIOException(e); + } + } + + @Override public String toJson(Object o) throws JsonIOException { return toJsonString(o, null); } + @Override public String toJson(Object o, JsonWriteOptions options) throws JsonIOException { return toJsonString(o, options); } - private String toJsonString(Object o, JsonWriteOptions options) throws JsonIOException { + private String toJsonString(Object value, JsonWriteOptions options) throws JsonIOException { try { StringWriter writer = new StringWriter(500); JsonGenerator gen = createGenerator(writer); - toJsonInternal(o, gen, options); + toJsonInternal(value, gen, options); gen.close(); return writer.toString(); } catch (IOException e) { @@ -174,29 +225,29 @@ public class DJsonContext implements JsonContext { } @SuppressWarnings("unchecked") - private void toJsonInternal(Object o, JsonGenerator gen, JsonWriteOptions options) throws IOException { + private void toJsonInternal(Object value, JsonGenerator gen, JsonWriteOptions options) throws IOException { - if (o == null) { + if (value == null) { gen.writeNull(); - } else if (o instanceof Number) { - gen.writeNumber(((Number) o).doubleValue()); - } else if (o instanceof Boolean) { - gen.writeBoolean((Boolean) o); - } else if (o instanceof String) { - gen.writeString((String) o); + } else if (value instanceof Number) { + gen.writeNumber(((Number) value).doubleValue()); + } else if (value instanceof Boolean) { + gen.writeBoolean((Boolean) value); + } else if (value instanceof String) { + gen.writeString((String) value); // } else if (o instanceof JsonElement) { - } else if (o instanceof Map) { - toJsonFromMap((Map) o, gen, options); + } else if (value instanceof Map) { + toJsonFromMap((Map) value, gen, options); - } else if (o instanceof Collection) { - toJsonFromCollection((Collection) o, null, gen, options); + } else if (value instanceof Collection) { + toJsonFromCollection((Collection) value, null, gen, options); - } else if (o instanceof EntityBean) { - BeanDescriptor d = getDescriptor(o.getClass()); + } else if (value instanceof EntityBean) { + BeanDescriptor d = getDescriptor(value.getClass()); WriteJson writeJson = createWriteJson(gen, options); - d.jsonWrite(writeJson, (EntityBean) o, null); + d.jsonWrite(writeJson, (EntityBean) value, null); } } diff --git a/src/test/java/com/avaje/ebean/text/json/JsonContextTest.java b/src/test/java/com/avaje/ebean/text/json/JsonContextTest.java new file mode 100644 index 000000000..d8ef8c871 --- /dev/null +++ b/src/test/java/com/avaje/ebean/text/json/JsonContextTest.java @@ -0,0 +1,101 @@ +package com.avaje.ebean.text.json; + +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.text.PathProperties; +import com.avaje.tests.model.basic.Customer; +import com.fasterxml.jackson.core.JsonGenerator; +import org.junit.Test; + +import java.io.StringReader; +import java.io.StringWriter; + +import static org.junit.Assert.*; + +public class JsonContextTest { + + @Test + public void testIsSupportedType() throws Exception { + + EbeanServer server = Ebean.getServer(null); + + JsonContext json = server.json(); + assertTrue(json.isSupportedType(Customer.class)); + assertFalse(json.isSupportedType(System.class)); + } + + @Test + public void test_toObject() throws Exception { + + EbeanServer server = Ebean.getServer(null); + + JsonContext json = server.json(); + + Customer customer = new Customer(); + customer.setId(1); + customer.setName("Jim"); + + String asJson = json.toJson(customer); + + Object bean = json.toObject(Customer.class, asJson); + + assertTrue(bean instanceof Customer); + assertEquals(Integer.valueOf(1), ((Customer) bean).getId()); + assertEquals("Jim", ((Customer) bean).getName()); + + StringReader reader = new StringReader(asJson); + bean = json.toObject(Customer.class, reader); + assertTrue(bean instanceof Customer); + assertEquals(Integer.valueOf(1), ((Customer) bean).getId()); + assertEquals("Jim", ((Customer) bean).getName()); + } + + @Test + public void testCreateGenerator() throws Exception { + + EbeanServer server = Ebean.getServer(null); + + StringWriter writer = new StringWriter(); + JsonContext json = server.json(); + JsonGenerator generator = json.createGenerator(writer); + + Customer customer = new Customer(); + customer.setId(1); + customer.setName("Jim"); + + + // we can use the generator before and after our json.toJson() call + // ... confirming we are not closing the generator + generator.writeStartArray(); + json.toJson(customer, generator, PathProperties.parse("id,name")); + generator.writeEndArray(); + generator.close(); + + String jsonString = writer.toString(); + assertTrue(jsonString, jsonString.startsWith("[")); + assertTrue(jsonString, jsonString.endsWith("]")); + assertTrue(jsonString, jsonString.contains("{\"id\":1,\"name\":\"Jim\"}")); + } + + @Test + public void testCreateGenerator_writeRaw() throws Exception { + + EbeanServer server = Ebean.getServer(null); + + StringWriter writer = new StringWriter(); + JsonContext json = server.json(); + JsonGenerator generator = json.createGenerator(writer); + + // test that we can write anything via writeRaw() + generator.writeRaw("START"); + generator.writeStartArray(); + generator.writeStartObject(); + generator.writeNumberField("count", 12); + generator.writeEndObject(); + generator.writeEndArray(); + generator.writeRaw("END"); + generator.close(); + + assertEquals("START[{\"count\":12}]END", writer.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/text/json/TestJsonMap.java b/src/test/java/com/avaje/tests/text/json/TestJsonMap.java index 55a5ec3c9..c96459ca6 100644 --- a/src/test/java/com/avaje/tests/text/json/TestJsonMap.java +++ b/src/test/java/com/avaje/tests/text/json/TestJsonMap.java @@ -67,15 +67,14 @@ public class TestJsonMap extends BaseTestCase { ResetBasicData.reset(); - PathProperties pathProperties = PathProperties.parse("(id,status,name,shippingAddress(id,line1,city),billingAddress(*),contacts(*))"); + PathProperties pathProperties = + PathProperties.parse("(id,status,name,shippingAddress(id,line1,city),billingAddress(*),contacts(*))"); List customers = Ebean.find(Customer.class) .apply(pathProperties) .findList(); - JsonWriteOptions options = JsonWriteOptions.parsePath("(id,status,name)"); - - String jsonString = Ebean.json().toJson(customers, options); + String jsonString = Ebean.json().toJson(customers, pathProperties); System.out.println(jsonString);