mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
This commit is contained in:
@@ -15,6 +15,8 @@ public class JsonReadOptions {
|
||||
|
||||
protected Map<String, JsonReadBeanVisitor<?>> visitorMap;
|
||||
|
||||
protected Object objectMapper;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
@@ -44,4 +46,17 @@ public class JsonReadOptions {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the ServerConfig).
|
||||
*/
|
||||
public Object getObjectMapper() {
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the ServerConfig).
|
||||
*/
|
||||
public void setObjectMapper(Object objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,21 @@ public class ClassUtil {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if Jackson annotations like JsonIgnore are present.
|
||||
*/
|
||||
public static boolean isJacksonAnnotationsPresent() {
|
||||
return isPresent("com.fasterxml.jackson.annotation.JsonIgnore", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if Jackson ObjectMapper is present.
|
||||
*/
|
||||
public static boolean isJacksonObjectMapperPresent() {
|
||||
return isPresent("com.fasterxml.jackson.databind.ObjectMapper", null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the given class is present.
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,8 @@ import com.avaje.ebeaninternal.server.text.json.WriteJson;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.DataInput;
|
||||
@@ -39,6 +41,8 @@ import java.util.Map;
|
||||
*/
|
||||
public class BeanProperty implements ElPropertyValue {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
|
||||
|
||||
/**
|
||||
* Flag to mark this at part of the unique id.
|
||||
*/
|
||||
@@ -299,8 +303,8 @@ public class BeanProperty implements ElPropertyValue {
|
||||
this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null);
|
||||
this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn);
|
||||
|
||||
this.jsonSerialize = deploy.isExposeSerialize();
|
||||
this.jsonDeserialize = deploy.isExposeDeserialize();
|
||||
this.jsonSerialize = deploy.isJsonSerialize();
|
||||
this.jsonDeserialize = deploy.isJsonDeserialize();
|
||||
}
|
||||
|
||||
private String tableAliasIntern(BeanDescriptor<?> descriptor, String s, boolean dbEncrypted, String dbColumn) {
|
||||
@@ -1072,22 +1076,40 @@ public class BeanProperty implements ElPropertyValue {
|
||||
if (value == null) {
|
||||
writeJson.writeNull(name);
|
||||
} else {
|
||||
scalarType.jsonWrite(writeJson.gen(), name, value);
|
||||
if (scalarType != null) {
|
||||
scalarType.jsonWrite(writeJson.gen(), name, value);
|
||||
} else {
|
||||
writeJson.writeValueUsingObjectMapper(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void jsonRead(ReadJson ctx, EntityBean bean) throws IOException {
|
||||
if (!jsonDeserialize) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonToken event = ctx.nextToken();
|
||||
if (JsonToken.VALUE_NULL == event) {
|
||||
setValue(bean, null);
|
||||
if (jsonDeserialize) {
|
||||
setValue(bean, null);
|
||||
}
|
||||
} else {
|
||||
// expect to read non-null json value
|
||||
Object objValue = scalarType.jsonRead(ctx.getParser(), event);
|
||||
setValue(bean, objValue);
|
||||
Object objValue;
|
||||
if (scalarType != null) {
|
||||
objValue = scalarType.jsonRead(ctx.getParser(), event);
|
||||
} else {
|
||||
try {
|
||||
objValue = ctx.readValueUsingObjectMapper(propertyType);
|
||||
} catch (IOException e) {
|
||||
// change in behavior for #318
|
||||
objValue = null;
|
||||
String msg = "Error trying to use Jackson ObjectMapper to read transient property "
|
||||
+ getFullBeanName() +" - consider marking this property with @JsonIgnore";
|
||||
logger.error(msg, e);
|
||||
}
|
||||
}
|
||||
if (jsonDeserialize) {
|
||||
setValue(bean, objValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -873,7 +873,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
if (help != null) {
|
||||
help.jsonWrite(ctx, name, value, include != null);
|
||||
} else {
|
||||
ctx.toJson(name, (Collection<?>) value);
|
||||
if (isTransient) {
|
||||
ctx.writeValueUsingObjectMapper(name, value);
|
||||
} else {
|
||||
ctx.toJson(name, (Collection<?>) value);
|
||||
}
|
||||
}
|
||||
ctx.popParentBeanMany();
|
||||
}
|
||||
|
||||
+37
-2
@@ -1,23 +1,42 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebeaninternal.server.text.json.ReadJson;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Help BeanPropertyAssocMany with JSON processing.
|
||||
*/
|
||||
public class BeanPropertyAssocManyJsonHelp {
|
||||
|
||||
/**
|
||||
* The associated many property.
|
||||
*/
|
||||
private final BeanPropertyAssocMany<?> many;
|
||||
|
||||
/**
|
||||
* Helper used to read json for transient 'many' properties.
|
||||
*/
|
||||
private final BeanPropertyAssocManyJsonTransient jsonTransient;
|
||||
|
||||
/**
|
||||
* Construct for the owning many property.
|
||||
*/
|
||||
public BeanPropertyAssocManyJsonHelp(BeanPropertyAssocMany<?> many) {
|
||||
this.many = many;
|
||||
this.jsonTransient = !ClassUtil.isJacksonObjectMapperPresent() ? null : new BeanPropertyAssocManyJsonTransient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the JSON for this property.
|
||||
*/
|
||||
public void jsonRead(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
if (!this.many.jsonDeserialize) {
|
||||
@@ -33,6 +52,11 @@ public class BeanPropertyAssocManyJsonHelp {
|
||||
throw new JsonParseException("Unexpected token " + event + " - expecting start_array ", parser.getCurrentLocation());
|
||||
}
|
||||
|
||||
if (many.isTransient()) {
|
||||
jsonReadTransientUsingObjectMapper(readJson, parentBean);
|
||||
return;
|
||||
}
|
||||
|
||||
BeanCollection<?> collection = many.createEmpty(parentBean);
|
||||
BeanCollectionAdd add = many.getBeanCollectionAdd(collection, null);
|
||||
do {
|
||||
@@ -51,4 +75,15 @@ public class BeanPropertyAssocManyJsonHelp {
|
||||
|
||||
many.setValue(parentBean, collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a Transient property using Jackson ObjectMapper.
|
||||
*/
|
||||
private void jsonReadTransientUsingObjectMapper(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
if (jsonTransient == null) {
|
||||
throw new IllegalStateException("Jackson ObjectMapper is required to read this Transient property "+many.getFullBeanName());
|
||||
}
|
||||
jsonTransient.jsonReadUsingObjectMapper(many, readJson, parentBean);
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.text.json.ReadJson;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||
import com.fasterxml.jackson.databind.type.MapType;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Helper used to read transient many properties using Jackson ObjectMapper.
|
||||
*/
|
||||
public class BeanPropertyAssocManyJsonTransient {
|
||||
|
||||
/**
|
||||
* Use Jackson ObjectMapper to read the transient 'many' property.
|
||||
*/
|
||||
public void jsonReadUsingObjectMapper(BeanPropertyAssocMany<?> many, ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
ObjectMapper mapper = readJson.getObjectMapper();
|
||||
|
||||
ManyType manyType = many.getManyType();
|
||||
|
||||
Object value;
|
||||
if (manyType.isMap()) {
|
||||
// read map using Jackson object mapper with unknown key type
|
||||
TypeFactory typeFactory = mapper.getTypeFactory();
|
||||
JavaType target = typeFactory.constructType(many.getTargetType());
|
||||
MapType jacksonType = typeFactory.constructMapType(LinkedHashMap.class, typeFactory.unknownType(), target);
|
||||
value = mapper.readValue(readJson.getParser(), jacksonType);
|
||||
|
||||
} else {
|
||||
// read list or set using Jackson object mapper
|
||||
CollectionType jacksonType = mapper.getTypeFactory().constructCollectionType(manyType.getCollectionType(), many.getTargetType());
|
||||
value = mapper.readValue(readJson.getParser(), jacksonType);
|
||||
}
|
||||
|
||||
many.setValue(parentBean, value);
|
||||
}
|
||||
}
|
||||
@@ -2,58 +2,76 @@ package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents the type of a OneToMany or ManyToMany property.
|
||||
*/
|
||||
public class ManyType {
|
||||
|
||||
public static final ManyType JAVA_LIST = new ManyType(Underlying.LIST);
|
||||
public static final ManyType JAVA_SET = new ManyType(Underlying.SET);
|
||||
public static final ManyType JAVA_MAP = new ManyType(Underlying.MAP);
|
||||
|
||||
public enum Underlying {
|
||||
LIST,
|
||||
SET,
|
||||
MAP
|
||||
}
|
||||
|
||||
private final SpiQuery.Type queryType;
|
||||
|
||||
private final Underlying underlying;
|
||||
|
||||
public static final ManyType JAVA_LIST = new ManyType(Underlying.LIST);
|
||||
public static final ManyType JAVA_SET = new ManyType(Underlying.SET);
|
||||
public static final ManyType JAVA_MAP = new ManyType(Underlying.MAP);
|
||||
|
||||
public ManyType(Underlying underlying) {
|
||||
this.underlying = underlying;
|
||||
switch (underlying) {
|
||||
case LIST:
|
||||
queryType = SpiQuery.Type.LIST;
|
||||
break;
|
||||
case SET:
|
||||
queryType = SpiQuery.Type.SET;
|
||||
break;
|
||||
public enum Underlying {
|
||||
|
||||
default:
|
||||
queryType = SpiQuery.Type.MAP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LIST(List.class),
|
||||
SET(Set.class),
|
||||
MAP(null);
|
||||
|
||||
public boolean isMap() {
|
||||
return Underlying.MAP.equals(underlying);
|
||||
Class<? extends Collection> type;
|
||||
|
||||
Underlying(Class<? extends Collection> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the matching Query type.
|
||||
*/
|
||||
public SpiQuery.Type getQueryType() {
|
||||
return queryType;
|
||||
}
|
||||
|
||||
private final SpiQuery.Type queryType;
|
||||
|
||||
private final Underlying underlying;
|
||||
|
||||
|
||||
public ManyType(Underlying underlying) {
|
||||
this.underlying = underlying;
|
||||
switch (underlying) {
|
||||
case LIST:
|
||||
queryType = SpiQuery.Type.LIST;
|
||||
break;
|
||||
case SET:
|
||||
queryType = SpiQuery.Type.SET;
|
||||
break;
|
||||
|
||||
default:
|
||||
queryType = SpiQuery.Type.MAP;
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying type.
|
||||
*/
|
||||
public Underlying getUnderlying() {
|
||||
return underlying;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isMap() {
|
||||
return Underlying.MAP.equals(underlying);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the matching Query type.
|
||||
*/
|
||||
public SpiQuery.Type getQueryType() {
|
||||
return queryType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying type.
|
||||
*/
|
||||
public Underlying getUnderlying() {
|
||||
return underlying;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns List.class or Set.class and null for Map.
|
||||
* Not intended to be called for maps.
|
||||
*/
|
||||
public Class<? extends Collection> getCollectionType() {
|
||||
return underlying.type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ public class DeployBeanProperty {
|
||||
|
||||
private boolean localEncrypted;
|
||||
|
||||
private boolean exposeSerialize = true;
|
||||
private boolean exposeDeserialize = true;
|
||||
private boolean jsonSerialize = true;
|
||||
private boolean jsonDeserialize = true;
|
||||
|
||||
private boolean dbEncrypted;
|
||||
private DbEncryptFunction dbEncryptFunction;
|
||||
@@ -292,20 +292,20 @@ public class DeployBeanProperty {
|
||||
return dbLength;
|
||||
}
|
||||
|
||||
public boolean isExposeSerialize() {
|
||||
return exposeSerialize;
|
||||
public boolean isJsonSerialize() {
|
||||
return jsonSerialize;
|
||||
}
|
||||
|
||||
public void setExposeSerialize(boolean exposeSerialize) {
|
||||
this.exposeSerialize = exposeSerialize;
|
||||
public void setJsonSerialize(boolean jsonSerialize) {
|
||||
this.jsonSerialize = jsonSerialize;
|
||||
}
|
||||
|
||||
public boolean isExposeDeserialize() {
|
||||
return exposeDeserialize;
|
||||
public boolean isJsonDeserialize() {
|
||||
return jsonDeserialize;
|
||||
}
|
||||
|
||||
public void setExposeDeserialize(boolean exposeDeserialize) {
|
||||
this.exposeDeserialize = exposeDeserialize;
|
||||
public void setJsonDeserialize(boolean jsonDeserialize) {
|
||||
this.jsonDeserialize = jsonDeserialize;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import com.avaje.ebean.annotation.*;
|
||||
import com.avaje.ebean.annotation.ColumnHstore;
|
||||
import com.avaje.ebean.annotation.CreatedTimestamp;
|
||||
import com.avaje.ebean.annotation.DbHstore;
|
||||
import com.avaje.ebean.annotation.DbJson;
|
||||
import com.avaje.ebean.annotation.DbJsonB;
|
||||
import com.avaje.ebean.annotation.EmbeddedColumns;
|
||||
import com.avaje.ebean.annotation.Encrypted;
|
||||
import com.avaje.ebean.annotation.Expose;
|
||||
import com.avaje.ebean.annotation.Formula;
|
||||
import com.avaje.ebean.annotation.Index;
|
||||
import com.avaje.ebean.annotation.JsonIgnore;
|
||||
import com.avaje.ebean.annotation.UpdatedTimestamp;
|
||||
import com.avaje.ebean.config.EncryptDeploy;
|
||||
import com.avaje.ebean.config.EncryptDeploy.Mode;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncrypt;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
|
||||
import com.avaje.ebean.config.dbplatform.IdType;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
@@ -13,7 +25,12 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.type.*;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundType;
|
||||
import com.avaje.ebeaninternal.server.type.DataEncryptSupport;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeBytesBase;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeBytesEncrypted;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeEncryptedWrapper;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
@@ -27,6 +44,11 @@ import java.util.UUID;
|
||||
*/
|
||||
public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
/**
|
||||
* If present read Jackson JsonIgnore.
|
||||
*/
|
||||
private boolean jacksonAnnotationsPresent = ClassUtil.isJacksonAnnotationsPresent();
|
||||
|
||||
/**
|
||||
* By default we lazy load Lob properties.
|
||||
*/
|
||||
@@ -101,10 +123,25 @@ public class AnnotationFields extends AnnotationParser {
|
||||
if (column != null) {
|
||||
readColumn(column, prop);
|
||||
}
|
||||
|
||||
if (jacksonAnnotationsPresent) {
|
||||
com.fasterxml.jackson.annotation.JsonIgnore jsonIgnore = get(prop, com.fasterxml.jackson.annotation.JsonIgnore.class);
|
||||
if (jsonIgnore != null) {
|
||||
prop.setJsonSerialize(!jsonIgnore.value());
|
||||
prop.setJsonDeserialize(!jsonIgnore.value());
|
||||
}
|
||||
}
|
||||
|
||||
Expose expose = get(prop, Expose.class);
|
||||
if (expose != null) {
|
||||
prop.setExposeSerialize(expose.serialize());
|
||||
prop.setExposeDeserialize(expose.deserialize());
|
||||
prop.setJsonSerialize(expose.serialize());
|
||||
prop.setJsonDeserialize(expose.deserialize());
|
||||
}
|
||||
|
||||
JsonIgnore jsonIgnore = get(prop, JsonIgnore.class);
|
||||
if (jsonIgnore != null) {
|
||||
prop.setJsonSerialize(jsonIgnore.serialize());
|
||||
prop.setJsonDeserialize(jsonIgnore.deserialize());
|
||||
}
|
||||
|
||||
if (prop.getDbColumn() == null) {
|
||||
|
||||
+2
-1
@@ -259,7 +259,8 @@ public class DeployCreateProperties {
|
||||
}
|
||||
|
||||
if (isTransientField(field)) {
|
||||
return null;
|
||||
// return with no ScalarType (still support JSON features)
|
||||
return new DeployBeanProperty(desc, propertyType, null, null);
|
||||
}
|
||||
try {
|
||||
CheckImmutableResponse checkImmutable = typeManager.checkImmutable(propertyType);
|
||||
|
||||
@@ -173,19 +173,27 @@ public class DeployUtil {
|
||||
// Note that Temporal types already have dbType
|
||||
// set via annotations
|
||||
Class<?> propType = property.getPropertyType();
|
||||
ScalarType<?> scalarType = typeManager.getScalarType(propType, property.getDbType());
|
||||
if (scalarType != null) {
|
||||
return scalarType;
|
||||
}
|
||||
try {
|
||||
ScalarType<?> scalarType = typeManager.getScalarType(propType, property.getDbType());
|
||||
if (scalarType != null) {
|
||||
return scalarType;
|
||||
}
|
||||
|
||||
String msg = property.getFullBeanName() + " has no ScalarType - type[" + propType.getName() + "]";
|
||||
if (!property.isTransient()) {
|
||||
throw new PersistenceException(msg);
|
||||
String msg = property.getFullBeanName() + " has no ScalarType - type[" + propType.getName() + "]";
|
||||
if (!property.isTransient()) {
|
||||
throw new PersistenceException(msg);
|
||||
|
||||
} else {
|
||||
// this is ok...
|
||||
logger.trace("... transient property " + msg);
|
||||
return null;
|
||||
} else {
|
||||
// this is ok...
|
||||
logger.trace("... transient property " + msg);
|
||||
return null;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (property.isTransient()) {
|
||||
// expected for transient properties with unknown/non-mapped types
|
||||
return null;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,12 @@ public class DJsonContext implements JsonContext {
|
||||
|
||||
private final JsonFactory jsonFactory;
|
||||
|
||||
private final Object defaultObjectMapper;
|
||||
|
||||
public DJsonContext(SpiEbeanServer server, JsonFactory jsonFactory) {
|
||||
this.server = server;
|
||||
this.jsonFactory = (jsonFactory != null) ? jsonFactory : new JsonFactory();
|
||||
this.defaultObjectMapper = this.server.getServerConfig().getObjectMapper();
|
||||
}
|
||||
|
||||
public boolean isSupportedType(Type genericType) {
|
||||
@@ -72,7 +75,7 @@ public class DJsonContext implements JsonContext {
|
||||
|
||||
public <T> T toBean(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException {
|
||||
|
||||
ReadJson readJson = new ReadJson(parser, options);
|
||||
ReadJson readJson = new ReadJson(parser, options, determineObjectMapper(options));
|
||||
try {
|
||||
BeanDescriptor<T> d = getDescriptor(cls);
|
||||
return d.jsonRead(readJson, null);
|
||||
@@ -104,7 +107,7 @@ public class DJsonContext implements JsonContext {
|
||||
|
||||
public <T> List<T> toList(Class<T> cls, JsonParser src, JsonReadOptions options) throws JsonIOException {
|
||||
|
||||
ReadJson readJson = new ReadJson(src, options);
|
||||
ReadJson readJson = new ReadJson(src, options, determineObjectMapper(options));
|
||||
try {
|
||||
BeanDescriptor<T> d = getDescriptor(cls);
|
||||
|
||||
@@ -271,7 +274,7 @@ public class DJsonContext implements JsonContext {
|
||||
|
||||
private WriteJson createWriteJson(JsonGenerator gen, JsonWriteOptions options) {
|
||||
PathProperties pathProps = (options == null) ? null : options.getPathProperties();
|
||||
return new WriteJson(server, gen, pathProps);
|
||||
return new WriteJson(server, gen, pathProps, determineObjectMapper(options));
|
||||
}
|
||||
|
||||
private <T> void toJsonFromCollection(Collection<T> collection, String key, JsonGenerator gen, JsonWriteOptions options) throws IOException {
|
||||
@@ -320,11 +323,36 @@ public class DJsonContext implements JsonContext {
|
||||
gen.writeEndObject();
|
||||
}
|
||||
|
||||
private <T> BeanDescriptor<T> getDescriptor(Class<T> cls) {
|
||||
BeanDescriptor<T> d = server.getBeanDescriptor(cls);
|
||||
/**
|
||||
* Return the BeanDescriptor for the given bean type.
|
||||
*/
|
||||
private <T> BeanDescriptor<T> getDescriptor(Class<T> beanType) {
|
||||
BeanDescriptor<T> d = server.getBeanDescriptor(beanType);
|
||||
if (d == null) {
|
||||
throw new RuntimeException("No BeanDescriptor found for " + cls);
|
||||
throw new RuntimeException("No BeanDescriptor found for " + beanType);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the object mapper to use for a JSON read request.
|
||||
*/
|
||||
private Object determineObjectMapper(JsonReadOptions options) {
|
||||
if (options == null) {
|
||||
return defaultObjectMapper;
|
||||
}
|
||||
Object mapper = options.getObjectMapper();
|
||||
return (mapper != null) ? mapper : defaultObjectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the object mapper to use for a JSON write request.
|
||||
*/
|
||||
private Object determineObjectMapper(JsonWriteOptions options) {
|
||||
if (options == null) {
|
||||
return defaultObjectMapper;
|
||||
}
|
||||
Object mapper = options.getObjectMapper();
|
||||
return (mapper != null) ? mapper : defaultObjectMapper;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.avaje.ebean.text.json.JsonReadBeanVisitor;
|
||||
import com.avaje.ebean.text.json.JsonReadOptions;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
@@ -28,17 +29,33 @@ public class ReadJson {
|
||||
*/
|
||||
final Map<String, JsonReadBeanVisitor<?>> visitorMap;
|
||||
|
||||
final Object objectMapper;
|
||||
|
||||
/**
|
||||
* Construct with parser and readOptions.
|
||||
*/
|
||||
public ReadJson(JsonParser parser, JsonReadOptions readOptions) {
|
||||
public ReadJson(JsonParser parser, JsonReadOptions readOptions, Object objectMapper) {
|
||||
|
||||
this.parser = parser;
|
||||
this.objectMapper = objectMapper;
|
||||
|
||||
// only create visitorMap, pathStack if needed ...
|
||||
this.visitorMap = (readOptions == null) ? null : readOptions.getVisitorMap();
|
||||
this.pathStack = (visitorMap == null) ? null : new PathStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the objectMapper used for this request.
|
||||
*/
|
||||
public ObjectMapper getObjectMapper() {
|
||||
if (objectMapper == null) {
|
||||
throw new IllegalStateException(
|
||||
"Jackson ObjectMapper required but has not set. The ObjectMapper can be set on"
|
||||
+" either the ServerConfig or on JsonReadOptions.");
|
||||
}
|
||||
return (ObjectMapper)objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JsonParser.
|
||||
*/
|
||||
@@ -84,4 +101,13 @@ public class ReadJson {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the property value using Jackson ObjectMapper.
|
||||
* <p/>
|
||||
* Typically this is used to read Transient properties where the type is unknown to Ebean.
|
||||
*/
|
||||
public Object readValueUsingObjectMapper(Class<?> propertyType) throws IOException {
|
||||
return getObjectMapper().readValue(parser, propertyType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.util.ArrayStack;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
@@ -24,10 +25,13 @@ public class WriteJson {
|
||||
|
||||
private final ArrayStack<Object> parentBeans = new ArrayStack<Object>();
|
||||
|
||||
public WriteJson(SpiEbeanServer server, JsonGenerator generator, PathProperties pathProperties){
|
||||
private final Object objectMapper;
|
||||
|
||||
public WriteJson(SpiEbeanServer server, JsonGenerator generator, PathProperties pathProperties, Object objectMapper){
|
||||
this.server = server;
|
||||
this.generator = generator;
|
||||
this.pathProperties = pathProperties;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
public JsonGenerator gen() {
|
||||
@@ -73,6 +77,20 @@ public class WriteJson {
|
||||
return new WriteBean(desc, explicitAllProps, currentIncludeProps, bean);
|
||||
}
|
||||
|
||||
public void writeValueUsingObjectMapper(String name, Object value) throws IOException {
|
||||
generator.writeFieldName(name);
|
||||
objectMapper().writeValue(generator, value);
|
||||
}
|
||||
|
||||
private ObjectMapper objectMapper() {
|
||||
if (objectMapper == null) {
|
||||
throw new IllegalStateException(
|
||||
"Jackson ObjectMapper required but not set. Expected to be set on either"
|
||||
+" serverConfig");
|
||||
}
|
||||
return (ObjectMapper)objectMapper;
|
||||
}
|
||||
|
||||
public static class WriteBean {
|
||||
|
||||
final boolean explicitAllProps;
|
||||
|
||||
@@ -159,7 +159,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
this.typeMap = new ConcurrentHashMap<Class<?>, ScalarType<?>>();
|
||||
this.nativeMap = new ConcurrentHashMap<Integer, ScalarType<?>>();
|
||||
|
||||
this.objectMapperPresent = ClassUtil.isPresent("com.fasterxml.jackson.databind.ObjectMapper", this.getClass());
|
||||
this.objectMapperPresent = ClassUtil.isJacksonObjectMapperPresent();
|
||||
|
||||
this.extraTypeFactory = new DefaultTypeFactory(config);
|
||||
|
||||
@@ -384,7 +384,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
return (ScalarType<T>) extraTypeFactory.createCalendar(jsonDateTime, jdbcType);
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unmatched ScalarType for " + type + " jdbcType:" + jdbcType);
|
||||
throw new IllegalArgumentException("Unmatched ScalarType for " + type + " jdbcType:" + jdbcType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user