
com.launchdarkly.sdk.json.LDJackson Maven / Gradle / Ivy
Show all versions of launchdarkly-java-sdk-common Show documentation
package com.launchdarkly.sdk.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.launchdarkly.sdk.LDUser;
import com.launchdarkly.sdk.LDValue;
import java.io.IOException;
/**
* A helper class for interoperability with application code that uses
* Jackson.
*
* An application that wishes to use Jackson to serialize or deserialize classes from the SDK should
* configure its {@code ObjectMapper} instance as follows:
*
* import com.launchdarkly.sdk.json.LDJackson;
*
* ObjectMapper mapper = new ObjectMapper();
* mapper.registerModule(LDJackson.module());
*
*
* This causes Jackson to use the correct JSON representation logic (the same that would be used by
* {@link JsonSerialization}) for any types that have the SDK's {@link JsonSerializable} marker
* interface, such as {@link LDUser} and {@link LDValue}, regardless of whether they are the
* top-level object being serialized or are contained in something else such as a collection. It
* does not affect Jackson's behavior for any other classes.
*
* The current implementation is limited in its ability to handle generic types. Currently, the only
* such type defined by the SDKs is {@link com.launchdarkly.sdk.EvaluationDetail}. You can serialize
* any {@code EvaluationDetail} instance and it will represent the {@code T} value correctly, but
* when deserializing, you will always get {@code EvaluationDetail}.
*/
public class LDJackson {
private LDJackson() {}
/**
* Returns a Jackson {@code Module} that defines the correct serialization and deserialization
* behavior for all LaunchDarkly SDK objects that implement {@link JsonSerializable}.
*
* import com.launchdarkly.sdk.json.LDJackson;
*
* ObjectMapper mapper = new ObjectMapper();
* mapper.registerModule(LDJackson.module());
*
* @return a {@code Module}
*/
public static Module module() {
SimpleModule module = new SimpleModule(LDJackson.class.getName());
module.addSerializer(JsonSerializable.class, LDJacksonSerializer.INSTANCE);
for (Class> c: JsonSerialization.getDeserializableClasses()) {
@SuppressWarnings("unchecked")
Class cjs = (Class)c;
module.addDeserializer(cjs, new LDJacksonDeserializer<>(cjs));
}
return module;
}
private static class LDJacksonSerializer extends JsonSerializer {
static final LDJacksonSerializer INSTANCE = new LDJacksonSerializer();
@Override
public void serialize(JsonSerializable value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
// Jackson will not call this serializer for a null value
String json = JsonSerialization.serializeInternal(value);
gen.writeRawValue(json);
}
}
private static class LDJacksonDeserializer extends JsonDeserializer {
private final Class objectClass;
LDJacksonDeserializer(Class objectClass) {
this.objectClass = objectClass;
}
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// This implementation is inefficient because our internal Gson instance can't use Jackson's
// streaming parser directly; instead we have to read the next JSON value, convert it to a
// string, and then ask our JsonSerialization to parse it back from a string.
JsonLocation loc = p.getCurrentLocation();
TreeNode jsonTree = p.readValueAsTree();
String jsonString = jsonTree.toString();
try {
return JsonSerialization.deserialize(jsonString, objectClass);
} catch (SerializationException e) {
throw new JsonParseException(p, "invalid JSON encoding for " + objectClass.getSimpleName(), loc, e);
}
}
}
}