io.quarkus.vertx.runtime.jackson.QuarkusJacksonJsonCodec Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quarkus-vertx Show documentation
Show all versions of quarkus-vertx Show documentation
Write reactive applications with the Vert.x API
package io.quarkus.vertx.runtime.jackson;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import io.netty.buffer.ByteBufInputStream;
import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.DecodeException;
import io.vertx.core.json.EncodeException;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.spi.json.JsonCodec;
/**
* The functionality of this class is copied almost verbatim from {@code io.vertx.core.json.jackson.DatabindCodec}.
* The difference is that this class obtains the ObjectMapper from Arc in order to inherit the
* user-customized ObjectMapper.
*/
class QuarkusJacksonJsonCodec implements JsonCodec {
private static final ObjectMapper mapper;
// we don't want to create this unless it's absolutely necessary (and it rarely is)
private static volatile ObjectMapper prettyMapper;
static {
ArcContainer container = Arc.container();
if (container == null) {
// this can happen in QuarkusUnitTest
mapper = new ObjectMapper();
} else {
ObjectMapper managedMapper = container.instance(ObjectMapper.class).get();
if (managedMapper == null) {
// TODO: is this too heavy-handed? It should never happen but even if it does, it's a mostly recoverable state
throw new IllegalStateException("There was no ObjectMapper bean configured");
}
// We don't want to change settings the settings of the User configured ObjectMapper,
// but we do want to inherit all the user's custom settings, so we copy the ObjectMapper.
// Theoretically we could have checked to see if each of the settings
// we want to apply is already applied, but in practice it doesn't make sense
// as at the very least InstantSerializer and InstantDeserializer will be different from those provided by the
// (always included with quarkus-jackson) JavaTimeModule.
mapper = managedMapper.copy();
}
// Non-standard JSON but we allow C style comments in our JSON
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
SimpleModule module = new SimpleModule("vertx-module");
// custom types
module.addSerializer(JsonObject.class, new JsonObjectSerializer());
module.addSerializer(JsonArray.class, new JsonArraySerializer());
// we have 2 extensions: RFC-7493
module.addSerializer(Instant.class, new InstantSerializer());
module.addDeserializer(Instant.class, new InstantDeserializer());
module.addSerializer(byte[].class, new ByteArraySerializer());
module.addDeserializer(byte[].class, new ByteArrayDeserializer());
module.addSerializer(Buffer.class, new BufferSerializer());
module.addDeserializer(Buffer.class, new BufferDeserializer());
mapper.registerModule(module);
}
private ObjectMapper prettyMapper() {
if (prettyMapper == null) {
prettyMapper = mapper.copy();
prettyMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
}
return prettyMapper;
}
@SuppressWarnings("unchecked")
@Override
public T fromValue(Object json, Class clazz) {
T value = QuarkusJacksonJsonCodec.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);
}
@Override
public T fromBuffer(Buffer buf, Class clazz) throws DecodeException {
return fromParser(createParser(buf), clazz);
}
public static JsonParser createParser(Buffer buf) {
try {
return QuarkusJacksonJsonCodec.mapper.getFactory()
.createParser((InputStream) new ByteBufInputStream(buf.getByteBuf()));
} catch (IOException e) {
throw new DecodeException("Failed to decode:" + e.getMessage(), e);
}
}
public static JsonParser createParser(String str) {
try {
return QuarkusJacksonJsonCodec.mapper.getFactory().createParser(str);
} catch (IOException e) {
throw new DecodeException("Failed to decode:" + e.getMessage(), e);
}
}
@SuppressWarnings("unchecked")
public static T fromParser(JsonParser parser, Class type) throws DecodeException {
T value;
JsonToken remaining;
try {
value = QuarkusJacksonJsonCodec.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 mapper = pretty ? prettyMapper() : QuarkusJacksonJsonCodec.mapper;
return mapper.writeValueAsString(object);
} catch (Exception e) {
throw new EncodeException("Failed to encode as JSON: " + e.getMessage(), e);
}
}
@Override
public Buffer toBuffer(Object object, boolean pretty) throws EncodeException {
try {
ObjectMapper mapper = pretty ? prettyMapper() : QuarkusJacksonJsonCodec.mapper;
return Buffer.buffer(mapper.writeValueAsBytes(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) {
}
}
@SuppressWarnings("rawtypes")
private static Object adapt(Object o) {
try {
if (o instanceof List) {
List list = (List) o;
return new JsonArray(list);
} else if (o instanceof Map) {
@SuppressWarnings("unchecked")
Map map = (Map) o;
return new JsonObject(map);
}
return o;
} catch (Exception e) {
throw new DecodeException("Failed to decode: " + e.getMessage());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy