0

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.

Col
  • 381
  • 1
  • 7
  • 13

1 Answers1

3

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);
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49
  • I was hoping to avoid specific type information in the json. Ideally I would have generic type information such as `{"type":"setPower","power":30}`. Then `CompanyX` class would deserialize it to a `CompanyXSetPower` object and `CompanyY` class would deserialize it to a `CompanyYSetPower` object. Obviously the contract is that subclasses don't add any new fields but only utilise the fields of the superclass. – Col Oct 31 '18 at 20:34
  • For future viewers of this question it is not possible to do what I was hoping to do. You need to be explicit in which subclass is to be instantiated as per this answer. – Col Nov 06 '18 at 22:39