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

io.quarkus.devui.deployment.jsonrpc.DevUIDatabindCodec Maven / Gradle / Ivy

package io.quarkus.devui.deployment.jsonrpc;

import java.io.Closeable;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;

import io.quarkus.devui.runtime.jsonrpc.json.JsonMapper;
import io.quarkus.devui.runtime.jsonrpc.json.JsonTypeAdapter;
import io.quarkus.vertx.runtime.jackson.ByteArrayDeserializer;
import io.quarkus.vertx.runtime.jackson.ByteArraySerializer;
import io.quarkus.vertx.runtime.jackson.InstantDeserializer;
import io.quarkus.vertx.runtime.jackson.InstantSerializer;
import io.vertx.core.json.DecodeException;
import io.vertx.core.json.EncodeException;

public class DevUIDatabindCodec implements JsonMapper {
    private final ObjectMapper mapper;
    private final ObjectMapper prettyMapper;
    private final Function, ?> runtimeObjectDeserializer;
    private final Function, ?> runtimeArrayDeserializer;

    private DevUIDatabindCodec(ObjectMapper mapper,
            Function, ?> runtimeObjectDeserializer,
            Function, ?> runtimeArrayDeserializer) {
        this.mapper = mapper;
        prettyMapper = mapper.copy();
        prettyMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        this.runtimeObjectDeserializer = runtimeObjectDeserializer;
        this.runtimeArrayDeserializer = runtimeArrayDeserializer;
    }

    @SuppressWarnings("unchecked")
    @Override
    public  T fromValue(Object json, Class clazz) {
        T value = mapper.convertValue(json, clazz);
        if (clazz == Object.class) {
            value = (T) adapt(value);
        }
        return value;
    }

    @Override
    public  T fromString(String str, Class clazz) throws DecodeException {
        return fromParser(createParser(str), clazz);
    }

    private JsonParser createParser(String str) {
        try {
            return mapper.getFactory().createParser(str);
        } catch (IOException e) {
            throw new DecodeException("Failed to decode:" + e.getMessage(), e);
        }
    }

    @SuppressWarnings("unchecked")
    private  T fromParser(JsonParser parser, Class type) throws DecodeException {
        T value;
        JsonToken remaining;
        try {
            value = mapper.readValue(parser, type);
            remaining = parser.nextToken();
        } catch (Exception e) {
            throw new DecodeException("Failed to decode:" + e.getMessage(), e);
        } finally {
            close(parser);
        }
        if (remaining != null) {
            throw new DecodeException("Unexpected trailing token");
        }
        if (type == Object.class) {
            value = (T) adapt(value);
        }
        return value;
    }

    @Override
    public String toString(Object object, boolean pretty) throws EncodeException {
        try {
            ObjectMapper theMapper = pretty ? prettyMapper : mapper;
            return theMapper.writeValueAsString(object);
        } catch (Exception e) {
            throw new EncodeException("Failed to encode as JSON: " + e.getMessage(), e);
        }
    }

    private static void close(Closeable parser) {
        try {
            parser.close();
        } catch (IOException ignore) {
        }
    }

    private Object adapt(Object o) {
        try {
            if (o instanceof List) {
                List list = (List) o;
                return runtimeArrayDeserializer.apply(list);
            } else if (o instanceof Map) {
                @SuppressWarnings("unchecked")
                Map map = (Map) o;
                return runtimeObjectDeserializer.apply(map);
            }
            return o;
        } catch (Exception e) {
            throw new DecodeException("Failed to decode: " + e.getMessage());
        }
    }

    public static final class Factory implements JsonMapper.Factory {
        @Override
        public JsonMapper create(JsonTypeAdapter> jsonObjectAdapter,
                JsonTypeAdapter> jsonArrayAdapter, JsonTypeAdapter bufferAdapter) {
            // We want our own mapper, separate from the user-configured one.
            ObjectMapper mapper = new ObjectMapper();

            // Non-standard JSON but we allow C style comments in our JSON
            mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

            SimpleModule module = new SimpleModule("vertx-module-common");
            module.addSerializer(Instant.class, new InstantSerializer());
            module.addDeserializer(Instant.class, new InstantDeserializer());
            module.addSerializer(byte[].class, new ByteArraySerializer());
            module.addDeserializer(byte[].class, new ByteArrayDeserializer());
            mapper.registerModule(module);

            SimpleModule runtimeModule = new SimpleModule("vertx-module-runtime");
            addAdapterToObject(runtimeModule, jsonObjectAdapter);
            addAdapterToObject(runtimeModule, jsonArrayAdapter);
            addAdapterToString(runtimeModule, bufferAdapter);
            mapper.registerModule(runtimeModule);

            return new DevUIDatabindCodec(mapper, jsonObjectAdapter.deserializer, jsonArrayAdapter.deserializer);
        }

        private static  void addAdapterToObject(SimpleModule module, JsonTypeAdapter adapter) {
            module.addSerializer(adapter.type, new JsonSerializer<>() {
                @Override
                public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
                    jgen.writeObject(adapter.serializer.apply(value));
                }
            });
        }

        private static  void addAdapterToString(SimpleModule module, JsonTypeAdapter adapter) {
            module.addSerializer(adapter.type, new JsonSerializer<>() {
                @Override
                public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
                    jgen.writeString(adapter.serializer.apply(value));
                }
            });
            module.addDeserializer(adapter.type, new JsonDeserializer() {
                @Override
                public T deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
                    return adapter.deserializer.apply(parser.getText());
                }
            });
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy