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

com.github.davidmoten.odata.client.Serializer Maven / Gradle / Ivy

package com.github.davidmoten.odata.client;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.InjectableValues.Std;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.github.davidmoten.guavamini.annotations.VisibleForTesting;
import com.github.davidmoten.odata.client.internal.ChangedFields;
import com.github.davidmoten.odata.client.internal.RequestHelper;
import com.github.davidmoten.odata.client.internal.UnmappedFields;

public final class Serializer {

    // must instantiate this before MAPPER_*
    private static final JacksonAnnotationIntrospector IGNORE_JSON_INCLUDE_ANNOTATION = new JacksonAnnotationIntrospector() {

        private static final long serialVersionUID = 4940526677740939988L;

        @Override
        protected  A _findAnnotation(final Annotated annotated,
                final Class annoClass) {
            if (!annotated.hasAnnotation(JsonInclude.class)) {
                return super._findAnnotation(annotated, annoClass);
            } else {
                return null;
            }
        }
    };
    
    private static final ObjectMapper MAPPER_EXCLUDE_NULLS = createObjectMapper(false);
    private static final ObjectMapper MAPPER_INCLUDE_NULLS = createObjectMapper(true);

    public static final Serializer INSTANCE = new Serializer();

    private Serializer() {
        // prevent instantiation
    }
    
    @VisibleForTesting
    static ObjectMapper createObjectMapper(boolean includeNulls) {
        return new ObjectMapper() //
                .setAnnotationIntrospector(IGNORE_JSON_INCLUDE_ANNOTATION) //
                .registerModule(new Jdk8Module()) //
                .registerModule(new JavaTimeModule())
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) //
                .setSerializationInclusion(includeNulls ? Include.ALWAYS : Include.NON_NULL) //
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) //
                
                // StdDateFormat is ISO8601 since jackson 2.9
                .setDateFormat(new StdDateFormat().withColonInTimeZone(true));
    }

    public  T deserialize(String text, Class cls, ContextPath contextPath,
            boolean addKeysToContextPath) {
        try {
            if (contextPath != null) {
                ObjectMapper m = MAPPER_EXCLUDE_NULLS.copy();
                Std iv = new InjectableValues.Std() //
                        .addValue(ContextPath.class, contextPath) //
                        .addValue(ChangedFields.class, new ChangedFields()) //
                        .addValue(UnmappedFields.class, new UnmappedFields());
                m.setInjectableValues(iv);
                T t = m.readValue(text, cls);
                if (t instanceof ODataType) {
                    ((ODataType) t).postInject(addKeysToContextPath);
                }
                return t;
            } else {
                return MAPPER_EXCLUDE_NULLS.readValue(text, cls);
            }
        } catch (IOException e) {
            System.out.println(text);
            throw new RuntimeException(e);
        }
    }

    public  T deserializeWithParametricType(String text, Class cls,
            Class parametricTypeClass, ContextPath contextPath,
            boolean addKeysToContextPath) {
        try {
            ObjectMapper m = MAPPER_EXCLUDE_NULLS.copy();
            JavaType type = m.getTypeFactory().constructParametricType(cls,
                    parametricTypeClass);
            if (contextPath != null) {
                Std iv = new InjectableValues.Std() //
                        .addValue(ContextPath.class, contextPath) //
                        .addValue(ChangedFields.class, new ChangedFields()) //
                        .addValue(UnmappedFields.class, new UnmappedFields());
                m.setInjectableValues(iv);
                T t = m.readValue(text, type);
                if (t instanceof ODataType) {
                    ((ODataType) t).postInject(addKeysToContextPath);
                }
                return t;
            } else {
                return m.readValue(text, type);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public  T deserialize(String text, Class cls) {
        return deserialize(text, cls, null, false);
    }

    public Optional getODataType(String text) {
        try {
            if (text == null) {
                return Optional.empty();
            }
            ObjectNode node = new ObjectMapper().readValue(text, ObjectNode.class);
            return Optional.ofNullable(node.get("@odata.type")).map(JsonNode::asText);
        } catch (IOException e) {
            return Optional.empty();
        }
    }

    public String serialize(Object entity) {
        try {
            return MAPPER_EXCLUDE_NULLS.writeValueAsString(entity);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    
    public String serializePrettyPrint(Object entity) {
        try {
            return MAPPER_EXCLUDE_NULLS.writerWithDefaultPrettyPrinter().writeValueAsString(entity);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public  String serializeChangesOnlyPrettyPrint(T entity) {
        return serializeChangesOnly(entity, true);
    }
    
    public  String serializeChangesOnly(T entity) {
        return serializeChangesOnly(entity, false);
    }
    
    public  String serializeChangesOnly(T entity, boolean prettyPrint) {
        try {
            ObjectMapper m = MAPPER_INCLUDE_NULLS;
            final String s;
            if (prettyPrint) {
                s = m.writerWithDefaultPrettyPrinter().writeValueAsString(entity);    
            } else {
                s = m.writeValueAsString(entity);
            }
            
            JsonNode tree = m.readTree(s);
            ObjectNode o = (ObjectNode) tree;
            ChangedFields cf = entity.getChangedFields();
            List list = new ArrayList<>();
            Iterator it = o.fieldNames();
            while (it.hasNext()) {
                String name = it.next();
                if (!cf.contains(name) && !name.equals("@odata.type")) {
                    list.add(name);
                }
            }
            o.remove(list);
            return o.toString();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public  CollectionPage deserializeCollectionPage(String json, Class cls,
            ContextPath contextPath, SchemaInfo schemaInfo, List requestHeaders) {
        CollectionInfo c = deserializeToCollection(json, cls, contextPath, schemaInfo);
        return new CollectionPage(contextPath, cls, c.list, c.nextLink, schemaInfo,
                requestHeaders);
    }

    private  CollectionInfo deserializeToCollection(String json, Class cls,
            ContextPath contextPath, SchemaInfo schemaInfo) {
        try {
            ObjectMapper m = MAPPER_EXCLUDE_NULLS;
            ObjectNode o = m.readValue(json, ObjectNode.class);
            List list = new ArrayList();
            for (JsonNode item : o.get("value")) {
                String text = m.writeValueAsString(item);
                Class subClass = RequestHelper.getSubClass(contextPath, schemaInfo,
                        cls, text);
                list.add(deserialize(text, subClass, contextPath, true));
            }
            // TODO support relative urls using odata.context if present
            Optional nextLink = Optional.ofNullable(o.get("@odata.nextLink"))
                    .map(JsonNode::asText);
            return new CollectionInfo(list, nextLink);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    private static final class CollectionInfo {

        final List list;
        final Optional nextLink;

        CollectionInfo(List list, Optional nextLink) {
            this.list = list;
            this.nextLink = nextLink;
        }
    }

    public boolean matches(String expectedJson, String actualJson) throws IOException {
        JsonNode expectedTree = MAPPER_EXCLUDE_NULLS.readTree(expectedJson);
        JsonNode textTree = MAPPER_EXCLUDE_NULLS.readTree(actualJson);
        return expectedTree.equals(textTree);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy