简体   繁体   中英

Java Jackson deserialize interfacing enums

I have the given situtation: This is the interface I am implementing:

    @JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME
        property = "type")
    @JsonSubTypes({
        @JsonSubTypes.Type(value = MasterDevice.class, name = "COMPUTER"),
        @JsonSubTypes.Type(value = SlaveDevice.class, name = "FLASH_DRIVE"),
    })
    interface DeviceType{
        String getName();
    }

The interface is used by two enums:

public enum MasterDevice implements DeviceType{
    COMPUTER("Computer");
    private String name;
    public MasterDevice(String name){
       this.name=name;
     }
    @Override public String getName(){return this.name;}
}

The second one is for devices you can attach to the MasterDevice .

public enum SlaveDevice implements DeviceType{
     FLASH_DRIVE("USB Drive");
     private String name;
     public SlaveDevice(String name){
       this.name=name;
     }
     @Override public String getName(){return this.name;}
}

The POJO that I want to deserialize is:

public class DeviceInformation{
    private DeviceType type;
}

And the json String I want to deserialize look like this:

String info = "{\"type\":\"COMPUTER\"}";
ObjectMapper mapper = new ObjectMapper();
DeviceInformation deviceInfo = mapper.readValue(info, DeviceInformation.class);

All research was proposing implementing a custom deserializer for the DeviceType which I am not keen to do since it seems so bad to maintain.

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class DeviceType]: missing type id property '@type' (for POJO property 'type')`

It seems like Jackson searches for an type property on the DeviceType which of course it does not have. How do I tell Jackson that the Enum selection is based on the enum value (COMPUTER, FLASH_DRIVE)?

I think you're expecting too many levels to be collapsed for you simply by giving a bunch of things the same field and property names.

The JSON required for your current setup would be:

String info = "{\"type\": {\"type\": \"COMPUTER\", \"COMPUTER\": null}}";

Here, the outer "type" is for DeviceInformation, the inner "type:COMPUTER" pair are for DeviceType polymorphism of MasterDevice. And the final "COMPUTER" is to instantiate MasterDevice.COMPUTER (this last bit of weirdness feels like a bug with the Jackson implementation).

To make it more obvious what's going on, here's a simplified version with some renaming:

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        property = "type"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = MasterDevice.class, name = "MASTER"),
        @JsonSubTypes.Type(value = SlaveDevice.class, name = "SLAVE"),
})
interface DeviceType {
}

public enum MasterDevice implements DeviceType {
    LAPTOP, SERVER;
}

public enum SlaveDevice implements DeviceType {
    FLASH_DRIVE, WEBCAM;
}

public class DeviceInformation {
    public DeviceType deviceType;
}

Then:

String info = "{\"deviceType\": {\"type\": \"MASTER\", \"SERVER\": null}}";
ObjectMapper mapper = new ObjectMapper();
DeviceInformation deviceInfo = mapper.readValue(info, DeviceInformation.class));

If you want something more elegant, then you'll likely need a custom serializer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM