Java tutorial
/* * Copyright 2013 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.Collections; import java.util.Map; import java.util.Set; import java.util.logging.Level; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonIdentityInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.github.nmorel.gwtjackson.client.JsonSerializationContext; import com.github.nmorel.gwtjackson.client.JsonSerializer; import com.github.nmorel.gwtjackson.client.JsonSerializerParameters; import com.github.nmorel.gwtjackson.client.stream.JsonWriter; /** * Base implementation of {@link JsonSerializer} for beans. * * @author Nicolas Morel */ public abstract class AbstractBeanJsonSerializer<T> extends JsonSerializer<T> implements InternalSerializer<T> { protected final BeanPropertySerializer[] serializers; private final Map<Class, SubtypeSerializer> subtypeClassToSerializer; private final IdentitySerializationInfo<T> defaultIdentityInfo; private final TypeSerializationInfo<T> defaultTypeInfo; private final AnyGetterPropertySerializer<T> anyGetterPropertySerializer; protected AbstractBeanJsonSerializer() { this.serializers = initSerializers(); this.defaultIdentityInfo = initIdentityInfo(); this.defaultTypeInfo = initTypeInfo(); this.subtypeClassToSerializer = initMapSubtypeClassToSerializer(); this.anyGetterPropertySerializer = initAnyGetterPropertySerializer(); } /** * Initialize the {@link Map} containing the property serializers. Returns an empty map if there are no properties to * serialize. */ protected BeanPropertySerializer[] initSerializers() { return new BeanPropertySerializer[0]; } /** * Initialize the {@link IdentitySerializationInfo}. Returns null if there is no {@link JsonIdentityInfo} annotation on bean. */ protected IdentitySerializationInfo<T> initIdentityInfo() { return null; } /** * Initialize the {@link TypeSerializationInfo}. Returns null if there is no {@link JsonTypeInfo} annotation on bean. */ protected TypeSerializationInfo<T> initTypeInfo() { return null; } /** * Initialize the {@link Map} containing the {@link SubtypeSerializer}. Returns an empty map if the bean has no subtypes. */ protected Map<Class, SubtypeSerializer> initMapSubtypeClassToSerializer() { return Collections.emptyMap(); } /** * Initialize the {@link AnyGetterPropertySerializer}. Returns null if there is no method annoted with {@link JsonAnyGetter} on bean. */ protected AnyGetterPropertySerializer<T> initAnyGetterPropertySerializer() { return null; } public abstract Class getSerializedType(); @Override public void doSerialize(JsonWriter writer, T value, JsonSerializationContext ctx, JsonSerializerParameters params) { getSerializer(writer, value, ctx).serializeInternally(writer, value, ctx, params, defaultIdentityInfo, defaultTypeInfo); } private InternalSerializer<T> getSerializer(JsonWriter writer, T value, JsonSerializationContext ctx) { if (value.getClass() == getSerializedType()) { return this; } SubtypeSerializer subtypeSerializer = subtypeClassToSerializer.get(value.getClass()); if (null == subtypeSerializer) { if (ctx.getLogger().isLoggable(Level.FINE)) { ctx.getLogger().fine("Cannot find serializer for class " + value.getClass() + ". Fallback to the serializer of " + getSerializedType()); } return this; } return subtypeSerializer; } public void serializeInternally(JsonWriter writer, T value, JsonSerializationContext ctx, JsonSerializerParameters params, IdentitySerializationInfo<T> defaultIdentityInfo, TypeSerializationInfo<T> defaultTypeInfo) { // Processing the parameters. We fallback to default if parameter is not present. final IdentitySerializationInfo identityInfo = null == params.getIdentityInfo() ? defaultIdentityInfo : params.getIdentityInfo(); final TypeSerializationInfo typeInfo = null == params.getTypeInfo() ? defaultTypeInfo : params.getTypeInfo(); final Set<String> ignoredProperties = null == params.getIgnoredProperties() ? Collections.<String>emptySet() : params.getIgnoredProperties(); if (params.isUnwrapped()) { // if unwrapped, we serialize the properties inside the current object serializeProperties(writer, value, ctx, ignoredProperties, identityInfo); return; } ObjectIdSerializer<?> idWriter = null; if (null != identityInfo) { idWriter = ctx.getObjectId(value); if (null != idWriter) { // the bean has already been serialized, we just serialize the id idWriter.serializeId(writer, ctx); return; } idWriter = identityInfo.getObjectId(value, ctx); if (identityInfo.isAlwaysAsId()) { idWriter.serializeId(writer, ctx); return; } ctx.addObjectId(value, idWriter); } if (null != typeInfo) { String typeInformation = typeInfo.getTypeInfo(value.getClass()); if (null == typeInformation) { ctx.getLogger().log(Level.WARNING, "Cannot find type info for class " + value.getClass()); } else { switch (typeInfo.getInclude()) { case PROPERTY: // type info is included as a property of the object serializeObject(writer, value, ctx, ignoredProperties, identityInfo, idWriter, typeInfo.getPropertyName(), typeInformation); return; case WRAPPER_OBJECT: // type info is included in a wrapper object that contains only one property. The name of this property is the type // info and the value the object writer.beginObject(); writer.name(typeInformation); serializeObject(writer, value, ctx, ignoredProperties, identityInfo, idWriter); writer.endObject(); return; case WRAPPER_ARRAY: // type info is included in a wrapper array that contains two elements. First one is the type // info and the second one the object writer.beginArray(); writer.value(typeInformation); serializeObject(writer, value, ctx, ignoredProperties, identityInfo, idWriter); writer.endArray(); return; default: ctx.getLogger().log(Level.SEVERE, "JsonTypeInfo.As." + typeInfo.getInclude() + " is not supported"); } } } 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<String> ignoredProperties, IdentitySerializationInfo identityInfo, ObjectIdSerializer<?> idWriter) { serializeObject(writer, value, ctx, ignoredProperties, identityInfo, idWriter, null, null); } /** * 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 */ protected void serializeObject(JsonWriter writer, T value, JsonSerializationContext ctx, Set<String> 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); } serializeProperties(writer, value, ctx, ignoredProperties, identityInfo); writer.endObject(); } private void serializeProperties(JsonWriter writer, T value, JsonSerializationContext ctx, Set<String> ignoredProperties, IdentitySerializationInfo identityInfo) { for (BeanPropertySerializer<T, ?> propertySerializer : serializers) { if ((null == identityInfo || !identityInfo.isProperty() || !identityInfo.getPropertyName().equals(propertySerializer.getPropertyName())) && !ignoredProperties.contains(propertySerializer.getPropertyName())) { propertySerializer.serializePropertyName(writer, value, ctx); propertySerializer.serialize(writer, value, ctx); } } if (null != anyGetterPropertySerializer) { anyGetterPropertySerializer.serialize(writer, value, ctx); } } }