JsonTypeInfo not supported when the instance is inside a Map?

954 views
Skip to first unread message

Rich MacDonald

unread,
Aug 29, 2015, 12:34:02 PM8/29/15
to jackson-user
I am finding that instances with a JsonTypeInfo annotation are not written with a @class property when the instance is inside a HashMap.


Here is my test code:

------------------------------------------------------------------------------------------------
package test;

import java.util.HashMap;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;


@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
public class JsonAttributes {
public JsonSub subAsProperty;

@JsonIgnore
public HashMap<String,Object> dynamicAttrs = new HashMap<String,Object>();

@JsonInclude
public HashMap<String,Object> explicitAttrs = new HashMap<String,Object>();

@JsonAnyGetter
public HashMap<String,Object> jsonGet(){
return dynamicAttrs;
}
@JsonAnySetter
public void jsonSet(String key, Object value){
dynamicAttrs.put(key, value);
}

}



------------------------------------------------------------------------------------------------
package test;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
public class JsonSub {
public String str;
public JsonSub() {}
public JsonSub(String string) {
str = string;
}
}




------------------------------------------------------------------------------------------------
package test;

import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;


public class Test {
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
JsonAttributes json = new JsonAttributes();
json.dynamicAttrs.put("subInDynamic", new JsonSub("sub inside anyGetter"));
json.explicitAttrs.put("subInExplicit", new JsonSub("sub inside explicit map"));
json.subAsProperty = new JsonSub("sub as property");

ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(json);
System.out.println(str);
JsonAttributes readJson = mapper.readValue(str, JsonAttributes.class); //this succeeds
//However, the subInDynamic JsonSub class is read as a HashMap
System.err.println("subInDynamic class type = "+readJson.dynamicAttrs.get("subInDynamic").getClass().getName());
}
}



------------------------------------------------------------------------------------------------
The result is:

{
"@class": "test.JsonAttributes",
"subAsProperty": {
"@class": "test.JsonSub",
"str": "sub as property"
},
"explicitAttrs": {
"subInExplicit": {
"str": "sub inside explicit map"
}
},
"subInDynamic": {
"str": "sub inside anyGetter"
}
}

And:

subInDynamic class type = java.util.LinkedHashMap


Note that the @class property for the JsonSub instance is only written when the property is explicitly of type JsonSub.
When the JsonSub instance is inside a Map, the @class property is not written.
And therefore, when the JsonAttributes is instantiated, the subInDynamic instance is now a LinkedHashMap, rather than a JsonSub.

This isn't my desired behavior.
I figured that jackson would inspect any instance it was serializing and would write the @class property if it found a JsonTypeInfo annotation.

Is there any way to get around this behavior, or have I made a mistake? TIA

Tatu Saloranta

unread,
Sep 8, 2015, 2:55:30 PM9/8/15
to jackso...@googlegroups.com
If I read the example correctly, I think your problem is due to incomplete type declaration for maps. Both explicit map and implicit 'any' map declare values to be of type `java.lang.Object`, and since this base type is used to check for @JsonTypeInfo, it is assumed values do not have type info.
The fact that some subtypes do is not considered; base type must define type info inclusion criteria.

But there should be an easy fix at least for explicit map.
You can and need to add @JsonTypeInfo for Map-valued property, like:

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
@JsonInclude
public HashMap<String,Object> explicitAttrs = new HashMap<String,Object>();

Alternatively if possible, you would use more specific base value class; this would be needed for "any" setter case.

Yet another possibility would be to enable "default typing" feature, but that would apply to all values, not just those of explicit/implicit maps you use.

-+ Tatu +-



--
You received this message because you are subscribed to the Google Groups "jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user...@googlegroups.com.
To post to this group, send email to jackso...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages