jsonschema2pojo icon indicating copy to clipboard operation
jsonschema2pojo copied to clipboard

Adding support for JsonSubTypes annotation

Open lsubramanya opened this issue 9 years ago • 18 comments

The generated polymorphic pojo classes had JsonType annotation for the parent class but was missing JsonSubTypes annotation. When using jackson for data binding, it was not able to recognize to which subclass it had to bind the data to and it needs JsonSubTypes annotation to figure out all possible subclasses. This was a requirment for a project that I am working on. Please let me know your feedback.

eg: Annotations for parent class Animal

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT, property = "name")
@JsonSubTypes({
    @JsonSubTypes.Type(value = Dog.class, name = "Dog"),
    @JsonSubTypes.Type(value = Cat.class, name = "Cat")
})

lsubramanya avatar Mar 16 '15 00:03 lsubramanya

Couple of comments on this:

  • We should support either a string or array as the value of deserializationClassProperty, rather than splitting on ,.
  • I think you may be able to achieve what you need without using javaassist, and instead using jclass.owner().ref(subClass) to create a JType. Would this work?

joelittlejohn avatar Mar 16 '15 16:03 joelittlejohn

Thank you for the feedback. Will update the pull request

lsubramanya avatar Mar 16 '15 17:03 lsubramanya

I have updated the pull request after addressing your feedback. jclass.owner().ref(subClass) works perfect.

lsubramanya avatar Mar 16 '15 19:03 lsubramanya

Thanks, could you also squash these commits into one?

joelittlejohn avatar Mar 16 '15 21:03 joelittlejohn

Done. Is it possible to have a release version with these changes?

lsubramanya avatar Mar 16 '15 22:03 lsubramanya

Is this part a breaking change for anyone using the existing deserializationClassProperty feature?

-            jsonTypeInfo.param("use", JsonTypeInfo.Id.CLASS);
-            jsonTypeInfo.param("include", JsonTypeInfo.As.PROPERTY);
-            jsonTypeInfo.param("property", annotationName);
+            jsonTypeInfo.param("use", JsonTypeInfo.Id.NAME);
+            jsonTypeInfo.param("include", JsonTypeInfo.As.WRAPPER_OBJECT);
+            jsonTypeInfo.param("property", "name"); 

joelittlejohn avatar Mar 18 '15 13:03 joelittlejohn

Yeah, its not backward compatible. I missed it. Let me try to work on it. But looks like when we use JsonTypeInfo.As.PROPERTY we need to use the annotationName(value of deserializationClassProperty) as a string which represents a key in the json object, not as the Sub class itself. The value of the key in json object will hold the details of the subclass. I was able to get jackson to bind the data just with JsonType annotation.

annotationName: type Eg: json object

{
"animal":{
    "type":"Dog",
    "colour":"black"
}
}

This object gets binded to the DOG class. But if we need JsonTypeInfo.As.WRAPPER_OBJECT then the annotationName will be a subClass in the schema. And the json will look like below

json object

{
"Dog":{
    "colour":"black"
}
}

lsubramanya avatar Mar 19 '15 17:03 lsubramanya

I think this is very useful especially to solve for user cases with class type hierarchy. But i think this change is not configurable enough.

I suggest we make it more configurable.

  1. class and the name value could be different, and user must be able to specific that as well.
"deserializationClassProperty" : [
        {
            "class" : "SummaryReport",
            "discriminiatorValue" : "SUMMARY"
        },
        {
            "class" : "DetailReport",
            "discriminiatorValue" : "DETAIL"
        }
    ]
  1. the key to derive the discriminiatorValue must also be configurable in schema instead of hardcode to name, it could be any other as well..

jsaimani avatar May 31 '15 06:05 jsaimani

I would very much like to see this implemented. Couldn't be the solution making lsubramanya's change using a slightly different keyword, which would keep the original functionality but add the new one? Something like deserializationClassPropertyWithSubs. I would make the change and a pull request if it is agreeable or lsubramanya could do it.

lufegit avatar Jul 10 '15 08:07 lufegit

I've updated the PR so that it does not break any existing changes. Thanks for the comments.

lsubramanya avatar Jul 10 '15 17:07 lsubramanya

This PR could really use a couple of tests showing JSON input documents being deserialized and verifying that the correct types were used.

ctrimble avatar Dec 12 '15 21:12 ctrimble

@joelittlejohn This would be a really useful feature for a project I'm working on at the moment. Are there plans to include this PR in a future release?

ville-k avatar Feb 03 '16 22:02 ville-k

Any update on plans here?

brentryan avatar Jul 20 '16 00:07 brentryan

This PR looks promising but its been gathering dust for a long time. What else does it need to move forward?

richardwalsh avatar Sep 22 '17 21:09 richardwalsh

I am not sure how to use "deserializationClassProperty" in my json schema...

Here is what I have

  vehicle.json
   {
     "type": "object",
     "id": "#Vehicle",
     "additionalProperties": false,
     "javaType": "Vehicle",
     "deserializationClassProperty" : "Car.class,Truck.class",
     "properties": {
       "color": {
         "type": "string"
       }
     }
   }
   car.json
   {
     "type": "object",
     "id": "#Car",
     "additionalProperties": false,
     "javaType": "Car",
     "extends": {
       "type": "object",
       "existingJavaType": "Vehicle"
     }
   }
   truck.json
   {
     "type": "object",
     "id": "#Truck",
     "additionalProperties": false,
     "javaType": "Truck",
     "extends": {
       "type": "object",
       "existingJavaType": "Vehicle"
     }
   }
   
   inventoryitem.json
   {
     "type": "object",
     "id": "#InventoryItem",
     "additionalProperties": false,
     "javaType": "InventoryItem",
     "properties": {
       "vehicle": {
         "#ref": "vehicle.json"
       }
     }
   }

Here is my input that I am trying to unmarshall using XmlMapper <InventoryItem><Vehicle xis:type="ns2:car"/></InventoryItem> The generated code for the above schema

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "Car.class, Truck.class")
public class Vehicle

But I need to generate as shown below to get it working I need to generate the following through json schema


    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
    @JsonSubTypes ({
            @JsonSubTypes.Type(value = Truck.class, name = "truck"), 
            @JsonSubTypes.Type(value = Car.class, name = "car")
    })
    public class Vehicle

Also how to avoid the namespace "ns2:car"

Thanks for you time

ravim786 avatar May 06 '21 00:05 ravim786

ping @joelittlejohn

atkawa7 avatar May 17 '21 15:05 atkawa7

Hello, I would like to generate @JsonSubTypes same way like ravim786 mentioned. Is there some possibility that this feature will be released?

Millenium47 avatar Jan 31 '22 14:01 Millenium47