diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/client/ser/bean/AbstractBeanJsonSerializer.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/client/ser/bean/AbstractBeanJsonSerializer.java index 94a4d984..b043f2f3 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/client/ser/bean/AbstractBeanJsonSerializer.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/client/ser/bean/AbstractBeanJsonSerializer.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonIdentityInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.github.nmorel.gwtjackson.client.JsonSerializationContext; import com.github.nmorel.gwtjackson.client.JsonSerializer; import com.github.nmorel.gwtjackson.client.JsonSerializerParameters; @@ -36,7 +37,7 @@ */ public abstract class AbstractBeanJsonSerializer extends JsonSerializer implements InternalSerializer { - private final Map> serializers; + protected final Map> serializers; private final Map subtypeClassToSerializer; @@ -143,15 +144,8 @@ public void serializeInternally( JsonWriter writer, T value, JsonSerializationCo switch ( typeInfo.getInclude() ) { case PROPERTY: // type info is included as a property of the object - writer.beginObject(); - writer.name( typeInfo.getPropertyName() ); - writer.value( typeInformation ); - if ( null != idWriter ) { - writer.name( identityInfo.getPropertyName() ); - idWriter.serializeId( writer, ctx ); - } - serializeObject( writer, value, ctx, identityInfo, ignoredProperties ); - writer.endObject(); + serializeObject( writer, value, ctx, ignoredProperties, identityInfo, idWriter, typeInfo + .getPropertyName(), typeInformation ); return; case WRAPPER_OBJECT: @@ -159,13 +153,7 @@ public void serializeInternally( JsonWriter writer, T value, JsonSerializationCo // info and the value the object writer.beginObject(); writer.name( typeInformation ); - writer.beginObject(); - if ( null != idWriter ) { - writer.name( identityInfo.getPropertyName() ); - idWriter.serializeId( writer, ctx ); - } - serializeObject( writer, value, ctx, identityInfo, ignoredProperties ); - writer.endObject(); + serializeObject( writer, value, ctx, ignoredProperties, identityInfo, idWriter ); writer.endObject(); return; @@ -174,13 +162,7 @@ public void serializeInternally( JsonWriter writer, T value, JsonSerializationCo // info and the second one the object writer.beginArray(); writer.value( typeInformation ); - writer.beginObject(); - if ( null != idWriter ) { - writer.name( identityInfo.getPropertyName() ); - idWriter.serializeId( writer, ctx ); - } - serializeObject( writer, value, ctx, identityInfo, ignoredProperties ); - writer.endObject(); + serializeObject( writer, value, ctx, ignoredProperties, identityInfo, idWriter ); writer.endArray(); return; @@ -190,24 +172,51 @@ public void serializeInternally( JsonWriter writer, T value, JsonSerializationCo } } - writer.beginObject(); - if ( null != idWriter ) { - writer.name( identityInfo.getPropertyName() ); - idWriter.serializeId( writer, ctx ); - } - serializeObject( writer, value, ctx, identityInfo, ignoredProperties ); - writer.endObject(); + serializeObject( writer, value, ctx, ignoredProperties, identityInfo, idWriter ); + } + + /** + * Serializes all the properties of the bean in a json object. + * + * @param writer writer + * @param value bean to serialize + * @param ctx context of the serialization process + * @param ignoredProperties ignored properties + * @param identityInfo identity info + * @param idWriter identifier writer + */ + private void serializeObject( JsonWriter writer, T value, JsonSerializationContext ctx, Set ignoredProperties, + IdentitySerializationInfo identityInfo, ObjectIdSerializer idWriter ) { + serializeObject( writer, value, ctx, ignoredProperties, identityInfo, idWriter, null, null ); } /** - * Serializes all the properties of the bean. The {@link JsonWriter} must be in a json object. + * Serializes all the properties of the bean in a json object. * * @param writer writer * @param value bean to serialize * @param ctx context of the serialization process + * @param ignoredProperties ignored properties + * @param identityInfo identity info + * @param idWriter identifier writer + * @param typeName in case of type info as property, the name of the property + * @param typeInformation in case of type info as property, the type information */ - private void serializeObject( JsonWriter writer, T value, JsonSerializationContext ctx, IdentitySerializationInfo identityInfo, - Set ignoredProperties ) { + protected void serializeObject( JsonWriter writer, T value, JsonSerializationContext ctx, Set ignoredProperties, + IdentitySerializationInfo identityInfo, ObjectIdSerializer idWriter, String typeName, String + typeInformation ) { + writer.beginObject(); + + if ( null != typeName && null != typeInformation ) { + writer.name( typeName ); + writer.value( typeInformation ); + } + + if ( null != idWriter ) { + writer.name( identityInfo.getPropertyName() ); + idWriter.serializeId( writer, ctx ); + } + for ( Map.Entry> entry : serializers.entrySet() ) { if ( (null == identityInfo || !identityInfo.isProperty() || !identityInfo.getPropertyName().equals( entry .getKey() )) && !ignoredProperties.contains( entry.getKey() ) ) { @@ -215,5 +224,7 @@ private void serializeObject( JsonWriter writer, T value, JsonSerializationConte entry.getValue().serialize( writer, value, ctx ); } } + + writer.endObject(); } } diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/client/ser/bean/AbstractValueBeanJsonSerializer.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/client/ser/bean/AbstractValueBeanJsonSerializer.java new file mode 100644 index 00000000..1e255943 --- /dev/null +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/client/ser/bean/AbstractValueBeanJsonSerializer.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014 Nicolas Morel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.nmorel.gwtjackson.client.ser.bean; + +import java.util.Set; + +import com.github.nmorel.gwtjackson.client.JsonSerializationContext; +import com.github.nmorel.gwtjackson.client.stream.JsonWriter; + +/** + * @author Nicolas Morel + */ +public abstract class AbstractValueBeanJsonSerializer extends AbstractBeanJsonSerializer { + + private final BeanPropertySerializer serializer; + + protected AbstractValueBeanJsonSerializer() { + super(); + this.serializer = initValueSerializer(); + } + + protected abstract BeanPropertySerializer initValueSerializer(); + + @Override + protected void serializeObject( JsonWriter writer, T value, JsonSerializationContext ctx, Set ignoredProperties, + IdentitySerializationInfo identityInfo, ObjectIdSerializer idWriter, String typeName, String + typeInformation ) { + serializer.serialize( writer, value, ctx ); + } +} diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/AbstractBeanJsonCreator.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/AbstractBeanJsonCreator.java index 74e2e2be..387cfa46 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/AbstractBeanJsonCreator.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/AbstractBeanJsonCreator.java @@ -33,6 +33,7 @@ import com.github.nmorel.gwtjackson.client.deser.bean.PropertyIdentityDeserializationInfo; import com.github.nmorel.gwtjackson.client.ser.bean.AbstractBeanJsonSerializer; import com.github.nmorel.gwtjackson.client.ser.bean.AbstractIdentitySerializationInfo; +import com.github.nmorel.gwtjackson.client.ser.bean.AbstractValueBeanJsonSerializer; import com.github.nmorel.gwtjackson.client.ser.bean.ObjectIdSerializer; import com.github.nmorel.gwtjackson.client.ser.bean.PropertyIdentitySerializationInfo; import com.github.nmorel.gwtjackson.rebind.bean.BeanIdentityInfo; @@ -102,8 +103,8 @@ public String getJoinedTypeParameterMappersWithType() { protected BeanJsonMapperInfo mapperInfo; - public AbstractBeanJsonCreator( TreeLogger logger, GeneratorContext context, RebindConfiguration configuration, - JacksonTypeOracle typeOracle ) { + public AbstractBeanJsonCreator( TreeLogger logger, GeneratorContext context, RebindConfiguration configuration, JacksonTypeOracle + typeOracle ) { super( logger, context, configuration, typeOracle ); } @@ -167,6 +168,8 @@ public String create( JClassType beanType ) throws UnableToCompleteException, Un ImmutableMap properties = PropertyProcessor .findAllProperties( configuration, logger, typeOracle, beanInfo, samePackage ); + beanInfo = BeanProcessor.processProperties( configuration, logger, typeOracle, beanInfo, properties ); + mapperInfo = new BeanJsonMapperInfo( beanType, qualifiedSerializerClassName, simpleSerializerClassName, qualifiedDeserializerClassName, simpleDeserializerClassName, beanInfo, properties ); @@ -175,15 +178,23 @@ public String create( JClassType beanType ) throws UnableToCompleteException, Un String superclass; if ( isSerializer() ) { - superclass = ABSTRACT_BEAN_JSON_SERIALIZER_CLASS + "<" + beanType.getParameterizedQualifiedSourceName() + ">"; - } else if ( isObject( beanType ) ) { - superclass = AbstractObjectBeanJsonDeserializer.class.getCanonicalName(); - } else if ( isSerializable( beanType ) ) { - superclass = AbstractSerializableBeanJsonDeserializer.class.getCanonicalName(); - } else if ( mapperInfo.getBeanInfo().isCreatorDelegation() ) { - superclass = AbstractDelegationBeanJsonDeserializer.class.getCanonicalName() + "<" + beanType.getParameterizedQualifiedSourceName() + ">"; + if ( mapperInfo.getBeanInfo().getValuePropertyInfo().isPresent() ) { + superclass = AbstractValueBeanJsonSerializer.class.getCanonicalName(); + } else { + superclass = ABSTRACT_BEAN_JSON_SERIALIZER_CLASS; + } + superclass = superclass + "<" + beanType.getParameterizedQualifiedSourceName() + ">"; } else { - superclass = ABSTRACT_BEAN_JSON_DESERIALIZER_CLASS + "<" + beanType.getParameterizedQualifiedSourceName() + ">"; + if ( isObject( beanType ) ) { + superclass = AbstractObjectBeanJsonDeserializer.class.getCanonicalName(); + } else if ( isSerializable( beanType ) ) { + superclass = AbstractSerializableBeanJsonDeserializer.class.getCanonicalName(); + } else if ( mapperInfo.getBeanInfo().isCreatorDelegation() ) { + superclass = AbstractDelegationBeanJsonDeserializer.class.getCanonicalName() + "<" + beanType + .getParameterizedQualifiedSourceName() + ">"; + } else { + superclass = ABSTRACT_BEAN_JSON_DESERIALIZER_CLASS + "<" + beanType.getParameterizedQualifiedSourceName() + ">"; + } } SourceWriter source = getSourceWriter( printWriter, packageName, getSimpleClassName() + getGenericClassBoundedParameters(), @@ -214,11 +225,11 @@ protected String getGenericClassBoundedParameters() { return mapperInfo.getGenericClassBoundedParameters(); } - protected abstract void writeClassBody( SourceWriter source, BeanInfo info, ImmutableMap properties ) throws UnableToCompleteException, UnsupportedTypeException; + protected abstract void writeClassBody( SourceWriter source, BeanInfo info, ImmutableMap properties ) throws + UnableToCompleteException, UnsupportedTypeException; - protected TypeParameters generateTypeParameterMapperFields( SourceWriter source, BeanInfo beanInfo, String mapperClass, - String mapperNameFormat ) throws UnableToCompleteException { + protected TypeParameters generateTypeParameterMapperFields( SourceWriter source, BeanInfo beanInfo, String mapperClass, String + mapperNameFormat ) throws UnableToCompleteException { if ( beanInfo.getParameterizedTypes().isEmpty() ) { return null; } @@ -252,14 +263,6 @@ protected String getParameterizedQualifiedClassName( JType type ) { } } - protected String getQualifiedClassName( JType type ) { - if ( null == type.isPrimitive() ) { - return type.getQualifiedSourceName(); - } else { - return type.isPrimitive().getQualifiedBoxedSourceName(); - } - } - protected Optional getIdentitySerializerType( BeanIdentityInfo identityInfo ) throws UnableToCompleteException, UnsupportedTypeException { if ( identityInfo.isIdABeanProperty() ) { @@ -401,8 +404,8 @@ private JClassType findFirstTypeToApplyPropertyAnnotation( List map return findFirstTypeToApplyPropertyAnnotation( subLevel ); } - protected void generateCommonPropertyParameters( SourceWriter source, PropertyInfo property, - JMapperType mapperType ) throws UnableToCompleteException { + protected void generateCommonPropertyParameters( SourceWriter source, PropertyInfo property, JMapperType mapperType ) throws + UnableToCompleteException { if ( property.getFormat().isPresent() ) { JsonFormat format = property.getFormat().get(); if ( !Strings.isNullOrEmpty( format.pattern() ) ) { diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/BeanJsonMapperInfo.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/BeanJsonMapperInfo.java index 37e6afbb..445b6bf4 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/BeanJsonMapperInfo.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/BeanJsonMapperInfo.java @@ -46,8 +46,8 @@ public class BeanJsonMapperInfo { private final ImmutableMap properties; public BeanJsonMapperInfo( JClassType type, String qualifiedSerializerClassName, String simpleSerializerClassName, String - qualifiedDeserializerClassName, String simpleDeserializerClassName, BeanInfo beanInfo, ImmutableMap properties ) { + qualifiedDeserializerClassName, String simpleDeserializerClassName, BeanInfo beanInfo, ImmutableMap + properties ) { this.type = type; this.qualifiedSerializerClassName = qualifiedSerializerClassName; this.simpleSerializerClassName = simpleSerializerClassName; diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/BeanJsonSerializerCreator.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/BeanJsonSerializerCreator.java index 5453e334..e0d22f92 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/BeanJsonSerializerCreator.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/BeanJsonSerializerCreator.java @@ -52,8 +52,8 @@ public class BeanJsonSerializerCreator extends AbstractBeanJsonCreator { private static final String JSON_SERIALIZER_PARAMETERS_CLASS = "com.github.nmorel.gwtjackson.client.JsonSerializerParameters"; - public BeanJsonSerializerCreator( TreeLogger logger, GeneratorContext context, RebindConfiguration configuration, - JacksonTypeOracle typeOracle ) { + public BeanJsonSerializerCreator( TreeLogger logger, GeneratorContext context, RebindConfiguration configuration, JacksonTypeOracle + typeOracle ) { super( logger, context, configuration, typeOracle ); } @@ -63,8 +63,8 @@ protected boolean isSerializer() { } @Override - protected void writeClassBody( SourceWriter source, BeanInfo beanInfo, ImmutableMap properties ) throws UnableToCompleteException { + protected void writeClassBody( SourceWriter source, BeanInfo beanInfo, ImmutableMap properties ) throws + UnableToCompleteException { source.println(); TypeParameters typeParameters = generateTypeParameterMapperFields( source, beanInfo, JSON_SERIALIZER_CLASS, @@ -79,27 +79,20 @@ protected void writeClassBody( SourceWriter source, BeanInfo beanInfo, Immutable source.println(); if ( !properties.isEmpty() ) { - Map propertiesMap = new LinkedHashMap(); - for ( PropertyInfo propertyInfo : properties.values() ) { - if ( null != propertyInfo && propertyInfo.getGetterAccessor().isPresent() && !propertyInfo.isIgnored() ) { - if ( propertyInfo.isRawValue() ) { - JSerializerType serializerType = new JSerializerType.Builder().type( propertyInfo.getType() ).instance( String - .format( "%s.<%s>getInstance()", RawValueJsonSerializer.class.getCanonicalName(), propertyInfo.getType() - .getParameterizedQualifiedSourceName() ) ).build(); + if ( beanInfo.getValuePropertyInfo().isPresent() ) { + generateInitValueSerializerMethod( source, beanInfo, beanInfo.getValuePropertyInfo().get() ); + } else { + Map propertiesMap = new LinkedHashMap(); + for ( PropertyInfo propertyInfo : properties.values() ) { + JSerializerType serializerType = getJsonSerializerFromProperty( propertyInfo ); + if ( null != serializerType ) { propertiesMap.put( propertyInfo, serializerType ); - } else { - try { - JSerializerType serializerType = getJsonSerializerFromType( propertyInfo.getType() ); - propertiesMap.put( propertyInfo, serializerType ); - } catch ( UnsupportedTypeException e ) { - logger.log( Type.WARN, "Property '" + propertyInfo.getPropertyName() + "' is ignored." ); - } } } - } - if ( !propertiesMap.isEmpty() ) { - generateInitSerializersMethod( source, beanInfo, propertiesMap ); - source.println(); + if ( !propertiesMap.isEmpty() ) { + generateInitSerializersMethod( source, beanInfo, propertiesMap ); + source.println(); + } } } @@ -127,6 +120,23 @@ protected void writeClassBody( SourceWriter source, BeanInfo beanInfo, Immutable generateClassGetterMethod( source, beanInfo ); } + private JSerializerType getJsonSerializerFromProperty( PropertyInfo propertyInfo ) throws UnableToCompleteException { + if ( null != propertyInfo && propertyInfo.getGetterAccessor().isPresent() && !propertyInfo.isIgnored() ) { + if ( propertyInfo.isRawValue() ) { + return new JSerializerType.Builder().type( propertyInfo.getType() ).instance( String + .format( "%s.<%s>getInstance()", RawValueJsonSerializer.class.getCanonicalName(), propertyInfo.getType() + .getParameterizedQualifiedSourceName() ) ).build(); + } else { + try { + return getJsonSerializerFromType( propertyInfo.getType() ); + } catch ( UnsupportedTypeException e ) { + logger.log( Type.WARN, "Property '" + propertyInfo.getPropertyName() + "' is ignored." ); + } + } + } + return null; + } + private void generateConstructors( SourceWriter source, TypeParameters typeParameters ) throws UnableToCompleteException { source.print( "public %s(", getSimpleClassName() ); @@ -147,8 +157,24 @@ private void generateConstructors( SourceWriter source, TypeParameters typeParam source.println( "}" ); } - private void generateInitSerializersMethod( SourceWriter source, BeanInfo beanInfo, Map properties ) throws UnableToCompleteException { + private void generateInitValueSerializerMethod( SourceWriter source, BeanInfo beanInfo, PropertyInfo propertyInfo ) throws + UnableToCompleteException { + source.println( "@Override" ); + source.println( "protected %s<%s, ?> initValueSerializer() {", BEAN_PROPERTY_SERIALIZER_CLASS, beanInfo.getType() + .getParameterizedQualifiedSourceName() ); + source.indent(); + + source.print( "return " ); + generateSerializer( source, beanInfo, propertyInfo, getJsonSerializerFromProperty( propertyInfo ) ); + source.println( ";" ); + + source.outdent(); + source.println( "}" ); + source.println(); + } + + private void generateInitSerializersMethod( SourceWriter source, BeanInfo beanInfo, Map properties ) + throws UnableToCompleteException { String mapType = String.format( "<%s, %s<%s, ?>>", String.class.getCanonicalName(), BEAN_PROPERTY_SERIALIZER_CLASS, beanInfo .getType().getParameterizedQualifiedSourceName() ); String resultType = String.format( "%s%s", Map.class.getCanonicalName(), mapType ); @@ -159,52 +185,57 @@ private void generateInitSerializersMethod( SourceWriter source, BeanInfo beanIn source.println( "%s map = new %s%s(%s);", resultType, LinkedHashMap.class.getCanonicalName(), mapType, properties.size() ); source.println(); + for ( Entry entry : properties.entrySet() ) { - PropertyInfo property = entry.getKey(); + source.print( "map.put(\"%s\", ", entry.getKey().getPropertyName() ); + generateSerializer( source, beanInfo, entry.getKey(), entry.getValue() ); + source.println( ");" ); + source.println(); + } - Accessor getterAccessor = property.getGetterAccessor().get().getAccessor( "bean" ); + source.println( "return map;" ); + source.outdent(); + source.println( "}" ); + } - source.println( "map.put(\"%s\", new %s<%s, %s>() {", property - .getPropertyName(), BEAN_PROPERTY_SERIALIZER_CLASS, getParameterizedQualifiedClassName( beanInfo - .getType() ), getParameterizedQualifiedClassName( property.getType() ) ); + private void generateSerializer( SourceWriter source, BeanInfo beanInfo, PropertyInfo property, JSerializerType serializerType ) + throws UnableToCompleteException { + Accessor getterAccessor = property.getGetterAccessor().get().getAccessor( "bean" ); - source.indent(); - source.println( "@Override" ); - source.println( "protected %s newSerializer() {", JSON_SERIALIZER_CLASS ); - source.indent(); - source.println( "return %s;", entry.getValue().getInstance() ); - source.outdent(); - source.println( "}" ); + source.println( "new %s<%s, %s>() {", BEAN_PROPERTY_SERIALIZER_CLASS, getParameterizedQualifiedClassName( beanInfo + .getType() ), getParameterizedQualifiedClassName( property.getType() ) ); - generatePropertySerializerParameters( source, property, entry.getValue() ); + source.indent(); + source.println( "@Override" ); + source.println( "protected %s newSerializer() {", JSON_SERIALIZER_CLASS ); + source.indent(); + source.println( "return %s;", serializerType.getInstance() ); + source.outdent(); + source.println( "}" ); - source.println(); + generatePropertySerializerParameters( source, property, serializerType ); - source.println( "@Override" ); - source.println( "public %s getValue(%s bean, %s ctx) {", getParameterizedQualifiedClassName( property - .getType() ), getParameterizedQualifiedClassName( beanInfo.getType() ), JSON_SERIALIZATION_CONTEXT_CLASS ); - source.indent(); - source.println( "return %s;", getterAccessor.getAccessor() ); - source.outdent(); - source.println( "}" ); + source.println(); - if ( getterAccessor.getAdditionalMethod().isPresent() ) { - source.println(); - getterAccessor.getAdditionalMethod().get().write( source ); - } + source.println( "@Override" ); + source.println( "public %s getValue(%s bean, %s ctx) {", getParameterizedQualifiedClassName( property + .getType() ), getParameterizedQualifiedClassName( beanInfo.getType() ), JSON_SERIALIZATION_CONTEXT_CLASS ); + source.indent(); + source.println( "return %s;", getterAccessor.getAccessor() ); + source.outdent(); + source.println( "}" ); - source.outdent(); - source.println( "});" ); + if ( getterAccessor.getAdditionalMethod().isPresent() ) { source.println(); + getterAccessor.getAdditionalMethod().get().write( source ); } - source.println( "return map;" ); source.outdent(); - source.println( "}" ); + source.print( "}" ); } - private void generatePropertySerializerParameters( SourceWriter source, PropertyInfo property, - JSerializerType serializerType ) throws UnableToCompleteException { + private void generatePropertySerializerParameters( SourceWriter source, PropertyInfo property, JSerializerType serializerType ) + throws UnableToCompleteException { if ( property.getFormat().isPresent() || property.getIgnoredProperties().isPresent() || property.getIgnoreUnknown().isPresent() || property.getIdentityInfo().isPresent() || property.getTypeInfo().isPresent() || property.getInclude().isPresent() ) { @@ -304,11 +335,10 @@ private void generateInitMapSubtypeClassToSerializerMethod( SourceWriter source, String subtypeClass; String serializerClass; - String subtypeQualifiedClassName; - if ( configuration.getSerializer( subtype ).isPresent() || null != subtype.isEnum() ) { + if ( configuration.getSerializer( subtype ).isPresent() || null != subtype.isEnum() || Enum.class.getName().equals( subtype + .getQualifiedSourceName() ) ) { subtypeClass = DefaultSubtypeSerializer.class.getCanonicalName(); - subtypeQualifiedClassName = getQualifiedClassName( subtype ); - serializerClass = String.format( "%s<%s>", JsonSerializer.class.getName(), subtypeQualifiedClassName ); + serializerClass = String.format( "%s", JsonSerializer.class.getName() ); } else { subtypeClass = BeanSubtypeSerializer.class.getCanonicalName(); serializerClass = String.format( "%s", ABSTRACT_BEAN_JSON_SERIALIZER_CLASS ); diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanInfo.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanInfo.java index efd0281f..985c45fd 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanInfo.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanInfo.java @@ -17,6 +17,7 @@ package com.github.nmorel.gwtjackson.rebind.bean; import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.github.nmorel.gwtjackson.rebind.property.PropertyInfo; import com.google.gwt.core.ext.typeinfo.JAbstractMethod; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JParameter; @@ -44,6 +45,8 @@ public interface BeanInfo { Optional getTypeInfo(); + Optional getValuePropertyInfo(); + ImmutableSet getIgnoredFields(); JsonAutoDetect.Visibility getFieldVisibility(); diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanInfoBuilder.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanInfoBuilder.java index 7a4ec8ef..a6248339 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanInfoBuilder.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanInfoBuilder.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.github.nmorel.gwtjackson.rebind.property.PropertyInfo; import com.google.gwt.core.ext.typeinfo.JAbstractMethod; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JParameter; @@ -47,6 +48,8 @@ final class BeanInfoBuilder { private Optional typeInfo = Optional.absent(); + private Optional valuePropertyInfo = Optional.absent(); + private Set ignoredFields = Collections.emptySet(); private JsonAutoDetect.Visibility fieldVisibility = JsonAutoDetect.Visibility.DEFAULT; @@ -70,6 +73,29 @@ final class BeanInfoBuilder { BeanInfoBuilder() { } + BeanInfoBuilder( BeanInfo beanInfo ) { + this.type = beanInfo.getType(); + this.parameterizedTypes = beanInfo.getParameterizedTypes(); + this.creatorMethod = beanInfo.getCreatorMethod(); + this.creatorParameters = beanInfo.getCreatorParameters(); + this.creatorDefaultConstructor = beanInfo.isCreatorDefaultConstructor(); + this.creatorDelegation = beanInfo.isCreatorDelegation(); + this.typeInfo = beanInfo.getTypeInfo(); + this.valuePropertyInfo = beanInfo.getValuePropertyInfo(); + this.ignoredFields = beanInfo.getIgnoredFields(); + + this.fieldVisibility = beanInfo.getFieldVisibility(); + this.getterVisibility = beanInfo.getGetterVisibility(); + this.isGetterVisibility = beanInfo.getIsGetterVisibility(); + this.setterVisibility = beanInfo.getSetterVisibility(); + this.creatorVisibility = beanInfo.getCreatorVisibility(); + + this.ignoreUnknown = beanInfo.isIgnoreUnknown(); + this.propertyOrderList = beanInfo.getPropertyOrderList(); + this.propertyOrderAlphabetic = beanInfo.isPropertyOrderAlphabetic(); + this.identityInfo = beanInfo.getIdentityInfo(); + } + void setType( JClassType type ) { this.type = type; } @@ -102,6 +128,10 @@ void setTypeInfo( Optional typeInfo ) { this.typeInfo = typeInfo; } + void setValuePropertyInfo( Optional valuePropertyInfo ) { + this.valuePropertyInfo = valuePropertyInfo; + } + void setIgnoredFields( Set ignoredFields ) { this.ignoredFields = ignoredFields; } @@ -148,7 +178,7 @@ void setIdentityInfo( Optional identityInfo ) { BeanInfo build() { return new ImmutableBeanInfo( type, parameterizedTypes, creatorMethod, creatorParameters, creatorDefaultConstructor, - creatorDelegation, typeInfo, ignoredFields, fieldVisibility, getterVisibility, isGetterVisibility, setterVisibility, - creatorVisibility, ignoreUnknown, propertyOrderList, propertyOrderAlphabetic, identityInfo ); + creatorDelegation, typeInfo, valuePropertyInfo, ignoredFields, fieldVisibility, getterVisibility, isGetterVisibility, + setterVisibility, creatorVisibility, ignoreUnknown, propertyOrderList, propertyOrderAlphabetic, identityInfo ); } } diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanProcessor.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanProcessor.java index b24ce1db..cf63821f 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanProcessor.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/BeanProcessor.java @@ -20,10 +20,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.UUID; import com.fasterxml.jackson.annotation.JsonAutoDetect; @@ -48,6 +46,7 @@ import com.github.nmorel.gwtjackson.rebind.CreatorUtils; import com.github.nmorel.gwtjackson.rebind.JacksonTypeOracle; import com.github.nmorel.gwtjackson.rebind.RebindConfiguration; +import com.github.nmorel.gwtjackson.rebind.property.PropertyInfo; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JAbstractMethod; @@ -228,7 +227,8 @@ private static void determineInstanceCreator( RebindConfiguration configuration, .get( 0 ), JsonProperty.class ) ) { // delegation constructor builder.setCreatorDelegation( true ); - builder.setCreatorParameters( ImmutableMap.of( BeanJsonDeserializerCreator.DELEGATION_PARAM_NAME, creatorMethod.get().getParameters()[0] ) ); + builder.setCreatorParameters( ImmutableMap.of( BeanJsonDeserializerCreator.DELEGATION_PARAM_NAME, creatorMethod.get() + .getParameters()[0] ) ); } else { // we want the property name define in the mixin and the parameter defined in the real creator method ImmutableMap.Builder creatorParameters = ImmutableMap.builder(); @@ -330,10 +330,10 @@ public static Optional processType( TreeLogger logger, JacksonType // TODO we could do better, we actually extract metadata twice for a lot of classes ImmutableMap classToSerializationMetadata = extractMetadata( logger, configuration, type, jsonTypeInfo, propertySubTypes, typeSubTypes, CreatorUtils - .filterSubtypesForSerialization( logger, configuration, type ) ); + .filterSubtypesForSerialization( logger, configuration, type ) ); ImmutableMap classToDeserializationMetadata = extractMetadata( logger, configuration, type, jsonTypeInfo, propertySubTypes, typeSubTypes, CreatorUtils - .filterSubtypesForDeserialization( logger, configuration, type ) ); + .filterSubtypesForDeserialization( logger, configuration, type ) ); return Optional .of( new ImmutableBeanTypeInfo( use, include, propertyName, classToSerializationMetadata, @@ -442,4 +442,41 @@ private static JsonSubTypes.Type findTypeOnSubTypes( JClassType subtype, Optiona return null; } + /** + * Process the properties of the bean to find additionnal informations like @JsonValue. + * + * @param configuration the configuration + * @param logger the logger + * @param typeOracle the oracle + * @param beanInfo the previous bean information + * @param properties the properties of the bean + * + * @return the new informations about the bean and its properties + */ + public static BeanInfo processProperties( RebindConfiguration configuration, TreeLogger logger, JacksonTypeOracle typeOracle, + BeanInfo beanInfo, ImmutableMap properties ) { + PropertyInfo valueProperty = null; + for ( PropertyInfo propertyInfo : properties.values() ) { + if ( propertyInfo.isValue() ) { + valueProperty = propertyInfo; + break; + } + } + + if ( null == valueProperty ) { + return beanInfo; + } + + BeanInfoBuilder builder = new BeanInfoBuilder( beanInfo ); + builder.setValuePropertyInfo( Optional.of( valueProperty ) ); + + if ( beanInfo.getTypeInfo().isPresent() && As.PROPERTY.equals( beanInfo.getTypeInfo().get().getInclude() ) ) { + // if the bean has type info on property with @JsonValue, we change it to WRAPPER_ARRAY because the value may not be an object + BeanTypeInfo typeInfo = beanInfo.getTypeInfo().get(); + builder.setTypeInfo( Optional.of( new ImmutableBeanTypeInfo( typeInfo.getUse(), As.WRAPPER_ARRAY, typeInfo + .getPropertyName(), typeInfo.getMapTypeToSerializationMetadata(), typeInfo.getMapTypeToDeserializationMetadata() ) ) ); + } + + return builder.build(); + } } diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/ImmutableBeanInfo.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/ImmutableBeanInfo.java index 02d4e4f9..50528648 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/ImmutableBeanInfo.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/bean/ImmutableBeanInfo.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.github.nmorel.gwtjackson.rebind.property.PropertyInfo; import com.google.gwt.core.ext.typeinfo.JAbstractMethod; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JParameter; @@ -50,6 +51,8 @@ final class ImmutableBeanInfo implements BeanInfo { private final Optional typeInfo; + private final Optional valuePropertyInfo; + /*#### Visibility properties ####*/ private final ImmutableSet ignoredFields; @@ -75,9 +78,9 @@ final class ImmutableBeanInfo implements BeanInfo { ImmutableBeanInfo( JClassType type, List parameterizedTypes, Optional creatorMethod, Map creatorParameters, boolean creatorDefaultConstructor, boolean creatorDelegation, Optional typeInfo, - Set ignoredFields, Visibility fieldVisibility, Visibility getterVisibility, Visibility isGetterVisibility, - Visibility setterVisibility, Visibility creatorVisibility, boolean ignoreUnknown, List propertyOrderList, - boolean propertyOrderAlphabetic, Optional identityInfo ) { + Optional valuePropertyInfo, Set ignoredFields, Visibility fieldVisibility, Visibility + getterVisibility, Visibility isGetterVisibility, Visibility setterVisibility, Visibility creatorVisibility, boolean + ignoreUnknown, List propertyOrderList, boolean propertyOrderAlphabetic, Optional identityInfo ) { this.type = type; this.parameterizedTypes = ImmutableList.copyOf( parameterizedTypes ); @@ -86,6 +89,7 @@ final class ImmutableBeanInfo implements BeanInfo { this.creatorDefaultConstructor = creatorDefaultConstructor; this.creatorDelegation = creatorDelegation; this.typeInfo = typeInfo; + this.valuePropertyInfo = valuePropertyInfo; this.ignoredFields = ImmutableSet.copyOf( ignoredFields ); this.fieldVisibility = fieldVisibility; @@ -135,6 +139,11 @@ public Optional getTypeInfo() { return typeInfo; } + @Override + public Optional getValuePropertyInfo() { + return valuePropertyInfo; + } + @Override public ImmutableSet getIgnoredFields() { return ignoredFields; diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/PropertyInfo.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/PropertyInfo.java index 0c87c330..700e0f80 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/PropertyInfo.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/PropertyInfo.java @@ -38,6 +38,8 @@ public interface PropertyInfo { boolean isRawValue(); + boolean isValue(); + Optional getManagedReference(); Optional getBackReference(); diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/ImmutablePropertyInfo.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/ImmutablePropertyInfo.java index 4b733490..c9441583 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/ImmutablePropertyInfo.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/ImmutablePropertyInfo.java @@ -40,6 +40,8 @@ final class ImmutablePropertyInfo implements PropertyInfo { private final boolean rawValue; + private final boolean value; + private final Optional managedReference; private final Optional backReference; @@ -60,16 +62,17 @@ final class ImmutablePropertyInfo implements PropertyInfo { private final Optional ignoredProperties; - ImmutablePropertyInfo( String propertyName, JType type, boolean ignored, boolean required, boolean rawValue, - Optional managedReference, Optional backReference, - Optional getterAccessor, Optional setterAccessor, - Optional identityInfo, Optional typeInfo, Optional format, - Optional include, Optional ignoreUnknown, Optional ignoredProperties ) { + ImmutablePropertyInfo( String propertyName, JType type, boolean ignored, boolean required, boolean rawValue, boolean value, + Optional managedReference, Optional backReference, Optional + getterAccessor, Optional setterAccessor, Optional identityInfo, + Optional typeInfo, Optional format, Optional include, Optional + ignoreUnknown, Optional ignoredProperties ) { this.propertyName = propertyName; this.type = type; this.ignored = ignored; this.required = required; this.rawValue = rawValue; + this.value = value; this.managedReference = managedReference; this.backReference = backReference; this.getterAccessor = getterAccessor; @@ -107,6 +110,11 @@ public boolean isRawValue() { return rawValue; } + @Override + public boolean isValue() { + return value; + } + @Override public Optional getManagedReference() { return managedReference; diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/PropertyInfoBuilder.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/PropertyInfoBuilder.java index 6f3e853e..24ed6c28 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/PropertyInfoBuilder.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/PropertyInfoBuilder.java @@ -40,6 +40,8 @@ final class PropertyInfoBuilder { private boolean rawValue = false; + private boolean value = false; + private Optional managedReference = Optional.absent(); private Optional backReference = Optional.absent(); @@ -97,6 +99,14 @@ void setRawValue( boolean rawValue ) { this.rawValue = rawValue; } + boolean isValue() { + return value; + } + + void setValue( boolean value ) { + this.value = value; + } + Optional getManagedReference() { return managedReference; } @@ -178,7 +188,7 @@ void setIgnoredProperties( Optional ignoredProperties ) { } PropertyInfo build() { - return new ImmutablePropertyInfo( propertyName, type, ignored, required, rawValue, managedReference, backReference, + return new ImmutablePropertyInfo( propertyName, type, ignored, required, rawValue, value, managedReference, backReference, getterAccessor, setterAccessor, identityInfo, typeInfo, format, include, ignoreUnknown, ignoredProperties ); } } diff --git a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/PropertyProcessor.java b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/PropertyProcessor.java index 1bad4bf0..b423c084 100644 --- a/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/PropertyProcessor.java +++ b/gwt-jackson/src/main/java/com/github/nmorel/gwtjackson/rebind/property/processor/PropertyProcessor.java @@ -39,6 +39,7 @@ import com.fasterxml.jackson.annotation.JsonRawValue; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonValue; import com.github.nmorel.gwtjackson.rebind.JacksonTypeOracle; import com.github.nmorel.gwtjackson.rebind.RebindConfiguration; import com.github.nmorel.gwtjackson.rebind.bean.BeanInfo; @@ -69,12 +70,11 @@ public final class PropertyProcessor { private static final List> AUTO_DISCOVERY_ANNOTATIONS = Arrays - .asList( JsonProperty.class, JsonManagedReference.class, JsonBackReference.class ); + .asList( JsonProperty.class, JsonManagedReference.class, JsonBackReference.class, JsonValue.class ); public static ImmutableMap findAllProperties( RebindConfiguration configuration, TreeLogger logger, - JacksonTypeOracle typeOracle, BeanInfo beanInfo, - boolean mapperInSamePackageAsType ) throws - UnableToCompleteException { + JacksonTypeOracle typeOracle, BeanInfo beanInfo, boolean + mapperInSamePackageAsType ) throws UnableToCompleteException { // we first parse the bean to retrieve all the properties ImmutableMap fieldsMap = PropertyParser.findPropertyAccessors( configuration, logger, beanInfo ); @@ -131,9 +131,8 @@ public String apply( @Nullable Entry entry ) { return result.build(); } - private static Optional processProperty( RebindConfiguration configuration, TreeLogger logger, - JacksonTypeOracle typeOracle, PropertyAccessors propertyAccessors, - BeanInfo beanInfo, boolean samePackage ) throws UnableToCompleteException { + private static Optional processProperty( RebindConfiguration configuration, TreeLogger logger, JacksonTypeOracle + typeOracle, PropertyAccessors propertyAccessors, BeanInfo beanInfo, boolean samePackage ) throws UnableToCompleteException { boolean getterAutoDetected = isGetterAutoDetected( propertyAccessors, beanInfo ); boolean setterAutoDetected = isSetterAutoDetected( propertyAccessors, beanInfo ); @@ -172,6 +171,11 @@ private static Optional processProperty( RebindConfiguration confi } determineSetter( propertyAccessors, samePackage, setterAutoDetected, fieldAutoDetected, builder ); + Optional jsonValue = propertyAccessors.getAnnotation( JsonValue.class ); + if ( jsonValue.isPresent() && jsonValue.get().value() && builder.getGetterAccessor().isPresent() ) { + builder.setValue( true ); + } + processBeanAnnotation( logger, typeOracle, configuration, type, propertyAccessors, builder ); builder.setFormat( propertyAccessors.getAnnotation( JsonFormat.class ) ); @@ -264,8 +268,8 @@ private static boolean isFieldAutoDetected( PropertyAccessors propertyAccessors, .isDefaultAccess() ); } - private static boolean isAutoDetected( JsonAutoDetect.Visibility visibility, boolean isPrivate, boolean isProtected, - boolean isPublic, boolean isDefaultAccess ) { + private static boolean isAutoDetected( JsonAutoDetect.Visibility visibility, boolean isPrivate, boolean isProtected, boolean + isPublic, boolean isDefaultAccess ) { switch ( visibility ) { case ANY: return true; @@ -320,8 +324,8 @@ private static boolean isPropertyIgnored( RebindConfiguration configuration, Pro } - private static void determineGetter( final PropertyAccessors propertyAccessors, final boolean samePackage, - final boolean getterAutoDetect, boolean fieldAutoDetect, final PropertyInfoBuilder builder ) { + private static void determineGetter( final PropertyAccessors propertyAccessors, final boolean samePackage, final boolean + getterAutoDetect, boolean fieldAutoDetect, final PropertyInfoBuilder builder ) { // if one of field/getter is present and the property has an annotation like JsonProperty or field/getter is auto detected if ( (propertyAccessors.getGetter().isPresent() || propertyAccessors.getField() .isPresent()) && (fieldAutoDetect || getterAutoDetect) ) { @@ -331,9 +335,8 @@ private static void determineGetter( final PropertyAccessors propertyAccessors, } } - private static void determineSetter( final PropertyAccessors propertyAccessors, final boolean samePackage, - final boolean setterAutoDetect, final boolean fieldAutoDetect, - final PropertyInfoBuilder builder ) { + private static void determineSetter( final PropertyAccessors propertyAccessors, final boolean samePackage, final boolean + setterAutoDetect, final boolean fieldAutoDetect, final PropertyInfoBuilder builder ) { // if one of field/setter is present and the property has an annotation like JsonProperty or field/setter is auto detected if ( (propertyAccessors.getSetter().isPresent() || propertyAccessors.getField() .isPresent()) && (fieldAutoDetect || setterAutoDetect) ) { @@ -343,9 +346,8 @@ private static void determineSetter( final PropertyAccessors propertyAccessors, } } - private static void processBeanAnnotation( TreeLogger logger, JacksonTypeOracle typeOracle, RebindConfiguration configuration, - JType type, PropertyAccessors propertyAccessors, - PropertyInfoBuilder builder ) throws UnableToCompleteException { + private static void processBeanAnnotation( TreeLogger logger, JacksonTypeOracle typeOracle, RebindConfiguration configuration, JType + type, PropertyAccessors propertyAccessors, PropertyInfoBuilder builder ) throws UnableToCompleteException { // identity Optional jsonIdentityInfo = propertyAccessors.getAnnotation( JsonIdentityInfo.class ); @@ -389,8 +391,8 @@ private static void processBeanAnnotation( TreeLogger logger, JacksonTypeOracle * * @return the extracted type */ - private static Optional extractBeanType( TreeLogger logger, JacksonTypeOracle typeOracle, JType type, - String propertyName ) { + private static Optional extractBeanType( TreeLogger logger, JacksonTypeOracle typeOracle, JType type, String propertyName + ) { if ( null != type.isWildcard() ) { // we use the base type to find the serializer to use type = type.isWildcard().getBaseType(); diff --git a/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/client/annotation/JsonCreatorGwtTest.java b/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/client/annotation/JsonCreatorGwtTest.java index 92fccb27..55038aa9 100644 --- a/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/client/annotation/JsonCreatorGwtTest.java +++ b/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/client/annotation/JsonCreatorGwtTest.java @@ -28,6 +28,7 @@ import com.github.nmorel.gwtjackson.shared.annotations.JsonCreatorTester.BeanWithConstructorAnnotated; import com.github.nmorel.gwtjackson.shared.annotations.JsonCreatorTester.BeanWithDefaultConstructorPrivate; import com.github.nmorel.gwtjackson.shared.annotations.JsonCreatorTester.BeanWithFactoryMethod; +import com.github.nmorel.gwtjackson.shared.annotations.JsonCreatorTester.BeanWithMapConstructorDelegationAndTypeInfo; import com.github.nmorel.gwtjackson.shared.annotations.JsonCreatorTester.BeanWithObjectConstructorDelegation; import com.github.nmorel.gwtjackson.shared.annotations.JsonCreatorTester.BeanWithPrivateFactoryMethod; import com.github.nmorel.gwtjackson.shared.annotations.JsonCreatorTester.BeanWithPropertiesOnlyPresentOnConstructor; @@ -206,4 +207,18 @@ public void testDeserializeBeanWithObjectConstructorDelegation() { JsonCreatorTester.INSTANCE .testDeserializeBeanWithObjectConstructorDelegation( BeanWithObjectConstructorDelegationReader.INSTANCE ); } + + /* ################################ */ + + public interface BeanWithMapConstructorDelegationAndTypeInfoMapper extends ObjectMapper, + ObjectMapperTester { + + static BeanWithMapConstructorDelegationAndTypeInfoMapper INSTANCE = GWT + .create( BeanWithMapConstructorDelegationAndTypeInfoMapper.class ); + } + + public void testBeanWithMapConstructorDelegationAndTypeInfo() { + JsonCreatorTester.INSTANCE + .testBeanWithMapConstructorDelegationAndTypeInfo( BeanWithMapConstructorDelegationAndTypeInfoMapper.INSTANCE ); + } } diff --git a/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/jackson/annotations/JsonCreatorJacksonTest.java b/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/jackson/annotations/JsonCreatorJacksonTest.java index d64a8012..b3b59c07 100644 --- a/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/jackson/annotations/JsonCreatorJacksonTest.java +++ b/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/jackson/annotations/JsonCreatorJacksonTest.java @@ -148,7 +148,15 @@ public void testBeanWithBooleanConstructorDelegationAndTypeInfo() { @Test public void testDeserializeBeanWithObjectConstructorDelegation() { JsonCreatorTester.INSTANCE - .testDeserializeBeanWithObjectConstructorDelegation( createReader( JsonCreatorTester - .BeanWithObjectConstructorDelegation.class ) ); + .testDeserializeBeanWithObjectConstructorDelegation( createReader( JsonCreatorTester.BeanWithObjectConstructorDelegation + .class ) ); + } + + @Test + @Ignore( "jackson put LinkedHashMap as type information ?!?" ) + public void testBeanWithMapConstructorDelegationAndTypeInfo() { + JsonCreatorTester.INSTANCE + .testBeanWithMapConstructorDelegationAndTypeInfo( createMapper( JsonCreatorTester + .BeanWithMapConstructorDelegationAndTypeInfo.class ) ); } } diff --git a/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/shared/annotations/JsonCreatorTester.java b/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/shared/annotations/JsonCreatorTester.java index 2627c86a..88d13f61 100644 --- a/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/shared/annotations/JsonCreatorTester.java +++ b/gwt-jackson/src/test/java/com/github/nmorel/gwtjackson/shared/annotations/JsonCreatorTester.java @@ -16,6 +16,7 @@ package com.github.nmorel.gwtjackson.shared.annotations; +import java.util.LinkedHashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonCreator; @@ -23,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.annotation.JsonValue; import com.github.nmorel.gwtjackson.client.exception.JsonDeserializationException; @@ -264,7 +266,7 @@ public Boolean getValue() { } } - @JsonTypeInfo( use = Id.CLASS ) + @JsonTypeInfo( use = Id.CLASS, include = As.PROPERTY ) public static class BeanWithBooleanConstructorDelegationAndTypeInfo { private final Boolean value; @@ -300,6 +302,28 @@ public void setB( Integer b ) { } } + @JsonTypeInfo( use = Id.CLASS, include = As.WRAPPER_ARRAY ) + public static class BeanWithMapConstructorDelegationAndTypeInfo { + + private final String a; + + private Integer b; + + @JsonCreator + public BeanWithMapConstructorDelegationAndTypeInfo( Map map ) { + a = (String) map.get( "propertyA" ); + b = (Integer) map.get( "propertyB" ); + } + + @JsonValue + public Map get() { + Map map = new LinkedHashMap(); + map.put( "propertyA", a ); + map.put( "propertyB", b ); + return map; + } + } + public static final JsonCreatorTester INSTANCE = new JsonCreatorTester(); private JsonCreatorTester() { @@ -534,4 +558,18 @@ public void testDeserializeBeanWithObjectConstructorDelegation( ObjectReaderTest assertEquals( 148, result.b.intValue() ); } + public void testBeanWithMapConstructorDelegationAndTypeInfo( ObjectMapperTester mapper ) { + Map map = new LinkedHashMap(); + map.put( "propertyA", "string A" ); + map.put( "propertyB", 148 ); + BeanWithMapConstructorDelegationAndTypeInfo bean = new BeanWithMapConstructorDelegationAndTypeInfo( map ); + String json = mapper.write( bean ); + assertEquals( "[\"" + BeanWithMapConstructorDelegationAndTypeInfo.class.getName() + "\",{\"propertyA\":\"string A\"," + + "\"propertyB\":148}]", json ); + + bean = mapper.read( json ); + assertEquals( "string A", bean.a ); + assertEquals( 148, bean.b.intValue() ); + } + } diff --git a/gwt-jackson/src/test/resources/com/github/nmorel/gwtjackson/GwtJacksonSharedTest.gwt.xml b/gwt-jackson/src/test/resources/com/github/nmorel/gwtjackson/GwtJacksonSharedTest.gwt.xml index 722bec53..3d30e2db 100644 --- a/gwt-jackson/src/test/resources/com/github/nmorel/gwtjackson/GwtJacksonSharedTest.gwt.xml +++ b/gwt-jackson/src/test/resources/com/github/nmorel/gwtjackson/GwtJacksonSharedTest.gwt.xml @@ -20,7 +20,7 @@ - +