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

com.microsoft.rest.v2.serializer.AdditionalPropertiesDeserializer Maven / Gradle / Ivy

/**
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for
 * license information.
 */

package com.microsoft.rest.v2.serializer;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.microsoft.rest.v2.annotations.Beta;
import com.microsoft.rest.v2.util.TypeUtil;

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

/**
 * Custom serializer for deserializing complex types with additional properties.
 * If a complex type has a property named "additionalProperties" with serialized
 * name empty ("") of type Map<String, Object>, all extra properties on the
 * payload will be stored in this map.
 */
@Beta(since = "1.7.0")
public final class AdditionalPropertiesDeserializer extends StdDeserializer implements ResolvableDeserializer {
    /**
     * The default mapperAdapter for the current type.
     */
    private final JsonDeserializer defaultDeserializer;

    /**
     * The object mapper for default deserializations.
     */
    private final ObjectMapper mapper;

    /**
     * Creates an instance of FlatteningDeserializer.
     * @param vc handled type
     * @param defaultDeserializer the default JSON mapperAdapter
     * @param mapper the object mapper for default deserializations
     */
    protected AdditionalPropertiesDeserializer(Class vc, JsonDeserializer defaultDeserializer, ObjectMapper mapper) {
        super(vc);
        this.defaultDeserializer = defaultDeserializer;
        this.mapper = mapper;
    }

    /**
     * Gets a module wrapping this serializer as an adapter for the Jackson
     * ObjectMapper.
     *
     * @param mapper the object mapper for default deserializations
     * @return a simple module to be plugged onto Jackson ObjectMapper.
     */
    public static SimpleModule getModule(final ObjectMapper mapper) {
        SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer deserializer) {
                for (Class c : TypeUtil.getAllClasses(beanDesc.getBeanClass())) {
                    Field[] fields = c.getDeclaredFields();
                    for (Field field : fields) {
                        if ("additionalProperties".equalsIgnoreCase(field.getName())) {
                            JsonProperty property = field.getAnnotation(JsonProperty.class);
                            if (property != null && property.value().isEmpty()) {
                                return new AdditionalPropertiesDeserializer(beanDesc.getBeanClass(), deserializer, mapper);
                            }
                        }
                    }
                }
                return deserializer;
            }
        });
        return module;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
        ObjectNode root = mapper.readTree(jp);
        ObjectNode copy = root.deepCopy();

        // compare top level fields and keep only missing fields
        final Class tClass = this.defaultDeserializer.handledType();
        for (Class c : TypeUtil.getAllClasses(tClass)) {
            Field[] fields = c.getDeclaredFields();
            for (Field field : fields) {
                JsonProperty property = field.getAnnotation(JsonProperty.class);
                String key = property.value().split("((?