com.gilecode.yagson.YaGson.java Source code

Java tutorial

Introduction

Here is the source code for com.gilecode.yagson.YaGson.java

Source

/*
 * Copyright (C) 2016 Andrey Mogilev
 *
 * 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.gilecode.yagson;

import com.gilecode.yagson.refs.References;
import com.gilecode.yagson.refs.ReferencesPolicy;
import com.gilecode.yagson.types.TypeInfoPolicy;
import com.gilecode.yagson.types.TypeUtils;
import com.google.gson.*;
import com.google.gson.internal.Excluder;
import com.google.gson.internal.bind.JsonTreeWriter;
import com.google.gson.stream.JsonWriter;

import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * The main class for using YaGson. Along with {@link YaGsonBuilder}, it provides similar
 * creation and usage patterns as the basic {@link Gson}, but the references emitting and
 * the type info emitting are enabled by default. See {@link References#defaultPolicy()} and
 * {@link TypeInfoPolicy#defaultPolicy()} for the default YaGson's settings.
 * <p/>
 * If any non-default features are required, use {@link YaGsonBuilder} to create {@link YaGson}
 * instance.
 *
 * @author Andrey Mogilev
 */
public class YaGson extends Gson {

    /**
     * Constructs a Gson object with the default configuration. The default configuration has the
     * following settings:
     * <ul>
     *   <li>By default, YaGson's detects all self-references and the duplicate objects in the whole references graph
     *   of the serialized object. All such objects except the first one are emitted as link-like references, see
     *   {@link ReferencesPolicy#DUPLICATE_OBJECTS}. Although this policy may be changed to the alternative ones by
     *   {@link YaGsonBuilder#setReferencesPolicy(ReferencesPolicy)}, it is not recommended in case of arbitrary objects,
     *   as the functionality of the deserialized objects may be broken.</li>
     *   <li>By default, the type information is emitted as {@literal @type/@value} wrappers when the actual types
     *   would be lost otherwise, i.e. when the known de-serialization type is less specific than the actual class
     *   of an object or its part being serialized. Use
     *   {@link YaGsonBuilder#setTypeInfoPolicy(TypeInfoPolicy)} to change it if necessary.</li>
     *   <li>The JSON generated by <code>toJson</code> methods is in compact representation. This
     *   means that all the unneeded white-space is removed. You can change this behavior with
     *   {@link GsonBuilder#setPrettyPrinting()}. </li>
     *   <li>The generated JSON omits all the fields that are null. Note that nulls in arrays are
     *   kept as is since an array is an ordered list. Moreover, if a field is not null, but its
     *   generated JSON is empty, the field is kept. You can configure Gson to serialize null values
     *   by setting {@link GsonBuilder#serializeNulls()}.</li>
     *   <li>The default Date format is same as {@link java.text.DateFormat#DEFAULT}. This format
     *   ignores the millisecond portion of the date during serialization. You can change
     *   this by invoking {@link GsonBuilder#setDateFormat(int)} or
     *   {@link GsonBuilder#setDateFormat(String)}. </li>
     *   <li>By default, Gson ignores the {@link com.google.gson.annotations.Expose} annotation.
     *   You can enable Gson to serialize/deserialize only those fields marked with this annotation
     *   through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}. </li>
     *   <li>By default, Gson ignores the {@link com.google.gson.annotations.Since} annotation. You
     *   can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.</li>
     *   <li>The default field naming policy for the output Json is same as in Java. So, a Java class
     *   field <code>versionNumber</code> will be output as <code>&quot;versionNumber&quot;</code> in
     *   Json. The same rules are applied for mapping incoming Json to the Java classes. You can
     *   change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.</li>
     *   <li>By default, YaGson excludes <code>static</code> fields from
     *   consideration for serialization and deserialization, but serializes most of <code>transient</code>
     *   fields.
     *   You can change this behavior through
     *   {@link GsonBuilder#excludeFieldsWithModifiers(int...)}.</li>
     * </ul>
     */
    public YaGson() {
        super(Excluder.DEFAULT.forReferencesPolicy(References.defaultPolicy()), FieldNamingPolicy.IDENTITY,
                Collections.<Type, InstanceCreator<?>>emptyMap(), false, TypeInfoPolicy.defaultPolicy().isEnabled(),
                DEFAULT_JSON_NON_EXECUTABLE, !TypeInfoPolicy.defaultPolicy().isEnabled(), // disable htmlSafe if types are printed
                false, false, LongSerializationPolicy.DEFAULT, Collections.<TypeAdapterFactory>emptyList(),
                References.defaultPolicy(), TypeInfoPolicy.defaultPolicy());
    }

    protected YaGson(Excluder excluder, FieldNamingStrategy fieldNamingPolicy,
            Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
            boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
            boolean prettyPrinting, boolean serializeSpecialFloatingPointValues,
            LongSerializationPolicy longSerializationPolicy, List<TypeAdapterFactory> typeAdapterFactories,
            ReferencesPolicy referencesPolicy, TypeInfoPolicy typeInfoPolicy) {
        super(excluder, fieldNamingPolicy, instanceCreators, serializeNulls, complexMapKeySerialization,
                generateNonExecutableGson, htmlSafe, prettyPrinting, serializeSpecialFloatingPointValues,
                longSerializationPolicy, typeAdapterFactories, referencesPolicy, typeInfoPolicy);
    }

    /**
     * This method serializes the specified object into its equivalent Json representation, provided that
     * the de-serialization type is not known at this time.
     * <p/>
     * By default, the root type information is emitted, unless the object is a string, long, double or boolean, so
     * the resulting JSON string may be successfully de-serialized into the similar object using
     * {@code fromJson(str, Object.class)}. If the object will be de-serialized using some other type than
     * {@code Object.class}, use {@link #toJson(Object, Type)} instead, and the root type information will be
     * emitted only when necessary, i.e. if the de-serialization type differs from the actual one.
     * <p/>
     * If you want to write out the object to a {@link Writer}, use {@link #toJson(Object, Appendable)} instead.
     *
     * @param src the object for which Json representation is to be created
     * @return Json representation of {@code src}.
     */
    @Override
    public String toJson(Object src) {
        return toJson(src, Object.class);
    }

    /**
     * This method serializes the specified object into its equivalent Json representation, given that the
     * de-serialization type is known.
     * <p/>
     * The root type information is emitted only if necessary, i.e. if the specified de-serialization type differs from
     * the actual object's class. It is guaranteed that the resulting string may be de-serialized to the similar object
     * using {@code fromJson(json, deserializationType)}, unless some YaGson's features are disabled.
     * <p/>
     * If you want to write out the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)}
     * instead.
     *
     * @param src       the object for which JSON representation is to be created
     * @param deserializationType The type which will be used for de-serialization of the resulting JSON representation
     * @return Json representation of {@code src}
     */
    @Override
    public String toJson(Object src, Type deserializationType) {
        StringWriter writer = new StringWriter();
        toJson(src, deserializationType, writer);
        return writer.toString();
    }

    /**
     * This method serializes the specified object into its equivalent Json representation, provided that
     * the de-serialization type is not known at this time.
     * <p/>
     * By default, the root type information is emitted, unless the object is a string, long, double or boolean, so
     * the resulting JSON string may be successfully de-serialized into the similar object using
     * {@code fromJson(str, Object.class)}. If the object will be de-serialized using some other type than
     * {@code Object.class}, use {@link #toJson(Object, Type, Appendable)} instead, and the root type information
     * will be emitted only when necessary, i.e. if the de-serialization type differs from the actual one.
     *
     * @param src    the object for which Json representation is to be created
     * @param writer Writer to which the Json representation needs to be written
     * @throws JsonIOException if there was a problem writing to the writer
     * @since 1.2
     */
    @Override
    public void toJson(Object src, Appendable writer) throws JsonIOException {
        toJson(src, Object.class, writer);
    }

    /**
     * This method serializes the specified object into its equivalent Json representation, given that the
     * de-serialization type is known, and writes it to the provided {@link Appendable}.
     * <p/>
     * The root type information is emitted only if necessary, i.e. if the specified de-serialization type differs from
     * the actual object's class. It is guaranteed that the resulting JSON representation may be de-serialized to the
     * similar object using {@code fromJson(json, deserializationType)}, unless some YaGson's features are disabled.
     *
     * @param src       the object for which JSON representation is to be created
     * @param deserializationType The type which will be used for de-serialization of the resulting JSON representation
     * @param writer    Writer to which the Json representation of src needs to be written.
     * @throws JsonIOException if there was a problem writing to the writer
     * @since 1.2
     */
    @Override
    public void toJson(Object src, Type deserializationType, Appendable writer) throws JsonIOException {
        // forwards to {@link #toJson(Object, Type, JsonWriter)}
        super.toJson(src, deserializationType, writer);
    }

    /**
     * This method serializes the specified object into its equivalent Json representation, given that the
     * de-serialization type is known, and writes it to the provided {@link JsonWriter}.
     * <p/>
     * The root type information is emitted only if necessary, i.e. if the specified de-serialization type differs from
     * the actual object's class. It is guaranteed that the resulting JSON representation may be de-serialized to the
     * similar object using {@code fromJson(json, deserializationType)}, unless some YaGson's features are disabled.
     *
     * @param src       the object for which JSON representation is to be created
     * @param deserializationType The type which will be used for de-serialization of the resulting JSON representation
     * @param writer    Writer to which the Json representation of src needs to be written.
     * @throws JsonIOException if there was a problem writing to the writer
     * @since 1.2
     */
    @Override
    public void toJson(Object src, Type deserializationType, JsonWriter writer) throws JsonIOException {
        if (src == null) {
            toJson(JsonNull.INSTANCE, writer);
            return;
        }

        if (src instanceof Number && TypeUtils.isNumberType(deserializationType)
                && TypeUtils.typesDiffer(deserializationType, src.getClass())) {
            // a special case, where a user may rely on auto-cast of primitive number types
            // perform such cast here
            src = TypeUtils.convertNumber((Number) src, deserializationType);
        }

        boolean isRootTypeRequired = TypeUtils.isTypeInfoRequired(src.getClass(), deserializationType, false);

        // use the exact source class for serialization, but try to keep the type parameters if available in
        // the specified deserialization type
        Type parameterizedSrcType = TypeUtils.mergeTypes(src.getClass(), deserializationType);
        super.toJson(src, parameterizedSrcType, writer, isRootTypeRequired);
    }

    /**
     * This method serializes the specified object into its equivalent Json representation as a tree of
     * {@link JsonElement}s, provided that the de-serialization type is not known at this time.
     * <p/>
     * By default, the root type information is emitted, unless the object is a string, long, double or boolean, so
     * the resulting JSON string may be successfully de-serialized into the similar object using
     * {@code fromJson(str, Object.class)}. If the object will be de-serialized using some other type than
     * {@code Object.class}, use {@link #toJsonTree(Object, Type)} instead, and the root type information will be
     * emitted only when necessary, i.e. if the de-serialization type differs from the actual one.
     *
     * @param src the object for which Json representation is to be created
     * @return Json representation of {@code src}.
     */
    @Override
    public JsonElement toJsonTree(Object src) {
        return toJsonTree(src, Object.class);
    }

    /**
     * This method serializes the specified object into its equivalent Json representation as a tree of
     * {@link JsonElement}s, given that the de-serialization type is known.
     * <p/>
     * The root type information is emitted only if necessary, i.e. if the specified de-serialization type differs from
     * the actual object's class. It is guaranteed that the resulting string may be de-serialized to the similar object
     * using {@code fromJson(json, deserializationType)}, unless some YaGson's features are disabled.
     *
     * @param src       the object for which JSON representation is to be created
     * @param deserializationType The type which will be used for de-serialization of the resulting JSON representation
     * @return Json representation of {@code src}
     */
    @Override
    public JsonElement toJsonTree(Object src, Type deserializationType) {
        if (src == null) {
            return JsonNull.INSTANCE;
        }

        boolean isRootTypeRequired = TypeUtils.isTypeInfoRequired(src.getClass(), deserializationType, false);
        JsonTreeWriter writer = new JsonTreeWriter();
        super.toJson(src, src.getClass(), writer, isRootTypeRequired);
        return writer.get();
    }
}