Search code examples
javajacksondeserializationjson-deserializationobjectmapper

Map a JSON field (to a value) based on another field (which is a key) using Jackson


{
  "key1": {
    "parameter1": "String1",
    "parameter2": "String2"
  },
  "key2": {
    "parameter1": "String3",
    "parameter2": "String4"
  },
  "key3": {
    "parameter1": "String5",
    "parameter2": "String6"
  }
}

I have the above JSON (/Users/user1/Desktop/responseMap.json) which is basically a Map<String, MockResponse> where MockResponse is the below POJO:

public class MockResponse {
    public String parameter1;
    public String parameter2;
} 

Now, I have another POJO - TestCase, and another JSON - testCase.json as below:

public class TestCase {

    public String responseMapFileLocation;
    public String mockResponseKey;
    public MockResponse mockResponse;
}

testCase.json

{
  "responseMapFileLocation": "/Users/user1/Desktop/responseMap.json",
  "mockResponseKey": "key1",
  "mockResponse": null
}

What I am able to do is first map testCase.json to TestCase using Jackson, then map responseMap.json to Map<String, MockResponse>, then in my code search for mockResponseKey in the map.

But what I want to do is when I map testCase.json to TestCase using Jackson, I want the value of variable mockResponse to set automatically based on the value of variable mockResponseKey using the first JSON map.


Solution

  • You need to write custom deserialiser for TestCase class. In custom deserialiser you can parse basic properties: responseMapFileLocation, mockResponseKey and load mockResponse from other file. To deserialiser MockResponse you can use new ObjectMapper instance. Below code shows how this concept could be implemented:

    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.TreeNode;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonDeserializer;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
    import com.fasterxml.jackson.databind.type.MapType;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.Map;
    
    public class JsonApp {
    
        public static void main(String[] args) throws Exception {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            ObjectMapper mapper = new ObjectMapper();
    
            System.out.println(mapper.readValue(jsonFile, TestCase.class));
        }
    }
    
    class MockResponse {
        public String parameter1;
        public String parameter2;
    }
    
    @JsonDeserialize(using = TestCaseFromExternalFileDeserializer.class)
    class TestCase {
    
        public String responseMapFileLocation;
        public String mockResponseKey;
        public MockResponse mockResponse;
    }
    
    class TestCaseFromExternalFileDeserializer extends JsonDeserializer<TestCase> {
    
        private final ObjectMapper mapper;
        private final MapType mapType;
    
        public TestCaseFromExternalFileDeserializer() {
            mapper = new ObjectMapper();
            mapType = mapper.getTypeFactory().constructMapType(Map.class, String.class, MockResponse.class);
        }
    
        @Override
        public TestCase deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            TreeNode treeNode = p.readValueAsTree();
    
            TestCase testCase = new TestCase();
            testCase.responseMapFileLocation = ((JsonNode) treeNode.get("responseMapFileLocation")).asText();
            testCase.mockResponseKey = ((JsonNode) treeNode.get("mockResponseKey")).asText();
            parseMockResponse(testCase);
    
            return testCase;
        }
    
        private void parseMockResponse(TestCase testCase) throws IOException {
            Map<String, MockResponse> map = mapper.readValue(new File(testCase.responseMapFileLocation), mapType);
    
            testCase.mockResponse = map.get(testCase.mockResponseKey);
        }
    }
    

    You need to implement only toString method for each POJO class. Above code prints:

    TestCase{responseMapFileLocation='./resource/responseMap.json', mockResponseKey='key1', mockResponse=MockResponse{parameter1='String1', parameter2='String2'}}
    

    Both JSON files are in resource folder.

    See also: