{"id":1505,"date":"2024-03-24T21:12:40","date_gmt":"2024-03-24T17:42:40","guid":{"rendered":"https:\/\/mshaeri.com\/blog\/?p=1505"},"modified":"2025-07-14T09:54:28","modified_gmt":"2025-07-14T06:24:28","slug":"polymorphic-deserialization-in-spring-boot-api-with-jaksons-jsontypeinfo-and-jsonsubtypes","status":"publish","type":"post","link":"https:\/\/mshaeri.com\/blog\/polymorphic-deserialization-in-spring-boot-api-with-jaksons-jsontypeinfo-and-jsonsubtypes\/","title":{"rendered":"Polymorphic Deserialization In Spring Boot API With Jackson&#8217;s JsonTypeInfo And JsonSubTypes"},"content":{"rendered":"\n<p>Jackson&#8217;s polymorphic deserialization is an exceptional feature that facilitates the deserialization of JSON into a hierarchy of Java objects, particularly when the object type is unknown at compile time. Consider an order validation API that aims to validate various types of orders (e.g., Analysis Order, Repair Order, Replacement Order). Each order type in our application is defined as a subclass of abstract Order class with a dedicated <code><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><strong>validate <\/strong><\/mark><\/code>method. Also, we know that adhering to the SOLID Principle as always should be fundamental aspect of our development, to have a maintainable clean code which is easy to scale :<\/p>\n\n\n\n<ul>\n<li><strong>Open\/Closed<\/strong> <strong>principle<\/strong>: If we need to add a new order type to the system, it should be done without any modification in the existing code. In other word, adding the new Order subclass with a dedicated validate method to the code, should be the only thing we should do.<\/li>\n<\/ul>\n\n\n\n<ul>\n<li><strong>Liskov substitution<\/strong> <strong>principle<\/strong>: The validation API must be able to validate all existing and future subclasses of Order class correctly and also without requiring any modifications to the API code.<\/li>\n<\/ul>\n\n\n\n<p>In this post, we will learn how the <strong><span style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>JsonTypeInfo<\/code> <\/span><\/strong>and <strong><span style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>JsonSubTypes<\/code> <\/span><\/strong>annotations in Java can help us to accomplish the above task.<\/p>\n\n\n\n<p>First, we define the abstract <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>Order<\/code><\/mark><\/strong> class with all basic fields required in an order.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = \"type\", visible = true)\n@JsonSubTypes({\n        @JsonSubTypes.Type(value = AnalysisOrder.class, name = \"ANALYSIS\"),\n        @JsonSubTypes.Type(value = RepairOrder.class, name = \"REPAIR\"),\n        @JsonSubTypes.Type(value = ReplacementOrder.class, name = \"REPLACEMENT\")\n})\npublic abstract class Order {\n \n @JsonProperty(\"type\") \n protected String type; \n\n @JsonProperty(\"department\") \n protected String department;\n \n @JsonProperty(\"start_date\") \n protected Date startDate;\n \n @JsonProperty(\"end_date\") \n protected Date endDate; \n\n @JsonProperty(\"currency\") \n protected String currency;\n\n protected ValidationResult basicValidate(){\n         valid = true;\n         validationErrors = new ArrayList&lt;&gt;();\n         this.validateDepartment();\n         this.validateStartDate();\n         this.validateEndDate();\n         this.validateCurrency();\n         this.validateCost();\n    return new ValidationResult(valid, validationErrors);\n }\n\n \/\/ Other methods of the class\n\n}\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>In the above code, the <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>Order<\/code> <\/mark><\/strong>class is annotated with <code><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">@JsonTypeInfo<\/mark><\/strong><\/code>, specifying that the <mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>type <\/code><\/mark>field should be used to determine the subclass during deserialization. In addition, the <code><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">@<strong>JsonTypeInfo<\/strong><\/mark><\/code><strong> <\/strong>annotation allows for runtime inclusion of type information in serialized JSON. The <mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>@<strong>JsonSubTypes<\/strong><\/code><\/mark><strong> <\/strong>annotation defines the possible subtypes that can be deserialized based on the <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>type <\/code><\/mark><\/strong>field. This way, new order types can be introduced by simply creating new child classes inheriting <strong><code><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Order <\/mark><\/code><\/strong>and annotating them with <code><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">@JsonSubTypes<\/mark><\/code>.<\/p>\n\n\n\n<p>Then we add sub classes as following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.*;\nimport lombok.experimental.Accessors;\nimport mst.example.ordervalidation.models.Part;\nimport mst.example.ordervalidation.models.ValidationError;\nimport mst.example.ordervalidation.models.ValidationResult;\n\nimport java.util.Date;\n\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@ToString\n@Getter\n@Setter\n@Accessors(fluent = false, chain = true)\npublic class RepairOrder extends Order{\n\n&nbsp; &nbsp; @JsonProperty(\"analysis_date\")\n&nbsp; &nbsp; private Date analysisDate;\n\n&nbsp; &nbsp; @JsonProperty(\"test_date\")\n&nbsp; &nbsp; private Date testDate;\n\n&nbsp; &nbsp; @JsonProperty(\"responsible_person\")\n&nbsp; &nbsp; private String responsiblePerson;\n\n\n\n&nbsp; &nbsp; \/**\n&nbsp; &nbsp; &nbsp;* This method applies validation related to Repair type of the order\n&nbsp; &nbsp; &nbsp;*\/\n&nbsp; &nbsp; @Override\n&nbsp; &nbsp; public ValidationResult validate() {\n\n&nbsp; &nbsp; &nbsp; &nbsp; this.basicValidate();\n&nbsp; &nbsp; &nbsp; &nbsp; this.validateAnalysisDate();\n&nbsp; &nbsp; &nbsp; &nbsp; this.validateResponsiblePerson();\n&nbsp; &nbsp; &nbsp; &nbsp; this.validateTestDate();\n&nbsp; &nbsp; &nbsp; &nbsp; this.validateParts();\n\n&nbsp; &nbsp; &nbsp; &nbsp; return new ValidationResult(valid, validationErrors);\n&nbsp; &nbsp; }\n\n\n   \/\/ Other methods of the class\n\n}<\/code><\/pre>\n\n\n\n<p>During deserialization, the type information is used to create the right subclass instance. This allows for seamless conversion between <strong>JSON <\/strong>and object of <strong><code><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Order <\/mark><\/code><\/strong>sub classes . Also when serializing an <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>Order<\/code> <\/mark><\/strong>object to JSON, the output will include a <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>type<\/code> <\/mark><\/strong>property indicating the specific subclass. For instance, an <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>AnalysisOrder<\/code> <\/mark><\/strong>will be serialized as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">{\n  \"type\": \"ANALYSIS\",\n  \"department\": \"DepartmentA\",\n  \"start_date\": \"2023-08-01\",\n  \"end_date\": \"2023-08-10\",\n  \"currency\": \"USD\",\n  \"cost\": 150.0,\n  \"parts\": []\n}\n\n<\/code><\/pre>\n\n\n\n<p>In the following code, we implement a POST endpoint <mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code><strong>\/<\/strong><\/code><strong><code>validateOrder<\/code> <\/strong><\/mark>accepting a JSON request body representing an <strong><code>Order<\/code> <\/strong>object . Thanks to Jackson&#8217;s annotations, the incoming order is automatically cast to its exact subclass (e.g., <code>AnalysisOrder<\/code>, <code>RepairOrder<\/code>, &#8230;) based on the <code>type<\/code> property :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">&nbsp; &nbsp; &nbsp;@PostMapping(value = \"\/validateOrder\", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)\n&nbsp; &nbsp; public ResponseEntity&lt;ValidationApiResponseModel&gt; validateOrder(@RequestBody Order order) {\n\n&nbsp; &nbsp; &nbsp;...\n\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/ Validate the given order\n&nbsp; &nbsp; &nbsp; &nbsp; \/\/ The order automatically cast to its exact subclass, so don't need to cast it explicitly\n&nbsp; &nbsp; &nbsp; &nbsp; ValidationResult validationResult = orderValidationService.validateOrder(order);\n&nbsp; &nbsp; &nbsp; &nbsp; \n...\n&nbsp; &nbsp; &nbsp; &nbsp; \n&nbsp; &nbsp; }<\/code><\/pre>\n\n\n\n<p>Then, the order is validated using <code><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">orderValidationService.validateOrder<\/mark><\/code> function which simply calls given order&#8217;s  <mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><code>validate <\/code><\/mark>method. This way, we don&#8217;t need to care about the type of order that is being validated, each order subclass has its own validate method that checks parameters specific to that subclass. <\/p>\n\n\n\n<p>You can find complete sample project on my github repository : <a href=\"https:\/\/github.com\/birddevelper\/order-validation\" target=\"_blank\" rel=\"noreferrer noopener\">example for polymorphic deserialization in spring boot<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-conclusion\"><strong>Conclusion<\/strong><\/h3>\n\n\n\n<p>Using <strong>JsonTypeInfo <\/strong>and <strong>JsonSubTypes <\/strong>annotations facilitates polymorphic deserialization of JSON into a hierarchy of Java objects, particularly when the specific type isn&#8217;t known at compile time. It helps us to have a cleaner code, better maintainability, and easy to extent features.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Jackson&#8217;s polymorphic deserialization is an exceptional feature that facilitates the deserialization of JSON into a hierarchy of Java objects, particularly when the object type is &hellip; <\/p>\n","protected":false},"author":1,"featured_media":1723,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[69,94,35,70],"tags":[240,222,9,67,223,224,63,71],"_links":{"self":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts\/1505"}],"collection":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/comments?post=1505"}],"version-history":[{"count":4,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts\/1505\/revisions"}],"predecessor-version":[{"id":1845,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts\/1505\/revisions\/1845"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/media\/1723"}],"wp:attachment":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/media?parent=1505"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/categories?post=1505"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/tags?post=1505"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}