I'm trying to create a generic Jackon polymorphic serializer that is able to serialize and deserialize to and from JSON with this format including the fqcn of the class of the object:
{
"fqcn": "full qualified class name of the object",
"data": "serialized object"
}
This wrapper should be applied to any object, so for example this will be the JSON representation of a HashMap> object:
{
"fqcn": "java.util.HashMap",
"data": {
"key1": {
"fqcn": "java.util.ArrayList",
"data": [
{
"fqcn": "java.lang.String",
"data": "value1"
},
{
"fqcn": "java.lang.String",
"data": "value2"
}
]
},
"key2": {
...
}
}
}
I could use a MixIn annotation all objects with #JsonTypeInfo
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_OBJECT)
public interface ObjMixin {
}
---
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Object.class, ObjMixin.class);
However, the format does not match with the required format: {"fqcn": ..., "data": ...}
I've also tried to register a StdConverter to convert any objects to a wrapper like this:
public class ObjectWrapper {
private String fqcn;
private Object data;
public ObjectWrapper(Object obj) {
this.fqcn = obj.getClass.getCanonicalName();
this.data = obj;
}
}
However it is not possible to create a StdDelegatingSerializer for Object.class.
With a custom StdSerializer like the following I am getting StackOverflowError:
#Override
public void serialize(Object obj, JsonGenerator jsonGen, SerializerProvider serializerProvider) throws IOException {
jsonGen.writeStartObject();
jsonGen.writeStringField("fqcn", obj.getClass().getCanonicalName());
jsonGen.writeFieldName("data");
if (obj instanceof Iterable) {
jsonGen.writeStartArray();
// Recursive serialization of all elements in the iterable
jsonGen.writeEndArray();
} else if (obj instanceof Map) {
jsonGen.writeStartObject();
// Recursive serialization of all elements in the map
jsonGen.writeEndObject();
} else {
// Infinite recursion here because I'm defining this serializer for Object.class
serializerProvider.defaultSerializeValue(obj, jsonGen);
}
}
Does anyone know any other solution to be able to achieve this?
You could use a custom serializer and custom serializer provider to wrap every object you want to serialize into this wrapper object (EDIT: that did not work recusrively, updated the code to not use the wrapper object but write the fields instead):
public class FQCNTest {
#Test
public void doTest() throws JsonProcessingException {
final ObjectMapper om = getObjectMapper();
final Object obj = getTestObject();
final String json = om.writeValueAsString(obj);
System.out.println(json); // {"fqcn":"java.util.HashMap","data":{"k":{"fqcn":"java.lang.String","data":"v"}}}
final Object obj2 = getTestValue();
final String json2 = om.writeValueAsString(obj2);
System.out.println(json2); // {"fcqn":"java.lang.String","data":"hello"}
final Object obj3 = null;
final String json3 = om.writeValueAsString(obj3);
System.out.println(json3); // null
}
private ObjectMapper getObjectMapper() {
final ObjectMapper mapper = new ObjectMapper();
final SerializerProvider sp = mapper.getSerializerProviderInstance();
mapper.setSerializerProvider(new CustomSerializerProvider(sp, mapper.getSerializerFactory()));
return mapper;
}
private Object getTestObject() {
final HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("k", "v");
return hashMap;
}
private Object getTestValue() {
return "hello";
}
}
class CustomSerializerProvider extends DefaultSerializerProvider {
private final SerializerProvider defaultInstance;
protected CustomSerializerProvider(final SerializerProvider defaultInstance, final SerializerFactory f) {
super(defaultInstance, defaultInstance.getConfig(), f);
this.defaultInstance = defaultInstance;
}
#Override
public WritableObjectId findObjectId(final Object forPojo, final ObjectIdGenerator<?> generatorType) {
return defaultInstance.findObjectId(forPojo, generatorType);
}
#Override
public JsonSerializer<Object> serializerInstance(final Annotated annotated, final Object serDef) throws JsonMappingException {
return new CustomSerializer();
}
#Override
public Object includeFilterInstance(final BeanPropertyDefinition forProperty, final Class<?> filterClass) {
try {
return defaultInstance.includeFilterInstance(forProperty, filterClass);
} catch (final JsonMappingException e) {
throw new RuntimeException(e);
}
}
#Override
public boolean includeFilterSuppressNulls(final Object filter) throws JsonMappingException {
return defaultInstance.includeFilterSuppressNulls(filter);
}
#Override
public DefaultSerializerProvider createInstance(final SerializationConfig config, final SerializerFactory jsf) {
return this;
}
#Override
public void serializeValue(final JsonGenerator gen, final Object value) throws IOException {
new CustomSerializer().serialize(value, gen, this);
}
#Override
public void serializeValue(final JsonGenerator gen, final Object value, final JavaType rootType) throws IOException {
super.serializeValue(gen, value, rootType);
}
#Override
public void serializeValue(final JsonGenerator gen, final Object value, final JavaType rootType, final JsonSerializer<Object> ser) throws IOException {
super.serializeValue(gen, value, rootType, ser);
}
}
class CustomSerializer extends StdSerializer<Object> {
protected CustomSerializer() {
super(Object.class);
}
#Override
public void serialize(final Object value, final JsonGenerator gen, final SerializerProvider provider) throws IOException {
if (value == null) {
provider.defaultSerializeValue(value, gen);
return;
}
final Class<?> clazz = value.getClass();
final JsonSerializer<Object> serForClazz = provider.findValueSerializer(clazz);
gen.writeStartObject();
gen.writeStringField("fqcn", clazz.getCanonicalName());
gen.writeFieldName("data");
if (value instanceof Iterable) {
gen.writeStartArray();
for (final Object e : ((Iterable<?>) value)) {
final JsonSerializer<Object> ser = new CustomSerializer();
ser.serialize(e, gen, provider);
}
gen.writeEndArray();
} else if (value instanceof Map) {
gen.writeStartObject();
// Recursive serialization of all elements in the map
for (final Map.Entry<?, ?> e : ((Map<?, ?>) value).entrySet()) {
final String key = e.getKey().toString(); // need to handle keys better
final Object mapValue = e.getValue();
gen.writeFieldName(key);
final JsonSerializer<Object> ser = new CustomSerializer();
ser.serialize(mapValue, gen, provider);
}
gen.writeEndObject();
} else {
serForClazz.serialize(value, gen, provider);
}
gen.writeEndObject();
}
}
Note: this code may contain too much stuff that is not necessary, I just took it far enough to make it work for the specific example. (and did not test deserialization, that may be a totally different thing)
Related
I want to create an Jackson objectmapper module, which will serialize all fields annotated with #Encrypt to a single field. However BeanPropertyWriter:serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov) only supports single field handling.
Let's assume we have an POJO:
public static class Pojo {
#Encrypt public String foo;
#Encrypt public String bar;
}
I've configured an module for my objectmapper as following:
public class CustomSimpleModule extends SimpleModule {
#Override
public void setupModule(final Module.SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new CustomBeanSerializerModifier());
}
private static class CustomBeanSerializerModifier extends BeanSerializerModifier {
#Override
public List<BeanPropertyWriter> changeProperties(final SerializationConfig config, final BeanDescription beanDesc, final List<BeanPropertyWriter> beanProperties) {
beanProperties.replaceAll(
property -> Objects.nonNull(property.getAnnotation(EncryptInLogs.class)) ? new CustomBeanPropertyWriter(property) : property);
return beanProperties;
}
}
private static class CustomBeanPropertyWriter extends BeanPropertyWriter {
protected CustomBeanPropertyWriter(final BeanPropertyWriter writer) {
super(writer);
}
#Override
public void serializeAsField(
final Object bean, final JsonGenerator jgen, final SerializerProvider prov)
throws Exception {
jgen.writeFieldName("encrypted");
jgen.writeStartObject();
super.serializeAsField(bean, jgen, prov);
jgen.writeEndObject();
}
}
}
When serializing the POJO it gives invalid JSON:
{
"encrypted": {
"foo": "foo"
},
"encrypted": {
"bar": "bar"
}
}
Is it possible to cluster the encrypted fields together, so we achieve the following JSON?
{
"encrypted": {
"foo": "foo",
"bar": "bar"
}
}
Is there a way to serialize collection and its elements unwrapped?
For example I want to serialize unwrapped all components:
class Model {
#JsonProperty
#JsonUnwrapped
Collection<Object> components;
Model(Collection<Object> components) {
this.components = components;
}
static class Component1 {
#JsonProperty
String stringValue;
Component1(String stringValue) {
this.stringValue= stringValue;
}
}
static class Component2 {
#JsonProperty
int intValue;
Component2(int intValue) {
this.intValue= intValue;
}
}
public static void main(String[] args) throws JsonProcessingException {
Model model = new Model(Arrays.asList(new Component1("something"), new Component2(42)));
String json = new ObjectMapper().writeValueAsString(model);
System.out.println(json);
}
}
Expected:
{"stringValue":"something","intValue":42}
But actual result is:
{"components":[{"stringValue":"something"},{"intValue":42}]}
Custom serializer might help:
class ModelSerializer extends JsonSerializer<Model> {
#Override
public void serialize(Model model, JsonGenerator generator, SerializerProvider serializers) throws IOException {
generator.writeStartObject();
JsonSerializer<Object> componentSerializer = serializers.findValueSerializer(getClass());
JsonSerializer<Object> unwrappingSerializer = componentSerializer.unwrappingSerializer(NameTransformer.NOP);
unwrappingSerializer.serialize(this, generator, serializers);
generator.writeEndObject();
}
}
I can't see a way to do that without custom serialization. I recommend these 2 serializers:
class ValueSerializer extends JsonSerializer<Object> {
#Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider sers) throws IOException {
for (Field field : value.getClass().getDeclaredFields()) {
try {
field.setAccessible(true);
gen.writeObjectField(field.getName(), field.get(value));
} catch (IllegalAccessException ignored) {
}
}
}
}
class ModelSerializer extends JsonSerializer<Model> {
#Override
public void serialize(Model model, JsonGenerator gen, SerializerProvider sers) throws IOException {
gen.writeStartObject();
for (Object obj : model.getComponents()) {
gen.writeObject(obj);
}
gen.writeEndObject();
}
}
Notice how we don't call writeStartObject() at ValueSerializer so no extra curly braces from here, neither from writeObjectField. On the other hand in ModelSerializer writheStartObject adds curly braces, and then we dump within them each object in components
You'd also need to annotate serializable classes to use these serializers e.g.
#JsonSerialize(using = ValueSerializer.class)
class Component1 {
#JsonSerialize(using = ValueSerializer.class)
class Component2 {
#JsonSerialize(using = ModelSerializer.class)
class Model {
Not elegant, but work code.
Sure about unique naming of key values
#JsonProperty
#JsonSerialize(using = CollectionSerializer.class)
Collection<Object> components;
static class CollectionSerializer extends JsonSerializer<Object> {
#Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeStartObject();
if (o instanceof Collection) {
Collection c = (Collection) o;
for (Object el : c) {
if (el instanceof Component1) {
jsonGenerator.writeStringField("stringValue", ((Component1) el).stringValue);
}
if (el instanceof Component2) {
jsonGenerator.writeNumberField("intValue", ((Component2) el).intValue);
}
}
}
jsonGenerator.writeEndObject();
}
}
I am trying to customise the serialisation of strings to avoid null values in the YAML file.
The code I have so far:
YAMLFactory yamlFactory = new YAMLFactory();
ObjectMapper mapper = new ObjectMapper(yamlFactory);
DefaultSerializerProvider sp = new DefaultSerializerProvider.Impl();
sp.setNullValueSerializer(new NullSerializer());
ObjectMapper m = new ObjectMapper();
mapper.setSerializerProvider(sp);
Map<String, Object> data = new HashMap<>();
data.put("aString", "test");
data.put("aNullObject", null);
data.put("anEmptyString", "");
String output = mapper.writeValueAsString(data);
System.out.println(output);
NullSerializer:
public class NullSerializer extends JsonSerializer<Object> {
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeString("");
}
}
Result:
---
aNullObject: ""
aString: "test"
anEmptyString: ""
The problem is that writeString is writing an empty string, and I'm trying to have an empty value entirely.
Desired result:
---
aNullObject:
aString: "test"
anEmptyString: ""
I tried to use jgen.writeRaw(""); but I get this error:
Caused by: java.lang.UnsupportedOperationException: Operation not supported by generator of type com.fasterxml.jackson.dataformat.yaml.YAMLGenerator
at com.fasterxml.jackson.core.JsonGenerator._reportUnsupportedOperation(JsonGenerator.java:1967)
at com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.writeRaw(YAMLGenerator.java:590)
at com.example.jackson.NullSerializer.serialize(NullSerializer.java:13)
at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeNull(SerializerProvider.java:1127)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:711)
... 7 more
For me disabling of "MINIMIZE_QUOTES" feature didn't work, still, an empty string is written. The only solution I found was to override ObjectMapper and YamlGenerator and to allow YamlGenerator to write empty raw value. And also you have to provide a custom null serializer that writes this raw value.
class YamlObjectMapper(yamlFactory: YAMLFactory) : ObjectMapper(yamlFactory) {
init {
val dS = DefaultSerializerProvider.Impl()
dS.setNullValueSerializer(NullSerializer)
setSerializerProvider(dS)
}}
class RawYAMLFactory : YAMLFactory() {
override fun _createGenerator(out: Writer?, ctxt: IOContext?): YAMLGenerator? {
val feats = _yamlGeneratorFeatures
return RawYamlGenerator(ctxt, _generatorFeatures, feats, _objectCodec, out, _version)
}}
private object NullSerializer : JsonSerializer<Any?>() {
override fun serialize(value: Any?, jgen: JsonGenerator, provider: SerializerProvider?) {
jgen.writeRaw("")
}}
private class RawYamlGenerator(ctxt: IOContext?, jsonFeatures: Int, yamlFeatures: Int,
codec: ObjectCodec, out: Writer?, version: DumperOptions.Version?)
: YAMLGenerator(ctxt, jsonFeatures, yamlFeatures, codec, out, version) {
override fun writeRaw(c: String) {
_writeContext.writeValue()
_emit(_scalarEvent("", DumperOptions.ScalarStyle.PLAIN))
}}
Based on the #Yuliia Liubchyk solution - I rewrote his code into Java.
Instance:
var yamlObjectMapper = new YamlObjectMapper(new RawYamlFactory().
enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)).
findAndRegisterModules();
class NullValueSerializer extends JsonSerializer<Object> {
#Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeRaw("");
}
}
public class RawYamlFactory extends YAMLFactory {
#Override
protected YAMLGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException {
var feats = _yamlGeneratorFeatures;
return new RawYamlGenerator(ctxt, _generatorFeatures, feats, _objectCodec, out, _version);
}
}
public class RawYamlGenerator extends YAMLGenerator {
public RawYamlGenerator(IOContext ctxt, int jsonFeatures, int yamlFeatures,
ObjectCodec codec, Writer out, DumperOptions.Version version) throws IOException {
super(ctxt, jsonFeatures, yamlFeatures, codec, out, version);
}
#Override
public void writeRaw(String text) throws IOException {
_writeContext.writeValue();
_emit(_scalarEvent("", DumperOptions.ScalarStyle.PLAIN));
}
}
public class YamlObjectMapper extends ObjectMapper {
public YamlObjectMapper(YAMLFactory jf) {
super(jf);
final DefaultSerializerProvider.Impl ds = new DefaultSerializerProvider.Impl();
ds.setNullValueSerializer(new NullValueSerializer());
setSerializerProvider(ds);
}
}
I had a similar problem but using an enum (or object) and not a string directly you can implement a serializer and decide to add or remove the """ before to serialize the information.
For example
public class Data {
private String aString;
private MyData aNullObject;
private String anEmptyString;
...
}
public class MyData {
private String value;
....
}
public class MySerializer {
#Override
public void serialize(
MyData data, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
YAMLGenerator yamlGenerator = (YAMLGenerator) jgen;
//DISABLE QUOTES
yamlGenerator.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES);
yamlGenerator.writeString(data.getValue());
//ENABLE QUOTES AGAIN
yamlGenerator.disable(YAMLGenerator.Feature.MINIMIZE_QUOTES);
}
}
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
SimpleModule module = new SimpleModule();
module.addSerializer(MyData.class, new MySerializer());
mapper.registerModule(module);
I hope to explain correctly...
I'm looking for a way to (de-)serialize a List of items without using Annotations in Jackson. Is this possible? What I'm doing up to now is trying to replace the <item>-tag with a tag telling about the item's class, but no avail. And even if this worked, I'm not sure whether Jackson would offer a way to process this tag information.
To give a better of what I'm aiming at, here's a sample:
public class JacksonTest {
private static class ListElement {
private boolean value;
// getters, setters, constructors omitted
}
#Test
public void testDeSerialization() throws Exception {
final List<ListElement> existing = Arrays.asList(new ListElement(true));
final ObjectMapper mapper = new XmlMapper();
final JavaType listJavaType = mapper.getTypeFactory().constructCollectionType(List.class, ListElement.class);
final String listString = mapper.writerFor(listJavaType).writeValueAsString(existing);
System.out.println(listString);
// "<List><item><value>true</value></item></List>"
}
}
So, the result is <List><item><value>true</value></item></List>, while I want the <item>-tag to be replaced with the (qualified) class name or offering a type-attribute.
Of course, even this would not help if there's no way in Jackson to process this class name.
Do I have reached a dead end here or is there a way to go?
You can define your own JsonSerializer (also used for XML) and add it to a JacksonXmlModule.
ToXmlGenerator has a setNextName function that allows you to override the default item name
private class MyListSerializer extends JsonSerializer<List> {
#Override
public void serialize(List list, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
for (Object obj : list) {
if (jsonGenerator instanceof ToXmlGenerator) {
ToXmlGenerator xmlGenerator = (ToXmlGenerator) jsonGenerator;
String className = obj.getClass().getSimpleName();
xmlGenerator.setNextName(new QName(className));
}
jsonGenerator.writeObject(obj);
// this is overridden at the next iteration
// and ignored at the last
jsonGenerator.writeFieldName("dummy");
}
}
#Override
public Class<List> handledType() {
return List.class;
}
}
#Test
public void testDeSerialization() throws Exception {
final List<ListElement> existing = Arrays.asList(new ListElement(true));
JacksonXmlModule module = new JacksonXmlModule();
module.addSerializer(new MyListSerializer());
final ObjectMapper mapper = new XmlMapper(module);
final JavaType listJavaType = mapper.getTypeFactory().constructCollectionType(List.class, ListElement.class);
final ObjectWriter writer = mapper.writerFor(listJavaType);
final String listString = writer.writeValueAsString(existing);
System.out.println(listString);
// "<List><ListElement><value>true</value></ListElement></List>"
}
Okay, after some tinkering and debugging with Evertude's proposal I've figured out a solution. I'm not really happy with the serialization part and honestly I don't know why I was supposed to do it this way. When debugging I've noticed that XmlGenerator::setNextName is required to be called once but does not have any effect on the next call, so I had to implement a switch there and set the field name for the next item in the loop directly.
I'ld be glad if somebody has an idea what I'm doing wrong, but at least my attempt is working for now:
#Test
public void testDeSerialization() throws Exception {
final List<ListElement> existing = Arrays.asList(new ListElement(true), new ListElement(false));
JacksonXmlModule module = new JacksonXmlModule();
module.addSerializer(new MyListSerializer());
final ObjectMapper mapper = new XmlMapper(module);
final JavaType listJavaType = mapper.getTypeFactory().constructCollectionType(List.class, ListElement.class);
final ObjectWriter writer = mapper.writerFor(listJavaType);
final String listString = writer.writeValueAsString(existing);
module.addDeserializer(List.class, new MyListDeserializer());
List<ListElement> deserialized = mapper.readValue(listString, List.class);
assertEquals(existing, deserialized); // provided there're proper hash() and equals() methods
}
private class MyListSerializer extends JsonSerializer<List> {
#Override
public void serialize(List list, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
boolean done = false;
for (Object obj : list) {
if (jsonGenerator instanceof ToXmlGenerator) {
ToXmlGenerator xmlGenerator = (ToXmlGenerator) jsonGenerator;
String className = obj.getClass().getSimpleName();
// weird switch
if (!done) xmlGenerator.setNextName(new QName(className));
else jsonGenerator.writeFieldName(className);
done = true;
}
jsonGenerator.writeObject(obj);
}
}
#Override
public Class<List> handledType() {
return List.class;
}
}
private class MyListDeserializer extends JsonDeserializer<List> {
#Override
public List deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
List<Object> items = new ArrayList<>();
JsonToken nextToken;
while ((nextToken = p.nextToken()) != JsonToken.END_OBJECT) {
String currentName = p.currentName();
try {
String className = "my.test.project.JacksonCustomSerializer$" + currentName;
Class<?> loadClass = getClass().getClassLoader().loadClass(className);
p.nextToken();
items.add(p.readValueAs(loadClass));
} catch (ClassNotFoundException e) {
// some handling
}
}
return items;
}
#Override
public Class<List> handledType() {
return List.class;
}
}
I am having trouble getting jackson to respect my custom JsonDeserializer. The situation is, I have a class MyClass that contains a list of another class, OtherClass, that is outside of my control (so I can't annotate it). This OtherClass class is an interface with multiple implementations. I don't care what the original OtherClass was, I want them to always deserialize as BasicOtherClass.
Here is what I have:
#Getter
public class MyClass {
#JsonProperty("otherclasses")
#JsonSerialize(contentUsing=OtherClassSerializer.class)
#JsonDeserialize(contentUsing=OtherClassDeserializer.class)
private List<OtherClass> otherClasses;
public MyClass(
#JsonProperty("otherclasses")
#JsonDeserialize(contentUsing=OtherClassDeserializer.class)
List<OtherClass> otherClasses) {
this.otherClass = otherClass;
}
}
public static class OtherClassSerializer extends JsonSerializer<OtherClass> {
#Override
public void serialize(OtherClass otherClass, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
gen.writeStartObject();
gen.writeStringField("name", otherClass.getName());
gen.writeStringField("value", otherClass.getValue());
gen.writeEndObject();
}
/** This method is required when default typing is enabled */
#Override
public void serializeWithType(
OtherClass otherClass, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer)
throws JsonProcessingException, IOException {
typeSer.writeTypePrefixForScalar(value, gen, OtherClass.class);
serialize(value, gen, serializers);
typeSer.writeTypeSuffixForScalar(value, gen);
}
}
public static class OtherClassDeserializer extends JsonDeserializer<Header> {
#Override
public Header deserialize(JsonParser p, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
if (p.getCurrentToken() != JsonToken.START_OBJECT) {
throw new IllegalStateException("Failed to parse OtherClass from json");
}
String name = null;
String value = null;
while (p.nextToken() != JsonToken.END_OBJECT) {
String key = p.getText();
p.nextToken();
String val = p.getText();
if (key.equals("name")) {
name = val;
} else if (key.equals("value")) {
value = val;
}
}
return new BasicOtherClass(name, value);
}
}
This is what I am trying to get to work:
ObjectMapper mapper = new ObjectMapper().enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
OtherClass otherClass = new BufferedOtherClass("name value");
MyClass myClass = new MyClass(Lists.newArrayList(otherClass));
String json = mapper.writeValueAsString(myClass);
// json == ["com.bschlenk.MyClass", {"otherclass": ["java.util.ArrayList", [["com.other.OtherClass", {"name": "name", "value", "value"}]]]}]
But when I try to read that json back into MyClass, it fails:
MyClass parsed = mapper.readValue(json, MyClass.class);
// com.fasterxml.jackson.databind.JsonMappingException:
// Can not construct instance of org.apache.http.Header, problem:
// abstract types either need to be mapped to concrete types,
// have custom deserializer,
// or be instantiated with additional type information
This works when I don't have type information enabled. However, it is other code that is serializing MyClass that I don't have control of, and it has type info on.
Is what I am trying to do even possible? Why doesn't mapper.readValue use my custom JsonDeserializer class? Is this by design?