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

io.katharsis.jackson.serializer.RelationshipContainerSerializer Maven / Gradle / Ivy

There is a newer version: 3.0.2
Show newest version
package io.katharsis.jackson.serializer;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import io.katharsis.jackson.exception.JsonSerializationException;
import io.katharsis.request.path.PathBuilder;
import io.katharsis.resource.field.ResourceField;
import io.katharsis.resource.registry.RegistryEntry;
import io.katharsis.resource.registry.ResourceRegistry;
import io.katharsis.response.ContainerType;
import io.katharsis.response.LinkageContainer;
import io.katharsis.response.RelationshipContainer;
import io.katharsis.utils.ClassUtils;
import io.katharsis.utils.Generics;
import io.katharsis.utils.PropertyUtils;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

/**
 * Serializes a relationship inside of top-level links object
 * Top-level Links.
 *
 * @see RelationshipContainer
 */
public class RelationshipContainerSerializer extends JsonSerializer {

    private static final String SELF_FIELD_NAME = "self";
    private static final String RELATED_FIELD_NAME = "related";
    private static final String DATA_FIELD_NAME = "data";
    private static final String LINKS_FIELD_NAME = "links";

    private final ResourceRegistry resourceRegistry;

    public RelationshipContainerSerializer(ResourceRegistry resourceRegistry) {
        this.resourceRegistry = resourceRegistry;
    }

    @Override
    public void serialize(RelationshipContainer relationshipContainer, JsonGenerator gen, SerializerProvider provider)
            throws IOException {
        gen.writeStartObject();
        writeLinks(relationshipContainer, gen);
        if ((!relationshipContainer.getRelationshipField().isLazy() &&
                !relationshipContainer.getDataLinksContainer().getContainerType().equals(ContainerType.INCLUDED_NESTED)) ||
                relationshipContainer.isForceInclusion()) {
            writeLinkage(relationshipContainer, gen);
        }
        gen.writeEndObject();
    }

    private void writeLinks(RelationshipContainer relationshipContainer, JsonGenerator gen) throws IOException {
        gen.writeFieldName(LINKS_FIELD_NAME);
        gen.writeStartObject();
        writeLink(relationshipContainer, gen, SELF_FIELD_NAME, true);
        writeLink(relationshipContainer, gen, RELATED_FIELD_NAME, false);
        gen.writeEndObject();
    }

    private void writeLink(RelationshipContainer relationshipContainer, JsonGenerator gen, String fieldName,
                           boolean addLinks) throws IOException {
        Object data = relationshipContainer.getDataLinksContainer().getData();
        Class sourceClass = data.getClass();
        String resourceUrl = resourceRegistry.getResourceUrl(sourceClass);
        RegistryEntry entry = resourceRegistry.getEntry(sourceClass);
        ResourceField idField = entry.getResourceInformation().getIdField();

        Object sourceId = PropertyUtils.getProperty(data, idField.getUnderlyingName());
        String url = resourceUrl + "/" + sourceId + (addLinks ? "/" + PathBuilder.RELATIONSHIP_MARK + "/" : "/")
                + relationshipContainer.getRelationshipField().getJsonName();
        gen.writeStringField(fieldName, url);
    }

    /**
     * Here it is needed to check actual generic type of a class. To achieve that {@code Class::getType} method cannot
     * be used because of type erasure.
     *
     * @param relationshipContainer
     * @param gen
     * @throws IOException
     */
    private void writeLinkage(RelationshipContainer relationshipContainer, JsonGenerator gen) throws IOException {
        Class baseClass = relationshipContainer.getRelationshipField().getType();
        Class relationshipClass = Generics
                .getResourceClass(relationshipContainer.getRelationshipField().getGenericType(), baseClass);
        RegistryEntry relationshipEntry = resourceRegistry.getEntry(relationshipClass);

        gen.writeFieldName(DATA_FIELD_NAME);
        writeLinkageField(relationshipContainer, gen, baseClass, relationshipEntry);
    }

    private void writeLinkageField(RelationshipContainer relationshipContainer, JsonGenerator gen, Class baseClass,
                                   RegistryEntry relationshipEntry)
            throws IOException {
        try {
            if (Iterable.class.isAssignableFrom(baseClass)) {
                writeToManyLinkage(relationshipContainer, gen, relationshipEntry);
            } else {
                writeToOneLinkage(relationshipContainer, gen, relationshipEntry);
            }
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new JsonSerializationException("Error writing linkage field");
        }
    }

    private static void writeToManyLinkage(RelationshipContainer relationshipContainer, JsonGenerator gen,
                                           RegistryEntry relationshipEntry)
            throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        ResourceField relationshipField = relationshipContainer.getRelationshipField();
        Object targetDataObj = PropertyUtils
                .getProperty(relationshipContainer.getDataLinksContainer().getData(), relationshipField.getUnderlyingName());

        gen.writeStartArray();
        if (targetDataObj != null) {
            for (Object objectItem : (Iterable) targetDataObj) {
                Class objectItemClass = ClassUtils.getJsonApiResourceClass(objectItem);
                gen.writeObject(new LinkageContainer(objectItem, objectItemClass, relationshipEntry));
            }
        }
        gen.writeEndArray();
    }

    private static void writeToOneLinkage(RelationshipContainer relationshipContainer, JsonGenerator gen,
                                          RegistryEntry relationshipEntry)
            throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        ResourceField relationshipField = relationshipContainer.getRelationshipField();
        Object targetDataObj = PropertyUtils.getProperty(relationshipContainer.getDataLinksContainer().getData(), relationshipField.getUnderlyingName());
        if (targetDataObj == null) {
            gen.writeObject(null);
        } else {
            Class targetDataObjClass = ClassUtils.getJsonApiResourceClass(targetDataObj);
            gen.writeObject(new LinkageContainer(targetDataObj, targetDataObjClass, relationshipEntry));
        }
    }

    public Class handledType() {
        return RelationshipContainer.class;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy