From 54d008e5e62287111f88ac7fc792bff451048a11 Mon Sep 17 00:00:00 2001 From: reda-alaoui Date: Sat, 20 Jun 2015 02:10:50 +0200 Subject: [PATCH] Fix for #41 --- .../hibernate4/ClonedBeanSerializer.java | 164 ++++++++++++++++++ .../datatype/hibernate4/Hibernate4Module.java | 11 +- .../hibernate4/HibernateProxySerializer.java | 88 +++++++--- .../hibernate4/HibernateSerializers.java | 151 +++++++++++++++- 4 files changed, 381 insertions(+), 33 deletions(-) create mode 100644 hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/ClonedBeanSerializer.java diff --git a/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/ClonedBeanSerializer.java b/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/ClonedBeanSerializer.java new file mode 100644 index 00000000..46835e7e --- /dev/null +++ b/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/ClonedBeanSerializer.java @@ -0,0 +1,164 @@ +package com.fasterxml.jackson.datatype.hibernate4; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder; +import com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer; +import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; +import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer; +import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; +import com.fasterxml.jackson.databind.util.NameTransformer; + +import java.io.IOException; + +/** + * Clone of BeanSerializer, because we need to alter serialize method in HibernateProxySerializer + */ +public class ClonedBeanSerializer + extends BeanSerializerBase +{ + private static final long serialVersionUID = -4536893235025590367L; + + /* + /********************************************************** + /* Life-cycle: constructors + /********************************************************** + */ + + /** + * @param builder Builder object that contains collected information + * that may be needed for serializer + * @param properties Property writers used for actual serialization + */ + public ClonedBeanSerializer(JavaType type, BeanSerializerBuilder builder, + BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) + { + super(type, builder, properties, filteredProperties); + } + + /** + * Alternate copy constructor that can be used to construct + * standard {@link ClonedBeanSerializer} passing an instance of + * "compatible enough" source serializer. + */ + protected ClonedBeanSerializer(BeanSerializerBase src) { + super(src); + } + + protected ClonedBeanSerializer(BeanSerializerBase src, + ObjectIdWriter objectIdWriter) { + super(src, objectIdWriter); + } + + protected ClonedBeanSerializer(BeanSerializerBase src, + ObjectIdWriter objectIdWriter, Object filterId) { + super(src, objectIdWriter, filterId); + } + + protected ClonedBeanSerializer(BeanSerializerBase src, String[] toIgnore) { + super(src, toIgnore); + } + + /* + /********************************************************** + /* Life-cycle: factory methods, fluent factories + /********************************************************** + */ + + /** + * Method for constructing dummy bean serializer; one that + * never outputs any properties + */ + public static ClonedBeanSerializer createDummy(JavaType forType) + { + return new ClonedBeanSerializer(forType, null, NO_PROPS, null); + } + + @Override + public JsonSerializer unwrappingSerializer(NameTransformer unwrapper) { + return new UnwrappingBeanSerializer(this, unwrapper); + } + + @Override + public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) { + return new ClonedBeanSerializer(this, objectIdWriter, _propertyFilterId); + } + + @Override + protected BeanSerializerBase withFilterId(Object filterId) { + return new ClonedBeanSerializer(this, _objectIdWriter, filterId); + } + + @Override + protected BeanSerializerBase withIgnorals(String[] toIgnore) { + return new ClonedBeanSerializer(this, toIgnore); + } + + /** + * Implementation has to check whether as-array serialization + * is possible reliably; if (and only if) so, will construct + * a {@link BeanAsArraySerializer}, otherwise will return this + * serializer as is. + */ + @Override + protected BeanSerializerBase asArraySerializer() + { + /* Can not: + * + * - have Object Id (may be allowed in future) + * - have "any getter" + * - have per-property filters + */ + if ((_objectIdWriter == null) + && (_anyGetterWriter == null) + && (_propertyFilterId == null) + ) { + return new BeanAsArraySerializer(this); + } + // already is one, so: + return this; + } + + /* + /********************************************************** + /* JsonSerializer implementation that differs between impls + /********************************************************** + */ + + /** + * Main serialization method that will delegate actual output to + * configured + * {@link BeanPropertyWriter} instances. + */ + @Override + public void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + if (_objectIdWriter != null) { + _serializeWithObjectId(bean, gen, provider, true); + return; + } + gen.writeStartObject(); + // [databind#631]: Assign current value, to be accessible by custom serializers + gen.setCurrentValue(bean); + if (_propertyFilterId != null) { + serializeFieldsFiltered(bean, gen, provider); + } else { + serializeFields(bean, gen, provider); + } + gen.writeEndObject(); + } + + /* + /********************************************************** + /* Standard methods + /********************************************************** + */ + + @Override public String toString() { + return "BeanSerializer for "+handledType().getName(); + } +} diff --git a/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/Hibernate4Module.java b/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/Hibernate4Module.java index 2b43d88e..0d8d5371 100644 --- a/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/Hibernate4Module.java +++ b/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/Hibernate4Module.java @@ -3,6 +3,8 @@ import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; import org.hibernate.SessionFactory; import org.hibernate.engine.spi.Mapping; @@ -137,7 +139,14 @@ public void setupModule(SetupContext context) if (ai != null) { context.appendAnnotationIntrospector(ai); } - context.addSerializers(new HibernateSerializers(_mapping, _moduleFeatures)); + ObjectMapper objectMapper = context.getOwner(); + DefaultSerializerProvider defaultSerializerProvider = ((DefaultSerializerProvider) objectMapper + .getSerializerProvider()); + context.addSerializers(new HibernateSerializers( + defaultSerializerProvider.createInstance( + objectMapper.getSerializationConfig(), + objectMapper.getSerializerFactory()), _mapping, + _moduleFeatures)); context.addBeanSerializerModifier(new HibernateSerializerModifier(_moduleFeatures, _sessionFactory)); } diff --git a/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateProxySerializer.java b/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateProxySerializer.java index 54a0a50a..c7ad2c13 100644 --- a/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateProxySerializer.java +++ b/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateProxySerializer.java @@ -1,12 +1,16 @@ package com.fasterxml.jackson.datatype.hibernate4; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder; +import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; +import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.proxy.HibernateProxy; @@ -14,6 +18,7 @@ import java.io.IOException; import java.util.HashMap; +import java.util.Map; /** * Serializer to use for values proxied using {@link org.hibernate.proxy.HibernateProxy}. @@ -25,7 +30,7 @@ * this one) have. */ public class HibernateProxySerializer - extends JsonSerializer + extends ClonedBeanSerializer { /** * Property that has proxy value to handle @@ -48,22 +53,51 @@ public class HibernateProxySerializer /********************************************************************** */ - public HibernateProxySerializer(boolean forceLazyLoading) - { - this(forceLazyLoading, false, null); + public HibernateProxySerializer(JavaType type, BeanSerializerBuilder builder, + BeanPropertyWriter[] properties, + BeanPropertyWriter[] filteredProperties, + boolean forceLazyLoading, + boolean serializeIdentifier, Mapping mapping) { + super(type, builder, properties, filteredProperties); + _forceLazyLoading = forceLazyLoading; + _serializeIdentifier = serializeIdentifier; + _mapping = mapping; + _dynamicSerializers = PropertySerializerMap.emptyMap(); + _property = null; } - public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier) { - this(forceLazyLoading, serializeIdentifier, null); + protected HibernateProxySerializer(HibernateProxySerializer hibernateProxySerializer, String[] toIgnore) { + super(hibernateProxySerializer, toIgnore); + _forceLazyLoading = hibernateProxySerializer._forceLazyLoading; + _serializeIdentifier = hibernateProxySerializer._serializeIdentifier; + _mapping = hibernateProxySerializer._mapping; + _dynamicSerializers = PropertySerializerMap.emptyMap(); + _property = null; } - public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, Mapping mapping) { - _forceLazyLoading = forceLazyLoading; - _serializeIdentifier = serializeIdentifier; - _mapping = mapping; + protected HibernateProxySerializer(HibernateProxySerializer hibernateProxySerializer, ObjectIdWriter objectIdWriter, Object filterId) { + super(hibernateProxySerializer, objectIdWriter, filterId); + _forceLazyLoading = hibernateProxySerializer._forceLazyLoading; + _serializeIdentifier = hibernateProxySerializer._serializeIdentifier; + _mapping = hibernateProxySerializer._mapping; _dynamicSerializers = PropertySerializerMap.emptyMap(); _property = null; } + + @Override + public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) { + return new HibernateProxySerializer(this, objectIdWriter, _propertyFilterId); + } + + @Override + protected BeanSerializerBase withFilterId(Object filterId) { + return new HibernateProxySerializer(this, _objectIdWriter, filterId); + } + + @Override + protected BeanSerializerBase withIgnorals(String[] toIgnore) { + return new HibernateProxySerializer(this, toIgnore); + } /* /********************************************************************** @@ -73,30 +107,34 @@ public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdent // since 2.3 @Override - public boolean isEmpty(HibernateProxy value) + public boolean isEmpty(Object value) { - return (value == null) || (findProxied(value) == null); + return (value == null) || (findProxied((HibernateProxy) value) == null); } - + @Override - public void serialize(HibernateProxy value, JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException + public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { - Object proxiedValue = findProxied(value); + Object proxiedValue = findProxied((HibernateProxy) value); // TODO: figure out how to suppress nulls, if necessary? (too late for that here) if (proxiedValue == null) { provider.defaultSerializeNull(jgen); return; } - findSerializer(provider, proxiedValue).serialize(proxiedValue, jgen, provider); + if(proxiedValue instanceof Map){ + findSerializer(provider, proxiedValue).serialize(proxiedValue, jgen, provider); + } else{ + super.serialize(value, jgen, provider); + } } @Override - public void serializeWithType(HibernateProxy value, JsonGenerator jgen, SerializerProvider provider, + public void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) - throws IOException, JsonProcessingException + throws IOException { - Object proxiedValue = findProxied(value); + Object proxiedValue = findProxied((HibernateProxy) value); if (proxiedValue == null) { provider.defaultSerializeNull(jgen); return; @@ -106,7 +144,11 @@ public void serializeWithType(HibernateProxy value, JsonGenerator jgen, Serializ * to know how to apply additional type info) or other things; * so it's not going to work well. But... we'll do out best. */ - findSerializer(provider, proxiedValue).serializeWithType(proxiedValue, jgen, provider, typeSer); + if(proxiedValue instanceof Map) { + findSerializer(provider, proxiedValue).serializeWithType(proxiedValue, jgen, provider, typeSer); + } else{ + super.serializeWithType(value, jgen, provider, typeSer); + } } /* @@ -116,7 +158,7 @@ public void serializeWithType(HibernateProxy value, JsonGenerator jgen, Serializ */ protected JsonSerializer findSerializer(SerializerProvider provider, Object value) - throws IOException, JsonProcessingException + throws IOException { /* TODO: if Hibernate did use generics, or we wanted to allow use of Jackson * annotations to indicate type, should take that into account. diff --git a/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateSerializers.java b/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateSerializers.java index ec3bab27..ec81eec9 100644 --- a/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateSerializers.java +++ b/hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateSerializers.java @@ -1,29 +1,38 @@ package com.fasterxml.jackson.datatype.hibernate4; -import com.fasterxml.jackson.databind.BeanDescription; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializationConfig; -import com.fasterxml.jackson.databind.ser.Serializers; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.ser.*; +import com.fasterxml.jackson.databind.ser.std.MapSerializer; +import com.fasterxml.jackson.databind.type.*; import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module.Feature; import org.hibernate.engine.spi.Mapping; import org.hibernate.proxy.HibernateProxy; -public class HibernateSerializers extends Serializers.Base +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class HibernateSerializers + extends BeanSerializerFactory implements Serializers { protected final boolean _forceLoading; protected final boolean _serializeIdentifiers; protected final Mapping _mapping; + private SerializerProvider _prov; public HibernateSerializers(int features) { - this(null, features); + this(null, null, features); } - public HibernateSerializers(Mapping mapping, int features) + public HibernateSerializers(SerializerProvider prov, Mapping mapping, int features) { + super(null); _forceLoading = Feature.FORCE_LAZY_LOADING.enabledIn(features); _serializeIdentifiers = Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS.enabledIn(features); _mapping = mapping; + _prov = prov; } @Override @@ -32,8 +41,132 @@ public JsonSerializer findSerializer(SerializationConfig config, { Class raw = type.getRawClass(); if (HibernateProxy.class.isAssignableFrom(raw)) { - return new HibernateProxySerializer(_forceLoading, _serializeIdentifiers, _mapping); + try { + BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc); + // BuilderUtil.setConfig(builder, config); + + // First: any detectable (auto-detect, annotations) properties + // to serialize? + List props; + props = findBeanProperties(_prov, beanDesc, builder); + if (props == null) { + props = new ArrayList(); + } + // [JACKSON-440] Need to allow modification bean properties to + // serialize: + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig + .serializerModifiers()) { + props = mod.changeProperties(config, beanDesc, props); + } + } + + // Any properties to suppress? + props = filterBeanProperties(config, beanDesc, props); + + // remove hibernate internal properties + Iterator iterator = props.iterator(); + while (iterator.hasNext()) { + BeanPropertyWriter beanPropertyWriter = iterator.next(); + if (beanPropertyWriter.getName().equals("handler") + || beanPropertyWriter.getName().equals( + "hibernateLazyInitializer")) { + iterator.remove(); + } + } + + // [JACKSON-440] Need to allow reordering of properties to + // serialize + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig + .serializerModifiers()) { + props = mod.orderProperties(config, beanDesc, props); + } + } + + /* + * And if Object Id is needed, some preparation for that as + * well: better do before view handling, mostly for the custom + * id case which needs access to a property + */ + builder.setObjectIdWriter(constructObjectIdHandler(_prov, + beanDesc, props)); + + builder.setProperties(props); + builder.setFilterId(findFilterId(config, beanDesc)); + + AnnotatedMember anyGetter = beanDesc.findAnyGetter(); + if (anyGetter != null) { + if (config.canOverrideAccessModifiers()) { + anyGetter.fixAccess(); + } + JavaType type2 = anyGetter.getType(beanDesc + .bindingsForBeanType()); + // copied from BasicSerializerFactory.buildMapSerializer(): + boolean staticTyping = config + .isEnabled(MapperFeature.USE_STATIC_TYPING); + JavaType valueType = type.getContentType(); + TypeSerializer typeSer = createTypeSerializer(config, + valueType); + // last 2 nulls; don't know key, value serializers (yet) + // TODO: support '@JsonIgnoreProperties' with any setter? + MapSerializer mapSer = MapSerializer.construct(/* + * ignored + * props + */null, + type2, staticTyping, typeSer, null, null, /* filterId */ + null); + // TODO: can we find full PropertyName? + PropertyName name = new PropertyName(anyGetter.getName()); + BeanProperty.Std anyProp = new BeanProperty.Std(name, + valueType, null, beanDesc.getClassAnnotations(), + anyGetter, PropertyMetadata.STD_OPTIONAL); + builder.setAnyGetter(new AnyGetterWriter(anyProp, + anyGetter, mapSer)); + } + // Next: need to gather view information, if any: + processViews(config, builder); + + // Finally: let interested parties mess with the result bit + // more... + if (_factoryConfig.hasSerializerModifiers()) { + for (BeanSerializerModifier mod : _factoryConfig + .serializerModifiers()) { + builder = mod.updateBuilder(config, beanDesc, builder); + } + } + return new HibernateProxySerializer(type, builder, + props.toArray(new BeanPropertyWriter[] {}), builder.getFilteredProperties(), + _forceLoading, _serializeIdentifiers, _mapping); + } catch (JsonMappingException e) { + throw new RuntimeException(e.getMessage(), e); + } } return null; } + + @Override + public JsonSerializer findArraySerializer(SerializationConfig config, ArrayType type, BeanDescription beanDesc, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return null; + } + + @Override + public JsonSerializer findCollectionSerializer(SerializationConfig config, CollectionType type, BeanDescription beanDesc, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return null; + } + + @Override + public JsonSerializer findCollectionLikeSerializer(SerializationConfig config, CollectionLikeType type, BeanDescription beanDesc, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return null; + } + + @Override + public JsonSerializer findMapSerializer(SerializationConfig config, MapType type, BeanDescription beanDesc, JsonSerializer keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return null; + } + + @Override + public JsonSerializer findMapLikeSerializer(SerializationConfig config, MapLikeType type, BeanDescription beanDesc, JsonSerializer keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer elementValueSerializer) { + return null; + } }