diff --git a/ebean-api/src/main/java/io/ebean/text/json/JsonBeanReader.java b/ebean-api/src/main/java/io/ebean/text/json/JsonBeanReader.java index 7acdb976f..935b78860 100644 --- a/ebean-api/src/main/java/io/ebean/text/json/JsonBeanReader.java +++ b/ebean-api/src/main/java/io/ebean/text/json/JsonBeanReader.java @@ -12,11 +12,18 @@ import io.ebean.bean.PersistenceContext; */ public interface JsonBeanReader { + /** + * Read the JSON into given bean. Will update existing properties. + */ + T read(T target); + /** * Read the JSON returning a bean. */ - T read(); - + default T read() { + return read(null); + } + /** * Create a new reader taking the context from the existing one but using a new JsonParser. */ diff --git a/ebean-api/src/main/java/io/ebean/text/json/JsonContext.java b/ebean-api/src/main/java/io/ebean/text/json/JsonContext.java index 2c7bdda00..d2c0df822 100644 --- a/ebean-api/src/main/java/io/ebean/text/json/JsonContext.java +++ b/ebean-api/src/main/java/io/ebean/text/json/JsonContext.java @@ -58,12 +58,60 @@ public interface JsonContext { */ T toBean(Class cls, JsonParser parser, JsonReadOptions options) throws JsonIOException; + /** + * Read json parser input into a given Bean.
+ * Note: This is a kind of "update". Only properties in the json will be modified. Embedded Lists and Maps will become new + * instances, so the object identity will not be preserved here. + * + * @throws JsonIOException When IOException occurs + */ + void toBean(T target, JsonParser parser) throws JsonIOException; + + /** + * Read json parser input into a given Bean additionally using JsonReadOptions.
+ * See {@link #toBean(Class, JsonParser)} for details modified. + * + * @throws JsonIOException When IOException occurs + */ + void toBean(T target, JsonParser parser, JsonReadOptions options) throws JsonIOException; + + /** + * Read json reader input into a given Bean.
+ * See {@link #toBean(Class, JsonParser)} for details + * + * @throws JsonIOException When IOException occurs + */ + void toBean(T target, Reader json) throws JsonIOException; + + /** + * Read json reader input into a given Bean additionally using JsonReadOptions.
+ * See {@link #toBean(Class, JsonParser)} for details modified. + * + * @throws JsonIOException When IOException occurs + */ + void toBean(T target, Reader json, JsonReadOptions options) throws JsonIOException; + + /** + * Read json string input into a given Bean.
+ * See {@link #toBean(Class, JsonParser)} for details + * + * @throws JsonIOException When IOException occurs + */ + void toBean(T target, String json) throws JsonIOException; + + /** + * Read json string input into a given Bean additionally using JsonReadOptions.
+ * See {@link #toBean(Class, JsonParser)} for details + * + * @throws JsonIOException When IOException occurs + */ + void toBean(T target, String json, JsonReadOptions options) throws JsonIOException; + /** * Create and return a new bean reading for the bean type given the JSON options and source. *

- * Note that JsonOption provides an option for setting a persistence context and also enabling - * further lazy loading. Further lazy loading requires a persistence context so if that is set - * on then a persistence context is created if there is not one set. + * Note that JsonOption provides an option for setting a persistence context and also enabling further lazy loading. Further lazy + * loading requires a persistence context so if that is set on then a persistence context is created if there is not one set. */ JsonBeanReader createBeanReader(Class cls, JsonParser parser, JsonReadOptions options) throws JsonIOException; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 81a326e79..d7a5f5ff5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -3381,12 +3381,12 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { jsonHelp.jsonWriteProperties(writeJson, bean); } - public T jsonRead(SpiJsonReader jsonRead, String path) throws IOException { - return jsonHelp.jsonRead(jsonRead, path, true); + public T jsonRead(SpiJsonReader jsonRead, String path, T target) throws IOException { + return jsonHelp.jsonRead(jsonRead, path, true, target); } - T jsonReadObject(SpiJsonReader jsonRead, String path) throws IOException { - return jsonHelp.jsonRead(jsonRead, path, false); + T jsonReadObject(SpiJsonReader jsonRead, String path, T target) throws IOException { + return jsonHelp.jsonRead(jsonRead, path, false, target); } public List uniqueProps() { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbedded.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbedded.java index 5c620fd73..70f6eb85c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbedded.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbedded.java @@ -61,13 +61,13 @@ class BeanDescriptorElementEmbedded extends BeanDescriptorElement { } @Override - public T jsonRead(SpiJsonReader jsonRead, String path) throws IOException { - return readJsonElement(jsonRead, path); + public T jsonRead(SpiJsonReader jsonRead, String path, T target) throws IOException { + return readJsonElement(jsonRead, path, target); } @SuppressWarnings("unchecked") - T readJsonElement(SpiJsonReader jsonRead, String path) throws IOException { - return (T)targetDescriptor.jsonRead(jsonRead, path); + T readJsonElement(SpiJsonReader jsonRead, String path, T target) throws IOException { + return (T)targetDescriptor.jsonRead(jsonRead, path, target); } void writeJsonElement(SpiJsonWriter ctx, Object element) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java index e0ae85be1..664e40da9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java @@ -58,13 +58,13 @@ class BeanDescriptorElementEmbeddedMap extends BeanDescriptorElementEmbedded< } if (stringKey) { parser.nextToken(); - Object val = readJsonElement(readJson, null); + Object val = readJsonElement(readJson, null, null); // CHECKME: Update existing map entry here? add.addKeyValue(fieldName, val); } else { parser.nextFieldName(); Object key = scalarTypeKey.jsonRead(parser); parser.nextFieldName(); - Object val = readJsonElement(readJson, null); + Object val = readJsonElement(readJson, null, null); // CHECKME: Update existing map entry here? add.addKeyValue(key, val); } } while (true); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java index 6eef2b06d..f20db7315 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java @@ -65,7 +65,7 @@ final class BeanDescriptorJsonHelp { } @SuppressWarnings("unchecked") - T jsonRead(SpiJsonReader jsonRead, String path, boolean withInheritance) throws IOException { + T jsonRead(SpiJsonReader jsonRead, String path, boolean withInheritance, T target) throws IOException { JsonParser parser = jsonRead.getParser(); //noinspection StatementWithEmptyBody if (parser.getCurrentToken() == JsonToken.START_OBJECT) { @@ -82,7 +82,7 @@ final class BeanDescriptorJsonHelp { } if (desc.inheritInfo == null || !withInheritance) { - return jsonReadObject(jsonRead, path); + return jsonReadObject(jsonRead, path, target); } ObjectNode node = jsonRead.getObjectMapper().readTree(parser); @@ -97,17 +97,25 @@ final class BeanDescriptorJsonHelp { JsonNode discNode = node.get(discColumn); if (discNode == null || discNode.isNull()) { if (!desc.isAbstractType()) { - return desc.jsonReadObject(newReader, path); + return desc.jsonReadObject(newReader, path, target); } String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?"; throw new JsonParseException(newParser, msg, parser.getCurrentLocation()); } - return (T) inheritInfo.readType(discNode.asText()).desc().jsonReadObject(newReader, path); + BeanDescriptor inheritDesc = (BeanDescriptor) inheritInfo.readType(discNode.asText()).desc(); + return inheritDesc.jsonReadObject(newReader, path, target); } - private T jsonReadObject(SpiJsonReader readJson, String path) throws IOException { - EntityBean bean = desc.createEntityBeanForJson(); + private T jsonReadObject(SpiJsonReader readJson, String path, T target) throws IOException { + EntityBean bean; + if (target == null) { + bean = desc.createEntityBeanForJson(); + } else if (desc.beanType.isInstance(target)) { + bean = (EntityBean) target; + } else { + throw new ClassCastException(target.getClass().getName() + " provided, but " + desc.beanType.getClass().getName() + " expected"); + } return jsonReadProperties(readJson, bean, path); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 09851725f..4e6f2d577 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -1016,7 +1016,8 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST BeanCollection collection = createEmpty(parentBean); BeanCollectionAdd add = beanCollectionAdd(collection); do { - EntityBean detailBean = (EntityBean) targetDescriptor.jsonRead(readJson, name); + // CHECKME: Update existing list entry here? + EntityBean detailBean = (EntityBean) targetDescriptor.jsonRead(readJson, name, null); if (detailBean == null) { // read the entire array break; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index 713c418bb..e534075f1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -783,7 +783,8 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr @Override public void jsonRead(SpiJsonReader readJson, EntityBean bean) throws IOException { if (jsonDeserialize && targetDescriptor != null) { - T assocBean = targetDescriptor.jsonRead(readJson, name); + T target = (T) value(bean); + T assocBean = targetDescriptor.jsonRead(readJson, name, target); setValue(bean, assocBean); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/text/json/DJsonBeanReader.java b/ebean-core/src/main/java/io/ebeaninternal/server/text/json/DJsonBeanReader.java index 79465e037..f16238bfa 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/text/json/DJsonBeanReader.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/text/json/DJsonBeanReader.java @@ -36,9 +36,9 @@ public final class DJsonBeanReader implements JsonBeanReader { } @Override - public T read() { + public T read(T target) { try { - return desc.jsonRead(readJson, null); + return desc.jsonRead(readJson, null, target); } catch (IOException e) { throw new PersistenceIOException(e); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/text/json/DJsonContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/text/json/DJsonContext.java index cbfb73559..83b04cb6d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/text/json/DJsonContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/text/json/DJsonContext.java @@ -125,7 +125,43 @@ public final class DJsonContext implements SpiJsonContext { BeanDescriptor desc = getDescriptor(cls); try { - return desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options)), null); + return desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options)), null, null); + } catch (IOException e) { + throw new JsonIOException(e); + } + } + + @Override + public void toBean(T target, String json) throws JsonIOException { + toBean(target, new StringReader(json)); + } + + @Override + public void toBean(T target, String json, JsonReadOptions options) throws JsonIOException { + toBean(target, new StringReader(json), options); + } + + @Override + public void toBean(T target, Reader jsonReader) throws JsonIOException { + toBean(target, createParser(jsonReader)); + } + + @Override + public void toBean(T target, Reader jsonReader, JsonReadOptions options) throws JsonIOException { + toBean(target, createParser(jsonReader), options); + } + + @Override + public void toBean(T target, JsonParser parser) throws JsonIOException { + toBean(target, parser, null); + } + + @Override + public void toBean(T target, JsonParser parser, JsonReadOptions options) throws JsonIOException { + + BeanDescriptor desc = (BeanDescriptor) getDescriptor(target.getClass()); + try { + desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options)), null, target); } catch (IOException e) { throw new JsonIOException(e); } @@ -188,7 +224,8 @@ public final class DJsonContext implements SpiJsonContext { List list = new ArrayList<>(); do { - T bean = desc.jsonRead(readJson, null); + // CHECKME: Should we update the list + T bean = desc.jsonRead(readJson, null, null); if (bean == null) { break; } else { diff --git a/ebean-test/src/test/java/org/tests/text/json/TestJsonBeanDescriptorParse.java b/ebean-test/src/test/java/org/tests/text/json/TestJsonBeanDescriptorParse.java index 6edefc417..177611724 100644 --- a/ebean-test/src/test/java/org/tests/text/json/TestJsonBeanDescriptorParse.java +++ b/ebean-test/src/test/java/org/tests/text/json/TestJsonBeanDescriptorParse.java @@ -26,12 +26,9 @@ public class TestJsonBeanDescriptorParse extends BaseTestCase { BeanDescriptor descriptor = server.descriptor(Customer.class); - StringReader reader = new StringReader("{\"id\":123,\"name\":\"Hello rob\"}"); - JsonParser parser = server.json().createParser(reader); + SpiJsonReader readJson = createRead(server, descriptor); - SpiJsonReader readJson = new ReadJson(descriptor, parser, null, null); - - Customer customer = descriptor.jsonRead(readJson, null); + Customer customer = descriptor.jsonRead(readJson, null, null); assertEquals(Integer.valueOf(123), customer.getId()); assertEquals("Hello rob", customer.getName()); @@ -42,6 +39,21 @@ public class TestJsonBeanDescriptorParse extends BaseTestCase { assertEquals(2, loadedProps.size()); assertTrue(loadedProps.contains("id")); assertTrue(loadedProps.contains("name")); + + customer.setName("Hello Roland"); + customer.setId(234); + readJson = createRead(server, descriptor); + descriptor.jsonRead(readJson, null, customer); + assertEquals(Integer.valueOf(123), customer.getId()); + assertEquals("Hello rob", customer.getName()); + } + + private SpiJsonReader createRead(SpiEbeanServer server, BeanDescriptor descriptor) { + StringReader reader = new StringReader("{\"id\":123,\"name\":\"Hello rob\"}"); + JsonParser parser = server.json().createParser(reader); + + SpiJsonReader readJson = new ReadJson(descriptor, parser, null, null); + return readJson; } }