jaksky
jaksky

Reputation: 3405

jackson JAXB annotations - inheritance mapping at the root element

I have following Jaxb annotated class hierarchy including inheritance at the document root element:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ParentClass", propOrder = {
        "parentField"
})
public class ParentClass{
    @XmlElement(name = "ParentField")
    protected String parentField;

  getters and setters here
}

ChildAClass:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ChildAClass", propOrder = {
        "childAfield"
})
public class ChildAClass extends ParentClass{

    @XmlElement(name = "ChildAfield")
    protected String childAfield;

  getters and setters here
}

ChildBClass:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ChildBClass", propOrder = {
        "childBfield"
})
public class ChildBClass extends ParentClass{

    @XmlElement(name = "ChildBfield")
    protected String childBfield;

  getters and setters here
}

That is pretty simple class hierarchy. Than I have simple test where I serialize a ChildAClass and try to deserialize to a ParentClass. I expect propper class to be deserialized back and upcasted to ParentClass. I suppose this is a valid use case.

Serialized document looks as follows, nothing fancy here.

{
  "ChildAfield": "child A field",
  "ParentField": "parent from child A"
}

But when I try to de-serialize :

    mapper = new ObjectMapper();
    JaxbAnnotationModule jaxbAnnotationModule = new JaxbAnnotationModule();
    mapper.registerModule(jaxbAnnotationModule);

    mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
    mapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX, false);
    mapper.setSerializationInclusion(JsonInclude.Include.NON_
ParentClass parentClass = mapper.readValue(new File(PATH_TO_FILE), ParentClass.class);

I am getting following exception:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "ChildAfield" (class inheritance.model.ParentClass), not marked as ignorable (one known property: "ParentField"])
 at [Source: src/test/resources/testfiles/json/inheritance.json; line: 2, column: 19] (through reference chain: inheritance.model.ParentClass["ChildAfield"])
    at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)
    at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty( ...

I suppose that some type metadata are missing here for propper type inference and deserialization. I was looking to @JsonTypeInfo but I am restricted to JAXB. I tried @XmlElements jaxb annotation but wasn't able to make it work.

Can someone give a hint?

Thx

Upvotes: 0

Views: 1630

Answers (2)

jaksky
jaksky

Reputation: 3405

I came to following conclussion why that is not working. The inheritance structure does match "well formed document", but in case of type tagging the document would have a different root element depending on what subclass is used. It would be probably solvable by using custom XmlTypeAdapter. The following seems to me as easiest approach.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "RootClass", propOrder = {
        "parentClass"
})
public class RootClass {
    @XmlElements({
            @XmlElement(name = "ChildAClass", type = ChildAClass.class),
            @XmlElement(name = "ChildBClass", type = ChildBClass.class)
    })
    protected ParentClass parentClass;

 ... getters and setters
}



@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ParentClass", propOrder = {
        "parentField"
})
public class ParentClass{
    @XmlElement(name = "ParentField")
    protected String parentField;

 ... getters and setters
}


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ChildAClass", propOrder = {
        "childAfield"
})
public class ChildAClass extends ParentClass{

    @XmlElement(name = "ChildAfield")
    protected String childAfield;

 ... getters and setters
}


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ChildBClass", propOrder = {
        "childBfield"
})
public class ChildBClass extends ParentClass{

    @XmlElement(name = "ChildBfield")
    protected String childBfield;

 ... getters and setters
}

Upvotes: 0

StaxMan
StaxMan

Reputation: 116512

It might be easiest to try to see if @JsonTypeInfo would work, just as a test. And if it does, then figure out how to get equivalent with JAXB annotations.

This is probably a good question for Jackson user google group as well.

One thing I have noticed, for what it is worth, is that root values are problematic for lots of things. I have found it helpful to limit Root value to simple POJOs, and never use a List, Map, or even polymorphic POJO there. The reason for this is that Java Type Erasure has much less effect on other values that are referred via properties and then have full generic type available. Restructuring here may or may not be possible, but I mention this since it sometimes resolves the issue at hand.

Upvotes: 1

Related Questions