Home > Software design >  How deserialize same xml element to different classes by element attribute with Jackson? (polymorphi
How deserialize same xml element to different classes by element attribute with Jackson? (polymorphi

Time:03-24

I have an xml file with the structure:

<actions shop_id="100500">

    <action type="refunds"> 
        <!-- properties collection for refunds -->
    </action>

    <action type="sales"> 
        <!-- another properties collection for sales -->
    </action>
    
    <action type="distributions"> 
        <!-- one more properties collection for distributions -->
    </action>

</actions>

How can I deserialize actions into different classes depending on the action type?

For example: map <action type="refunds"> to Refunds.class, map <action type="sales"> to Sales.class

CodePudding user response:

What you want is called polymorphic deserialization.
You need to extend your Refunds, Sales and Distributions classes from a common base class (let us call it Action). Put the specific properties into these subclasses and annotate them with @JacksonXmlProperty(...) as needed.

public class Refunds extends Action {
    
    @JacksonXmlProperty(localName = "a", isAttribute = false)
    private int a;

    // getters and setters (omitted here for brevity)
}


public class Sales extends Action {

    @JacksonXmlProperty(localName = "b", isAttribute = false)
    private int b;

    // getters and setters (omitted here for brevity)
}


public class Distributions extends Action {

    @JacksonXmlProperty(localName = "c", isAttribute = false)
    private int c;

    // getters and setters (omitted here for brevity)
}

The tricky part is implementing the base class. I recommend reading the article "Inheritance in Jackson", especially its section "2.2. Per-Class Annotations".
You need to give Jackson enough information about the mapping between your subclasses and the type property. (And don't be irritated by the historical annotation names beginning with Json... although you are dealing with XML.)

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(name = "refunds", value = Refunds.class),
    @JsonSubTypes.Type(name = "sales", value = Sales.class),
    @JsonSubTypes.Type(name = "distributions", value = Distributions.class)
})
public abstract class Action {

    @JacksonXmlProperty(isAttribute = true)
    private String type;
    
    // no getter/setter needed for type
}

And finally you need a root class for representing the whole <actions>...</actions> content. Its List<Action> property will receive a mixture of Refunds, Sales and Distributions objects.

@JacksonXmlRootElement(localName = "actions")
public class Actions {

    @JacksonXmlProperty(localName = "shop_id", isAttribute = true)
    private String shopId;

    @JacksonXmlElementWrapper(useWrapping = false)
    @JacksonXmlProperty(localName = "action", isAttribute = false)
    private List<Action> actions;

    // getters and setters (omitted here for brevity)
}

Using the classes above and Jackson's XmlMapper you are able to read and write XML content like this.

<actions shop_id="100500">
  <action type="refunds">
    <a>1</a>
  </action>
  <action type="sales">
    <b>2</b>
  </action>
  <action type="distributions">
    <c>3</c>
  </action>
</actions>
  • Related