All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.yasson.internal.serializer.AbstractContainerSerializer Maven / Gradle / Ivy

/*
 * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

package org.eclipse.yasson.internal.serializer;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.Optional;

import jakarta.json.bind.serializer.JsonbSerializer;
import jakarta.json.bind.serializer.SerializationContext;
import jakarta.json.stream.JsonGenerator;

import org.eclipse.yasson.internal.Marshaller;
import org.eclipse.yasson.internal.ReflectionUtils;
import org.eclipse.yasson.internal.model.ClassModel;
import org.eclipse.yasson.internal.model.customization.ClassCustomizationBuilder;
import org.eclipse.yasson.internal.model.customization.ContainerCustomization;

/**
 * Base class for container serializers (list, array, etc.).
 *
 * @param  container value type
 */
public abstract class AbstractContainerSerializer extends AbstractItem implements JsonbSerializer {

    private JsonbSerializer valueSerializer;

    private Class valueClass;

    /**
     * Create instance of current item with its builder.
     *
     * @param builder {@link SerializerBuilder} used to build this instance
     */
    protected AbstractContainerSerializer(SerializerBuilder builder) {
        super(builder);
    }

    /**
     * Creates a new instance.
     *
     * @param wrapper     Item to serialize.
     * @param runtimeType Runtime type of the item.
     * @param classModel  Class model.
     */
    public AbstractContainerSerializer(CurrentItem wrapper, Type runtimeType, ClassModel classModel) {
        super(wrapper, runtimeType, classModel);
    }

    /**
     * Process container before serialization begins.
     * Does nothing by default.
     *
     * @param obj item to be serialized
     */
    protected void beforeSerialize(T obj) {
    }

    /**
     * Write start of an object or an array without a key.
     *
     * @param generator JSON format generator
     */
    protected abstract void writeStart(JsonGenerator generator);

    /**
     * Write start of an object or an array with a key.
     *
     * @param key       JSON key name.
     * @param generator JSON format generator
     */
    protected abstract void writeStart(String key, JsonGenerator generator);

    /**
     * Writes end of an object or an array.
     *
     * @param generator JSON format generator
     */
    protected void writeEnd(JsonGenerator generator) {
        generator.writeEnd();
    }

    /**
     * Serialize content of provided container.
     *
     * @param obj       container to be serialized
     * @param generator JSON format generator
     * @param ctx       JSON serialization context
     */
    protected abstract void serializeInternal(T obj, JsonGenerator generator, SerializationContext ctx);

    @Override
    public final void serialize(T obj, JsonGenerator generator, SerializationContext ctx) {
        beforeSerialize(obj);
        writeStart(generator);
        serializeInternal(obj, generator, ctx);
        writeEnd(generator);
    }

    /**
     * Serializes container object item.
     *
     * @param serializer serializer of the object
     * @param object     object to serialize
     * @param generator  json generator
     * @param ctx        context
     * @param         type of object
     */
    @SuppressWarnings("unchecked")
    protected  void serializerCaptor(JsonbSerializer serializer,
                                        X object,
                                        JsonGenerator generator,
                                        SerializationContext ctx) {
        ((JsonbSerializer) serializer).serialize(object, generator, ctx);
    }

    /**
     * Return last used serializer if last value class matches.
     *
     * @param valueClass class of the serialized object
     * @return cached serializer or null
     */
    protected JsonbSerializer getValueSerializer(Class valueClass) {
        if (valueSerializer != null && valueClass == this.valueClass) {
            return valueSerializer;
        }
        return null;
    }

    /**
     * Cache a serializer and serialized object class for next use.
     *
     * @param valueSerializer serializer
     * @param valueClass      class of serializer object
     */
    protected void addValueSerializer(JsonbSerializer valueSerializer, Class valueClass) {
        Objects.requireNonNull(valueSerializer);
        Objects.requireNonNull(valueClass);
        this.valueSerializer = valueSerializer;
        this.valueClass = valueClass;
    }

    /**
     * Serializes container object.
     *
     * @param item      container
     * @param generator json generator
     * @param ctx       context
     */
    protected void serializeItem(Object item, JsonGenerator generator, SerializationContext ctx) {
        if (item == null) {
            generator.writeNull();
            return;
        }
        Class itemClass = item.getClass();
        //Not null when generic type is present or previous item is of same type
        JsonbSerializer serializer = getValueSerializer(itemClass);

        //Raw collections + lost generic information
        if (serializer == null) {
            Type instanceValueType = getValueType(getRuntimeType());
            instanceValueType = instanceValueType.equals(Object.class) ? itemClass : instanceValueType;

            SerializerBuilder builder = new SerializerBuilder(((Marshaller) ctx).getJsonbContext());
            builder.withObjectClass(itemClass);
            builder.withWrapper(this);
            builder.withType(instanceValueType);

            if (!DefaultSerializers.isKnownType(itemClass)) {
                //Need for class level annotations + user adapters/serializers bound to type
                ClassModel classModel = ((Marshaller) ctx).getJsonbContext().getMappingContext().getOrCreateClassModel(itemClass);
                builder.withCustomization(new ContainerCustomization(classModel.getClassCustomization()));
            } else {
                //Still need to override isNillable to true with ContainerCustomization for all serializers
                //to preserve collections and array null elements
                builder.withCustomization(new ContainerCustomization(new ClassCustomizationBuilder()));
            }
            serializer = builder.build();

            //Cache last used value serializer in case of next item is the same type.
            addValueSerializer(serializer, itemClass);
        }
        serializerCaptor(serializer, item, generator, ctx);
    }

    /**
     * Value type of the container.
     *
     * @param valueType value type
     * @return raw value type
     */
    protected Type getValueType(Type valueType) {
        if (valueType instanceof ParameterizedType) {
            Optional runtimeTypeOptional = ReflectionUtils
                    .resolveOptionalType(this, ((ParameterizedType) valueType).getActualTypeArguments()[0]);
            return runtimeTypeOptional.orElse(Object.class);
        }
        return Object.class;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy