Mapping a Dynamic JSON Object Field in Jackson with Java
All REST API services are providing the resultant output either as XML or JSON pattern only as both are portable in nature and among that JSON is more popular. We can expect unknown properties in a JSON object. There are different ways available to map dynamic JSON objects into Java classes. They are obviously required in many scenarios. Let us cover this with a sample maven project.
Example Project
A maven project has to be created by adding the dependencies as specified in pom.xml
<dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20220320</version> </dependency>
Project Structure:

As this is the maven project, all the dependencies need to be seen in pom.xml
pom.xml
XML
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 < modelVersion >4.0.0</ modelVersion > < artifactId >gfg-jackson-conversions</ artifactId > < version >0.0.1-SNAPSHOT</ version > < name >gfg-jackson-conversions</ name > < parent > < groupId >com.gfg</ groupId > < artifactId >jackson-modules</ artifactId > < version >0.0.1-SNAPSHOT</ version > </ parent > < dependencies > < dependency > < groupId >org.mock-server</ groupId > < artifactId >mockserver-netty</ artifactId > < version >${mockserver-netty.version}</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.json</ groupId > < artifactId >json</ artifactId > < version >${json.version}</ version > </ dependency > </ dependencies > < build > < finalName >gfg-jackson-conversions</ finalName > < resources > < resource > < directory >src/main/resources</ directory > < filtering >true</ filtering > </ resource > </ resources > </ build > < properties > < mockserver-netty.version >5.13.2</ mockserver-netty.version > < json.version >20220320</ json.version > </ properties > </ project > |
By seeing different examples, let us cover the concept. Suppose, we have a dynamic JSON of this type
{ "productName": "Samsung M30", "productCategory": "cellphone", "productDetails": { "displayAspectRatio": "97:3", "audioConnector": "available" } }
In the above JSON, “displayAspectRatio” and “audioConnector” are applicable to a mobilephone but not a shoe or any foodItem.
1. By using com.fasterxml.jackson.databind.JsonNode
com.fasterxml.jackson.databind.JsonNode can handle dynamic keys and hence in our java class, we can make the productDetails as JsonNode
OnlineShoppingProductJsonNode.java
Java
import com.fasterxml.jackson.databind.JsonNode; public class OnlineShoppingProductJsonNode { private String productName; private String productCategory; // As JsonNode support dynamic key, // it is done like this private JsonNode productDetails; public String getProductName() { return productName; } public void setProductName(String productName) { this .productName = productName; } public String getProductCategory() { return productCategory; } public void setProductCategory(String productCategory) { this .productCategory = productCategory; } public JsonNode getProductDetails() { return productDetails; } public void setProductDetails(JsonNode productDetails) { this .productDetails = productDetails; } } |
Note: dependency on Jackson library is needed if we use JsonNode
Hence during testing, we need to write as
@Test void jsonString_parsingToJsonNode_resultWillBeDynamicProperties() throws JsonParseException, JsonMappingException, IOException { // given String givenJson = readJsonResource("/sample-dynamic-object/embedded.json"); // when OnlineShoppingProductJsonNode product = objectMapper.readValue(givenJson, OnlineShoppingProductJsonNode.class); // then assertThat(product.getProductName()).isEqualTo("Samsung M30"); assertThat(product.getProductCategory()).isEqualTo("cellphone"); assertThat(product.getProductDetails().get("audioConnector").asText()).isEqualTo("available"); }
Instead of depending on Jackson library, it can be made by using
2. By using java.util.Map
It can be done by using Map<String, Object>
OnlineShoppingProductMap,java
Java
import java.util.Map; public class OnlineShoppingProductMap { private String productName; private String productCategory; private Map<String, Object> productDetails; public String getProductName() { return productName; } public void setProductName(String name) { this .productName = name; } public String getProductCategory() { return productCategory; } public void setProductCategory(String category) { this .productCategory = category; } public Map<String, Object> getProductDetails() { return productDetails; } public void setProductDetails(Map<String, Object> productDetails) { this .productDetails = productDetails; } } |
Product Details need to be stored in Map<String,Object>. Hence testing is easier i.e. no need to depend on Jackson library
@Test void jsonString_parsingToMap_resultWillBeDynamicProperties() throws JsonParseException, JsonMappingException, IOException { // given String givenJson = readJsonResource("/sample-dynamic-object/embedded.json"); // when OnlineShoppingProductMap product = objectMapper.readValue(givenJson, OnlineShoppingProductMap.class); // then assertThat(product.getProductName()).isEqualTo("Samsung M30"); assertThat(product.getProductCategory()).isEqualTo("cellphone"); assertThat(product.getProductDetails().get("audioConnector")).isEqualTo("available"); }
3. By using @JsonAnySetter
When fixed and dynamic properties are there in JSON, then we need to go for @JsonAnySetter. @JsonAnySetter is used to mark a method for handling additional, unknown properties and should contain the name and value of the property.
@JsonAnySetter public void setProductDetails(String key, Object value) { productDetails.put(key,value); }
Example JSON
// We have fixed and dynamic properties { "productName": "Samsung M30", "productCategory": "cellphone", "displayAspectRatio": "97:3", "audioConnector": "available" }
OnlineShoppingProduct.java
Java
import java.util.LinkedHashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonAnySetter; public class OnlineShoppingProduct { private String productName; private String productCategory; private Map<String, Object> productDetails = new LinkedHashMap<>(); public String getProductName() { return productName; } public void setProductName(String productName) { this .productName = productName; } public String getProductCategory() { return productCategory; } public void setProductCategory(String productCategory) { this .productCategory = productCategory; } public Map<String, Object> getProductDetails() { return productDetails; } @JsonAnySetter public void setProductDetails(String key, Object value) { productDetails.put(key,value); } } |
Let us assume that we have embedded json and flat json available in a particular folder called “test/resources/sample-dynamic-object
embedded json
{ "productName": "Samsung M30", "productCategory": "cellphone", "productDetails": { "displayAspectRatio": "97:3", "audioConnector": "available" } }
flat json
{ "productName": "Samsung M30", "productCategory": "cellphone", "displayAspectRatio": "97:3", "audioConnector": "available" }
Let us write the test file
GFGDynamicObjectDeserializationSampleUnitTest.java
For the above two JSONs, our test scenarios pass
Java
import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; import java.util.Scanner; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; public class GFGDynamicObjectDeserializationSampleUnitTest { private ObjectMapper objectMapper; @BeforeEach void setup() { objectMapper = new ObjectMapper(); } private String readJsonResource(String jsonPath) { try (Scanner scanner = new Scanner(getClass().getResourceAsStream(jsonPath), "UTF-8" )) { return scanner.useDelimiter( "\\A" ).next(); } } @Test void jsonString_parsingToJsonNode_resultWillBeDynamicProperties() throws JsonParseException, JsonMappingException, IOException { // given String givenJson = readJsonResource( "/sample-dynamic-object/embedded.json" ); // when OnlineShoppingProductJsonNode product = objectMapper.readValue(givenJson, OnlineShoppingProductJsonNode. class ); // then assertThat(product.getProductName()).isEqualTo( "Samsung M30" ); assertThat(product.getProductCategory()).isEqualTo( "cellphone" ); assertThat(product.getProductDetails().get( "audioConnector" ).asText()).isEqualTo( "available" ); } @Test void jsonString_parsingToMap_resultWillBeDynamicProperties() throws JsonParseException, JsonMappingException, IOException { // given String givenJson = readJsonResource( "/sample-dynamic-object/embedded.json" ); // when OnlineShoppingProductMap product = objectMapper.readValue(givenJson, OnlineShoppingProductMap. class ); // then assertThat(product.getProductName()).isEqualTo( "Samsung M30" ); assertThat(product.getProductCategory()).isEqualTo( "cellphone" ); assertThat(product.getProductDetails().get( "audioConnector" )).isEqualTo( "available" ); } @Test void jsonString_ParsingWithJsonAnySetter_resultWillBeDynamicProperties() throws JsonParseException, JsonMappingException, IOException { // given String givenJson = readJsonResource( "/sample-dynamic-object/flat.json" ); // when OnlineShoppingProduct product = objectMapper.readValue(givenJson, OnlineShoppingProduct. class ); // then assertThat(product.getProductName()).isEqualTo( "Samsung M30" ); assertThat(product.getProductCategory()).isEqualTo( "cellphone" ); assertThat(product.getProductDetails().get( "audioConnector" )).isEqualTo( "available" ); } } |
When run as JUNIT Test, then

Please Login to comment...