Col
Col

Reputation: 391

Deserializing superclass json to subclass object

My software revolves heavily around rfid devices. All of these devices have have (mostly) the same configuration options such as what power setting the antennas should transmit at. However the APIs supplied with each reader differ in the way the antenna power is set. Configuration of the readers is done on a central server and saved as a json string. I am trying to come up a way to have a common json string to configure the antenna power (for example) without it having to be customised for each API. Here's what I'm trying to achieve.

The json string for setting antenna power would be something like:

{"power":30}

My super class for configuring power:

public abstract class SetPower<T> {
    protected int power;

    public SetPower(int power) {
        this.power = power;
    }

    public abstract void configure(T rfidReader);
}

Subclass for configuring power from CompanyX's reader:

public class CompanyXSetPower extends SetPower<CompanyXReader> {

    @JsonCreator
    public CompanyXSetPower(@JsonProperty("power") int power) {
        super(power);
    }

    @Override
    public void configure(CompanyXReader reader) {
        reader.setPower((byte) power);
    }
}

Subclass for configuring power from CompanyY's reader:

public class CompanyYSetPower extends SetPower<CompanyYReader> {

    @JsonCreator
    public CompanyYSetPower(@JsonProperty("power") int power) {
        super(power);
    }

    @Override
    public void configure(CompanyYReader reader) {
        reader.setOutputPower(power);
    }
}

What's the 'best' way to deserialize generic json into a specific subclass without having to specific class details to the json string. I am ok with including the superclass details to the json string as there needs to be some way of identifying what object type the json data is for.

Upvotes: 0

Views: 3729

Answers (1)

Thomas Fritsch
Thomas Fritsch

Reputation: 10147

What you want is called deserializing JSON into polymorphic types. (see for example this question for a similar problem).

You will need to add an additional type attribute to your JSON content, so that for example {"type":"x","power":30} gets deserialized into an CompanyXSetPower object, and {"type":"y","power":30} gets deserialized into an CompanyYSetPower object.

For this to work you need to enhance your abstract base class SetPower with @JsonTypeInfo and @JsonSubTypes annotations, so that Jackson will have enough information about the mapping between JSON and your various Java classes.

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = CompanyXSetPower.class, name = "x"),
    @JsonSubTypes.Type(value = CompanyYSetPower.class, name = "y")
})
public abstract class SetPower<T> {
    protected int power;

    public SetPower(int power) {
        this.power = power;
    }

    public abstract void configure(T rfidReader);
}

The deserialization then can be simply done by:

ObjectMapper objectMapper = new ObjectMapper();
SetPower<?> setPower = objectMapper.readValue(url, SetPower.class);

Upvotes: 3

Related Questions