I have the following interface:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({
#JsonSubTypes.Type(value = EmptyProxy.class, name = "empty"),
... other types not included ...
})
public interface Proxy {
}
I have the following implementation:
#JsonTypeName("empty")
public static class EmptyProxy implements Proxy {
}
As you can see, it is just an empty class. I left the other (working) implementations out of this example.
I have the following container data class:
public static class Data {
#JacksonXmlProperty(localName = "name")
private String name;
#JacksonXmlProperty(localName = "proxy")
private Proxy proxy;
}
Deserializing EmptyProxy does not seem to work. For example:
final ObjectMapper mapper = new XmlMapper().registerModule(new JacksonXmlModule());
final Data data = mapper.readValue("<data><name>my-name</name><proxy><empty/></proxy></data>", Data.class);
This gives the following exeption:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of Test$EmptyProxy out of VALUE_NULL token
at [Source: java.io.StringReader#59ec2012; line: 1, column: 42] (through reference chain: Data["proxy"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:857)
Is this a bug in Jackson? FWIW, when I add a dummy field to EmptyProxy, it works.
update
I tried with JAXB only, and get the same result. Code:
public static class Data {
#XmlElement(name = "name")
private String name;
#XmlElements({
#XmlElement(type = EmptyProxy.class, name = "empty")
})
private Proxy proxy;
}
public interface Proxy {
}
#XmlType(name = "empty")
public static class EmptyProxy implements Proxy {
}
public static void main(String[] a) throws IOException {
final ObjectMapper mapper = new XmlMapper()/*.registerModule(new JacksonXmlModule())*/.registerModule(new JaxbAnnotationModule());
final Data data = mapper.readValue("<data><name>my-name</name><proxy><empty></empty></proxy></data>", Data.class);
}
I have created a bug entry for this. See http://github.com/FasterXML/jackson-dataformat-xml/issues/169.
Related
I have trouble with deserialization JSON to some of classes ChildA, ChildB and etc. that implements Basic interface in following example.
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = InstagramUser.class, name = "ChildA")
})
public interface Basic {
getName();
getCount();
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonTypeName("ChildA")
public class ChildA implements Basic { ... }
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonTypeName("ChildB")
public class ChildB implements Basic { ... }
...
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Response<E extends Basic> {
#JsonProperty("data")
private List<E> data;
public List<E> getData() {
return data;
}
public void setData(List<E> data) {
this.data = data;
}
}
// deserialization
HTTPClient.objectMapper.readValue(
response,
(Class<Response<ChildA>>)(Class<?>) Response.class
)
Exception is: com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'type' that is to contain type id (for class Basic)
Expected JSON is like this:
{
"data": [{ ... }, ...]
}
There is no property that is presented in all type objects so they are completely different. But as you can see on readValue line I know what is expected type. How to structure JsonTypeInfo and JsonSubTypes annotaions to deserialize JSON as expected class?
I kinda had the same problem as you, based in the reading here: Jackson Deserialize Abstract Classes I created my own solution, it basically consists of creating my own deserializer, the trick is to use/identify a specific property within JSON to know which instance type should be returned from deserialization, example is:
public interface Basic {
}
First Child:
public class ChildA implements Basic {
private String propertyUniqueForThisClass;
//constructor, getters and setters ommited
}
SecondChild:
public class ChildB implements Basic {
private String childBUniqueProperty;
//constructor, getters and setters ommited
}
The deserializer (BasicDeserializer.java) would be like:
public class BasicDeserializer extends StdDeserializer<Basic> {
public BasicDeserializer() {
this(null);
}
public BasicDeserializer(final Class<?> vc) {
super(vc);
}
#Override
public Basic deserialize(final JsonParser jsonParser,
final DeserializationContext deserializationContext)
throws IOException {
final JsonNode node = jsonParser.getCodec().readTree(jsonParser);
final ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
// look for propertyUniqueForThisClass property to ensure the message is of type ChildA
if (node.has("propertyUniqueForThisClass")) {
return mapper.treeToValue(node, ChildA.class);
// look for childBUniqueProperty property to ensure the message is of type ChildB
} else if (node.has("childBUniqueProperty")) {
return mapper.treeToValue(node, ChildB.class);
} else {
throw new UnsupportedOperationException(
"Not supported class type for Message implementation");
}
}
}
Finally, you'd have an utility class (BasicUtils.java):
private static final ObjectMapper MAPPER;
// following good software practices, utils can not have constructors
private BasicUtils() {}
static {
final SimpleModule module = new SimpleModule();
MAPPER = new ObjectMapper();
module.addDeserializer(Basic.class, new BasicDeserializer());
MAPPER.registerModule(module);
}
public static String buildJSONFromMessage(final Basic message)
throws JsonProcessingException {
return MAPPER.writeValueAsString(message);
}
public static Basic buildMessageFromJSON(final String jsonMessage)
throws IOException {
return MAPPER.readValue(jsonMessage, Basic.class);
}
For testing:
#Test
public void testJsonToChildA() throws IOException {
String message = "{\"propertyUniqueForThisClass\": \"ChildAValue\"}";
Basic basic = BasicUtils.buildMessageFromJSON(message);
assertNotNull(basic);
assertTrue(basic instanceof ChildA);
System.out.println(basic);
}
#Test
public void testJsonToChildB() throws IOException {
String message = "{\"childBUniqueProperty\": \"ChildBValue\"}";
Basic basic = BasicUtils.buildMessageFromJSON(message);
assertNotNull(basic);
assertTrue(basic instanceof ChildB);
System.out.println(basic);
}
The source code can be found on: https://github.com/darkstar-mx/jsondeserializer
I find not exactly solution but a workaround. I used custom response class ChildAResponse and passed it to ObjectMapper.readValue() method.
class ChildAResponse extends Response<ChildA> {}
// deserialization
HTTPClient.objectMapper.readValue(
response,
ChildAResponse.class
)
So JsonTypeInfo and JsonSubTypes annotations on the interface are no longer needed.
class Person:
#Data
public class Person<T extends Parent> implements Serializable {
private static final long serialVersionUID = 7822965786010192978L;
private static final ObjectMapper objectMapper = new ObjectMapper();
private String id;
private T people; // change (String peopleInfo) to object extends Parent after read data from database
private String peopleInfo; // change (T people) to string and save in the dataBase as an string
#SneakyThrows
public void setPeople(T people) {
this.people = people;
peopleInfo = objectMapper.writeValueAsString(people);
}
#SneakyThrows
public void setPeopleInfo(String peopleInfo) {
this.peopleInfo = peopleInfo;
if (!Strings.isNullOrEmpty(peopleInfo)) {
people = objectMapper.readValue(peopleInfo, new TypeReference<T>() {});
}
}
}
class Parent:
#Data
public class Parent implements Serializable {
private static final long serialVersionUID = 2092353331893381153L;
private String name;
}
class Child:
#Data
public class Child extends Parent {
private static final long serialVersionUID = 3318503314202792570L;
private String pocketMoney;
}
and the test function: I want to save the persnInfo to the database as String and change this string to an object people automatically after reading data from database.
#Test
public void testReadData() throws Exception {
Child child = new Child();
child.setName("_child");
child.setPocketMoney("10$");
Person<Child> person = new Person<>();
person.setId("1");
person.setPeople(child);
// assume this json was read from database
String json = person.getPeopleInfo();
System.out.println(json);
Person<Child> readPerson = new Person<>();
readPerson.setId("1");
readPerson.setPeopleInfo(json);
Child readChild = readPerson.getPeople();
System.out.println(readChild.getPocketMoney());
}
The error happened:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "pocketMoney" (class pers.test.common.objmapper.Parent), not marked as ignorable (one known property: "name"])
at [Source: {"name":"_child","pocketMoney":"10$"}; line: 1, column: 33] (through reference chain: pers.test.common.objmapper.Parent["pocketMoney"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
I have no idea to solve this, anyone can help me make it pass without changing the testReadData()' but change the modelPeople`.
Your problem is here:
new TypeReference<T>()
This doesn't do what you expect it to do. Java generics are erased at runtime; therefore the above statement is basically new TypeReference<Object>.
In other words - the fact that you declared
Person<Child> readPerson = new Person<>();
to expect Child objects is simply not sufficient!
You probably have to pass the specific class Child.class to the code that maps JSON strings back. For further information, look here.
I must to parse XML which can have two forms:
<Command><Variable/></Command>
or:
<Command>some text</Command>
How can I do this? When I try to declare both #Element and #Text in class responsible for Command parsing then exception is thrown when I try to parse XML to instance of this class.
My current version of code:
#Root(name = "Command", strict = false)
public class AppCommand {
#Element(name = "Variable", required = false)
#Getter
private Variable variable;
#Text(required = false)
#Getter
private String content;
}
And exception is: Text annotation #org.simpleframework.xml.Text(required=false, empty=, data=false) on field 'content' private java.lang.String com.example.AppCommand.content used with elements in class com.example.AppCommand
My solution (not beautiful, but works and doesn't require much work to implement):
private static class SerializerWithPreprocessor extends Persister {
public SerializerWithPreprocessor(RegistryMatcher matcher, Format format) {
super(matcher, format);
}
#Override
public <T> T read(Class<? extends T> type, String source) throws Exception {
source = source.replaceFirst("<Command (.*)>([[\\w||[+=]]&&[^<>]]+)</Command>", "<Command $1><Content>$2</Content></Command>");
return super.read(type, source);
}
}
So I just created new Serializer class. This class use regular expressions to change Text element inside Command into normal Element. Then I can use:
#Root(name = "Command", strict = false)
public class AppCommand {
#Element(name = "Variable", required = false)
#Getter
private Variable variable;
#Element(name = "Content", required = false)
#Getter
private String content;
}
and during deserialization everything works like I wanted to.
Yes, Simple can't deal with this.
Command.java:
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Text;
#Root
public class Command {
#Element(required = false, name = "Variable")
private Variable variable;
#Text(required = false)
private String text;
}
Variable.java:
class Variable {
}
SOPlayground.java:
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
public class SOPlayground {
public static void main(String[] args) throws Exception {
Serializer serializer = new Persister();
String xml1 = "<Command><Variable/></Command>";
String xml2 = "<Command>some text</Command>";
serializer.validate(Command.class, xml1);
serializer.validate(Command.class, xml2);
}
}
This does compile but it does not run:
Exception in thread "main" org.simpleframework.xml.core.TextException: Text annotation #org.simpleframework.xml.Text(data=false, required=false, empty=) on field 'text' private java.lang.String de.lhorn.so.Command.text used with elements in class de.lhorn.so.Command
It looks like can not have both #Element and #Text members.
I use Jackson 2.2.3 to serialize POJOs to JSON. Then I had the problem, that I couldn't serialize recursive structures...I solved this problem by using #JsonIdentityInfo => works great.
But, I don't want this annotation on the top of my POJO.
So my question is: Is there any other possibility to set the default behavior of my ObjectMapper to use the feature for every POJO?
So I want to transform this annotation code
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id")
to something like
ObjectMapper om = new ObjectMapper();
om.setDefaultIdentityInfo(ObjectIdGenerators.IntSequenceGenerator.class, "#id");
Any ideas?
You can achieve that using the Jackson mix-in annotations or the Jackson annotation introspector.
Here is an example showing both methods:
public class JacksonJsonIdentityInfo {
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
static class Bean {
public final String field;
public Bean(final String field) {this.field = field;}
}
static class Bean2 {
public final String field2;
public Bean2(final String field2) {this.field2 = field2;}
}
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id2")
static interface Bean2MixIn {
}
static class Bean3 {
public final String field3;
public Bean3(final String field3) {this.field3 = field3;}
}
static class MyJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {
#Override
public ObjectIdInfo findObjectIdInfo(final Annotated ann) {
if (ann.getRawType() == Bean3.class) {
return new ObjectIdInfo(
PropertyName.construct("#id3", null),
null,
ObjectIdGenerators.IntSequenceGenerator.class,
null);
}
return super.findObjectIdInfo(ann);
}
}
public static void main(String[] args) throws JsonProcessingException {
final Bean bean = new Bean("value");
final Bean2 bean2 = new Bean2("value2");
final Bean3 bean3 = new Bean3("value3");
final ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Bean2.class, Bean2MixIn.class);
mapper.setAnnotationIntrospector(new MyJacksonAnnotationIntrospector());
System.out.println(mapper.writeValueAsString(bean));
System.out.println(mapper.writeValueAsString(bean2));
System.out.println(mapper.writeValueAsString(bean3));
}
}
Output:
{"#id":1,"field":"value"}
{"#id2":1,"field2":"value2"}
{"#id3":1,"field3":"value3"}
After several months and a lot of research, I've implemented my own solution to keep my domain clear of jackson dependencies.
public class Parent {
private Child child;
public Child getChild(){return child;}
public void setChild(Child child){this.child=child;}
}
public class Child {
private Parent parent;
public Child getParent(){return parent;}
public void setParent(Parent parent){this.parent=parent;}
}
First, you have to declare each of your entities of the bidirectional relationship:
public interface BidirectionalDefinition {
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=Parent.class)
public interface ParentDef{};
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=Child.class)
public interface ChildDef{};
}
After that, the object mapper can be automatically configured:
ObjectMapper om = new ObjectMapper();
Class<?>[] definitions = BidirectionalDefinition.class.getDeclaredClasses();
for (Class<?> definition : definitions) {
om.addMixInAnnotations(definition.getAnnotation(JsonIdentityInfo.class).scope(), definition);
}
I'm currently working on a model which uses generics and is little complicated. I understand that similar questions have been answered but none of them clearly answers mine.
Here is my model:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT, property = "type")
#JsonSubTypes(
{
#Type(value = Cls2.class, name = "Cls2")
})
abstract class Cls1<T> implements Serializable
{
private T myObj;
public T getMyObj()
{
return myObj;
}
public Cls1(T obj)
{
myObj = obj;
}
#JsonTypeName("Cls2")
public static class Cls2<E extends Int1> extends Cls1<E> implements Serializable
{
public Cls2()
{
super(null);
}
}
}
#JsonTypeName("ChildContainer")
class ChildContainer extends ParentContainer<OtherBean>
{
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT, property = "type")
#JsonSubTypes(
{
#Type(value = ChildContainer.class, name = "ChildContainer")
})
class ParentContainer<T extends RootBean> implements Int1
{
}
#JsonTypeName("OtherBean")
class OtherBean extends RootBean
{
}
#JsonTypeName("RootBean")
class RootBean implements Int1
{
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT, property = "type")
#JsonSubTypes(
{
#Type(value = RootBean.class, name = "RootBean"),
#Type(value = OtherBean.class, name = "OtherBean")
})
interface Int1 extends Serializable
{
}
My goal is to serialize and deserialze using jackson as follows:
public static void main(String[] args) throws Exception
{
Cls2<ChildContainer> req = new Cls2<ChildContainer>();
File file = new File("==some-file-path==");
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(file, req);
//read it back using mapper.readValue(file, clazz) --Not sure about this
}
I get the following java.lang.StackOverflowError during the serialization:
Exception in thread "main" java.lang.StackOverflowError
at java.lang.Class.getDeclaringClass(Native Method)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:290)
at org.codehaus.jackson.map.type.TypeBindings._resolve(TypeBindings.java:221)
at org.codehaus.jackson.map.type.TypeBindings.findType(TypeBindings.java:138)
at org.codehaus.jackson.map.type.TypeFactory._fromVariable(TypeFactory.java:951)
at org.codehaus.jackson.map.type.TypeFactory._constructType(TypeFactory.java:493)
at org.codehaus.jackson.map.type.TypeFactory.findTypeParameters(TypeFactory.java:423)
at org.codehaus.jackson.map.type.TypeFactory.findTypeParameters(TypeFactory.java:395)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:299)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:290)
at org.codehaus.jackson.map.type.TypeBindings._resolve(TypeBindings.java:221)
at org.codehaus.jackson.map.type.TypeBindings.findType(TypeBindings.java:138)
at org.codehaus.jackson.map.type.TypeFactory._fromVariable(TypeFactory.java:951)
at org.codehaus.jackson.map.type.TypeFactory._constructType(TypeFactory.java:493)
at org.codehaus.jackson.map.type.TypeFactory.findTypeParameters(TypeFactory.java:423)
at org.codehaus.jackson.map.type.TypeFactory.findTypeParameters(TypeFactory.java:395)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:299)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:290)
Any help is deeply appreciated.
The stacktrace suggests an infinite recursion in type resolving of Cls2 which extends the class it is by itself nested in. This seems to be a corner case bug in Jackson (report it!). In the meanwhile, extracting Cls2 into a standalone class instead of nesting it in its superclass should solve this problem.