Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deserializing nested polymorphic types does not work #2000

Closed
raderio opened this issue Apr 17, 2018 · 12 comments
Closed

Deserializing nested polymorphic types does not work #2000

raderio opened this issue Apr 17, 2018 · 12 comments

Comments

@raderio
Copy link

raderio commented Apr 17, 2018

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Car.class, name = "car"),
        @JsonSubTypes.Type(value = Truck.class, name = "truck")
})
public abstract class Vehicle {
    protected String make;
    protected String model;
    protected String type;

    // no-arg constructor, getters and setters
}
public class Truck extends Vehicle {
    private double payloadCapacity;

     // no-arg constructor, getters and setters
}
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "status")
@JsonSubTypes({
        @JsonSubTypes.Type(value = CarLite.class, name = "lite"),
        @JsonSubTypes.Type(value = CarWeight.class, name = "weight")
})
public class Car extends Vehicle {
    protected int seatingCapacity;
    protected double topSpeed;
    protected String status;

     // no-arg constructor, getters and setters
}
public class CarLite extends Car {
    private String lite;

     // no-arg constructor, getters and setters
}
public class CarWeight extends Car {
    private int weight;

    // no-arg constructor, getters and setters
}
public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();

        String json = "[\n" +
                "        {\n" +
                "            \"type\": \"car\",\n" +
                "            \"make\": \"Mercedes-Benz\",\n" +
                "            \"model\": \"S500\",\n" +
                "            \"seatingCapacity\": 5,\n" +
                "            \"topSpeed\": 250.0,\n" +
                "            \"status\": \"lite\",\n" +
                "            \"lite\": \"#ccc\"\n" +
                "        },\n" +
                " \n" +
                "        {\n" +
                "            \"type\": \"truck\",\n" +
                "            \"make\": \"Isuzu\",\n" +
                "            \"model\": \"NQR\",\n" +
                "            \"payloadCapacity\": 7500.0\n" +
                "        }\n" +
                "    ]";

        List<Vehicle> v = objectMapper.readValue(json, new TypeReference<List<Vehicle>>() {});

        v.forEach(System.out::println);
    }
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "lite" (class models.Car), not marked as ignorable (6 known properties: "topSpeed", "seatingCapacity", "model", "type", "make", "status"])

Using jackson 2.9.5

@raderio raderio changed the title Deserializing nested polymorphic property does not work Deserializing nested polymorphic types does not work Apr 17, 2018
@cowtowncoder
Copy link
Member

This is not supported usage: @JsonProperty settings may not vary across type hierarchy -- so for base type of Vehicle, settings from that are used and not possible overrides by subtypes.
The only thing you can use in subtypes is @JsonSubTypes which is handled transitively to locate further subtypes. But type id settings must be homogenous.

@raderio
Copy link
Author

raderio commented Apr 18, 2018

What are issues in supporting this feature?

We need to deserialize a Type by "category" property, an then a SubType to deserialize by another property "status".

@cowtowncoder
Copy link
Member

@raderio It is not meant to work that way. Basic premise is that wrt type handling declared base type is used; never subtypes. On serialization it would be theoretically possible to change that, but on deserialization I am not sure how it could work.

So it is safe to say that what you are attempting to do will not work with standard @JsonTypeInfo: it is not designed to allow incremental/multi-level type resolution.
If you do want to make it work you will need to use custom TypeSerializer / TypeDeserializer; or, perhaps, fully custom serializer/deserializer.

@raderio
Copy link
Author

raderio commented Apr 19, 2018

but on deserialization I am not sure how it could work.

First read type property value in order to decide to what child of Vehicle to deserialize, then if it is "car" read status property value in order to decide to what child of Car to deserialize.

Is it possible to support this scenario?

@cowtowncoder
Copy link
Member

@raderio No. This is not supported by the design at all. As I said, if you want something like that, you will have to implement it without using existing polymorphic type handling.

@raderio
Copy link
Author

raderio commented Apr 20, 2018

As I said, if you want something like that, you will have to implement it without using existing polymorphic type handling.

Yes I can implement such deserializer, but I also use a library(https://github.com/springfox/springfox) that generate Swagger documentation based on Jackson annotations, so if Jackson not support this, the library also will not support

This is not supported by the design at all.

What are impediments to change the design? It will not break any code.

@raderio
Copy link
Author

raderio commented Apr 25, 2018

This also will be supported by other tools Redocly/redoc#210
OpenAPI also supports it.

@cowtowncoder
Copy link
Member

That is too bad since it is once again a construct difficult and inefficient to support. There are many JSON standards that build things in a way that does not considered feasibility of implement or support.

I will not continue discussion on this issue: if you are interested in learning why it is difficult to implement I suggest you try to implement it. Support for polymorphic types is rather complicated to begin with, when databinding is incremental. Adding multiple levels of indirection will significantly complicate things, which in itself is unfortunate, but becomes impractical when couple with all other functionality supported (with pluggable modular handles, object identity handling, unwrapping, builders and all the jazz).

@raderio
Copy link
Author

raderio commented Dec 15, 2018

Related issues
#374
#1188

This really will simplify the REST API, instead of having multiple endpoints for related things, will have just one endpoint
discriminator-demo
Image from https://github.com/Rebilly/ReDoc

@raderio
Copy link
Author

raderio commented Dec 25, 2018

if you are interested in learning why it is difficult to implement I suggest you try to implement it

@cowtowncoder can you please give me a starting point in the code? So, I want to try to implement it

BTW, https://github.com/cbeust/klaxon library supports this

@cowtowncoder
Copy link
Member

@raderio I am sure other libraries can implement it. I have no interest in implementing, or using my limited time on documenting why I think it is not worth the hassle. If you truly want to figure it out that's fine of course. It may be fun exercise.

One starting point would be figuring out how polymorphic deserialization actually works. JsonDeserializer and TypeDeserializer are main abstractions that handle interaction between "regular" deserializer (which handles content) and type deserializer that handles extracting of type id from json content to figure out which content deserializer to call.

@pedroviniv
Copy link

pedroviniv commented May 3, 2020

@raderio Hi. I know it's been a couple of time. But I was facing the same issue and I developed a custom JsonDeserializer to workaround this limitation. The idea is to support multiple discriminator fields when deserializing the JSON. checkout the repository: https://github.com/pedroviniv/multi-discriminators-polymorphic-deserializer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants