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

net.optionfactory.hj.jackson.JacksonJsonDriver Maven / Gradle / Ivy

package net.optionfactory.hj.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.io.SegmentedStringWriter;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import net.optionfactory.hj.JsonDriver;
import net.optionfactory.hj.JsonMappingException;
import net.optionfactory.hj.TypeDescriptor;
import net.optionfactory.hj.jackson.reflection.ResolvableType;

/**
 *
 * @author rferranti
 */
public class JacksonJsonDriver implements JsonDriver {

    private final ObjectMapper mapper;

    public JacksonJsonDriver(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public String serialize(Object value, TypeDescriptor type) {
        try {
            return writeValueAsString(value, type.as(JavaType.class));
        } catch (JsonProcessingException ex) {
            throw new JsonMappingException(ex);
        }
    }

    @Override
    public Object deserialize(String value, TypeDescriptor type) {
        try {
            return mapper.readValue(value, type.as(JavaType.class));
        } catch (IOException ex) {
            throw new JsonMappingException(ex);
        }
    }

    @Override
    public JacksonTypeDescriptor fieldType(Field field, Class context) {        
        final ResolvableType rt = ResolvableType.forField(field, context);
        return new JacksonTypeDescriptor(resolvableTypeToJavaType(rt));
    }
    
    
    
    private JavaType resolvableTypeToJavaType(ResolvableType source){
        final JavaType[] generics = Arrays.stream(source.getGenerics()).map(this::resolvableTypeToJavaType).toArray(n -> new JavaType[n]);
        return mapper.getTypeFactory().constructParametricType(source.resolve(), generics);
    }

    /**
     * Missing overload in ObjectMapper using a JavaType and enabling to specify generics context 
     * in which the value has to be serialized.
     */
    private String writeValueAsString(Object value, JavaType type) throws JsonProcessingException {
        // alas, we have to pull the recycler directly here...
        SegmentedStringWriter sw = new SegmentedStringWriter(mapper.getFactory()._getBufferRecycler());
        try {
            JsonGenerator g = mapper.getFactory().createGenerator(sw);
            SerializationConfig cfg = mapper.getSerializationConfig();
            cfg.initialize(g); // since 2.5
            if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
                _configAndWriteCloseable(g, value, cfg);
                return sw.getAndClear();
            }
            boolean closed = false;
            try {
                _serializerProvider(cfg).serializeValue(g, value, type);
                closed = true;
                g.close();
                return sw.getAndClear();
            } finally {
                /* won't try to close twice; also, must catch exception (so it 
                 * will not mask exception that is pending)
                 */
                if (!closed) {
                    /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of
                     * structures, which typically causes more damage.
                     */
                    g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
                    try {
                        g.close();
                    } catch (IOException ioe) {
                    }
                }
            }

        } catch (JsonProcessingException e) { // to support [JACKSON-758]
            throw e;
        } catch (IOException e) { // shouldn't really happen, but is declared as possibility so:
            throw com.fasterxml.jackson.databind.JsonMappingException.fromUnexpectedIOE(e);
        }
    }
    
    // duplicated from ObjectMapper for visibility reasons.
    private void _configAndWriteCloseable(JsonGenerator g, Object value, SerializationConfig cfg)
        throws IOException
    {
        Closeable toClose = (Closeable) value;
        try {
            _serializerProvider(cfg).serializeValue(g, value);
            JsonGenerator tmpGen = g;
            g = null;
            tmpGen.close();
            Closeable tmpToClose = toClose;
            toClose = null;
            tmpToClose.close();
        } finally {
            // Need to close both generator and value, as long as they haven't yet been closed
            if (g != null) {
                // 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of
                //    structures, which typically causes more damage.
                g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
                try {
                    g.close();
                } catch (IOException ioe) { }
            }
            if (toClose != null) {
                try {
                    toClose.close();
                } catch (IOException ioe) { }
            }
        }
    }    
    
    // duplicated from ObjectMapper for visibility reasons.
    private DefaultSerializerProvider _serializerProvider(SerializationConfig cfg) {
        return ((DefaultSerializerProvider) mapper.getSerializerProvider()).createInstance(cfg, mapper.getSerializerFactory());
    }    


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy