com.github.nmorel.gwtjackson.client.ser.bean.AbstractBeanJsonSerializer.java Source code

Java tutorial

Introduction

Here is the source code for com.github.nmorel.gwtjackson.client.ser.bean.AbstractBeanJsonSerializer.java

Source

/*
 * 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);
        }
    }
}