JsonContext accepts a bean as target

This commit is contained in:
Roland Praml
2022-03-28 15:38:44 +02:00
parent f95212eeb3
commit ffeb630e7e
11 changed files with 146 additions and 32 deletions
@@ -12,11 +12,18 @@ import io.ebean.bean.PersistenceContext;
*/
public interface JsonBeanReader<T> {
/**
* 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.
*/
@@ -58,12 +58,60 @@ public interface JsonContext {
*/
<T> T toBean(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException;
/**
* Read json parser input into a given Bean. <br>
* 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
*/
<T> void toBean(T target, JsonParser parser) throws JsonIOException;
/**
* Read json parser input into a given Bean additionally using JsonReadOptions.<br>
* See {@link #toBean(Class, JsonParser)} for details modified.
*
* @throws JsonIOException When IOException occurs
*/
<T> void toBean(T target, JsonParser parser, JsonReadOptions options) throws JsonIOException;
/**
* Read json reader input into a given Bean.<br>
* See {@link #toBean(Class, JsonParser)} for details
*
* @throws JsonIOException When IOException occurs
*/
<T> void toBean(T target, Reader json) throws JsonIOException;
/**
* Read json reader input into a given Bean additionally using JsonReadOptions.<br>
* See {@link #toBean(Class, JsonParser)} for details modified.
*
* @throws JsonIOException When IOException occurs
*/
<T> void toBean(T target, Reader json, JsonReadOptions options) throws JsonIOException;
/**
* Read json string input into a given Bean.<br>
* See {@link #toBean(Class, JsonParser)} for details
*
* @throws JsonIOException When IOException occurs
*/
<T> void toBean(T target, String json) throws JsonIOException;
/**
* Read json string input into a given Bean additionally using JsonReadOptions.<br>
* See {@link #toBean(Class, JsonParser)} for details
*
* @throws JsonIOException When IOException occurs
*/
<T> 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.
* <p>
* 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.
*/
<T> JsonBeanReader<T> createBeanReader(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException;
@@ -3381,12 +3381,12 @@ public class BeanDescriptor<T> implements BeanType<T>, 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<BeanProperty[]> uniqueProps() {
@@ -61,13 +61,13 @@ class BeanDescriptorElementEmbedded<T> extends BeanDescriptorElement<T> {
}
@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) {
@@ -58,13 +58,13 @@ class BeanDescriptorElementEmbeddedMap<T> 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);
@@ -65,7 +65,7 @@ final class BeanDescriptorJsonHelp<T> {
}
@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<T> {
}
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<T> {
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<T> inheritDesc = (BeanDescriptor<T>) 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);
}
@@ -1016,7 +1016,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> 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;
@@ -783,7 +783,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> 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);
}
}
@@ -36,9 +36,9 @@ public final class DJsonBeanReader<T> implements JsonBeanReader<T> {
}
@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);
}
@@ -125,7 +125,43 @@ public final class DJsonContext implements SpiJsonContext {
BeanDescriptor<T> 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 <T> void toBean(T target, String json) throws JsonIOException {
toBean(target, new StringReader(json));
}
@Override
public <T> void toBean(T target, String json, JsonReadOptions options) throws JsonIOException {
toBean(target, new StringReader(json), options);
}
@Override
public <T> void toBean(T target, Reader jsonReader) throws JsonIOException {
toBean(target, createParser(jsonReader));
}
@Override
public <T> void toBean(T target, Reader jsonReader, JsonReadOptions options) throws JsonIOException {
toBean(target, createParser(jsonReader), options);
}
@Override
public <T> void toBean(T target, JsonParser parser) throws JsonIOException {
toBean(target, parser, null);
}
@Override
public <T> void toBean(T target, JsonParser parser, JsonReadOptions options) throws JsonIOException {
BeanDescriptor<T> desc = (BeanDescriptor<T>) 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<T> 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 {
@@ -26,12 +26,9 @@ public class TestJsonBeanDescriptorParse extends BaseTestCase {
BeanDescriptor<Customer> 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<Customer> 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;
}
}