使用 Jackson 根据另一个字段(这是一个键)映射一个 JSON 字段(到一个值)

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"
  }
}

我有上面的 JSON (/Users/user1/Desktop/responseMap.json) 这基本上是一个 Map<String, MockResponse> 其中 MockResponse 是下面的 POJO :

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

现在,我有另一个 POJO - TestCase,和另一个 JSON - testCase.json,如下所示:

public class TestCase {

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

testCase.json

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

我能做的是首先使用 JacksontestCase.json 映射到 TestCase,然后将 responseMap.json 映射到 Map<String, MockResponse>,然后在我的代码中在地图中搜索 mockResponseKey

但我想做的是当我使用 JacksontestCase.json 映射到 TestCase 时,我希望变量 mockResponse 的值根据使用第一个 JSON 映射的变量 mockResponseKey 的值。

只有 Jackson 无法满足您的要求。 Jackson 主要是一个 marshalling/unmarshalling 工具,将 JSON 转换为对象,反之亦然。换句话说,在解组时必须知道对象的值。

但是您可以使用以下代码将 json 解编为 HashMap:

new JSONObject(map);

使用 mockResponseKey 搜索 MockResponse-as-a-string,然后将该代码解编为新的 MockResponse。

在测试 class 中调整 getter setter 并将该字段标记为私有我能够使其动态化(导入来自 org.codehaus.jackson 包)

class TestCase {

    private String responseMapFileLocation;

    private String mockResponseKey;

    @JsonIgnore
    private MockResponse mockResponse; //else value will be override in json value

    public String getResponseMapFileLocation() {
        return responseMapFileLocation;
    }
    public void setResponseMapFileLocation(String responseMapFileLocation) {
        this.responseMapFileLocation = responseMapFileLocation;
    }
    public String getMockResponseKey() {
        return mockResponseKey;
    }
    public void setMockResponseKey(String mockResponseKey1) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, MockResponse> map = mapper.readValue(new File("C:\Users\Json1.json"), TypeFactory.mapType(HashMap.class, String.class, MockResponse.class));
        this.mockResponse = map.get(mockResponseKey1);
        this.mockResponseKey = mockResponseKey1;
    }
    public MockResponse getMockResponse() {
        return mockResponse;
    }

    @Override
    public String toString() {
        return "TestCase [responseMapFileLocation=" + responseMapFileLocation + ", mockResponseKey=" + mockResponseKey
                + ", mockResponse=" + mockResponse + "]";
    }
}


class MockResponse {
    public String parameter1;
    public String parameter2;
    @Override
    public String toString() {
        return "MockResponse [parameter1=" + parameter1 + ", parameter2=" + parameter2 + "]";
    }
}

和 运行 下面的代码

public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        TestCase testCase = mapper.readValue(new File("C:\UsersJson2.json"), TestCase.class);
        System.out.println(testCase);

    }

输出将是

TestCase [responseMapFileLocation=/Users/user1/Desktop/responseMap.json, mockResponseKey=key1, mockResponse=MockResponse [parameter1=String1, parameter2=String2]]

您需要为 TestCase class 编写自定义解串器。在自定义反序列化器中,您可以解析基本属性:responseMapFileLocationmockResponseKey 并从其他文件加载 mockResponse。要反序列化 MockResponse,您可以使用新的 ObjectMapper 实例。下面的代码显示了如何实现这个概念:

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);
    }
}

您只需为每个 POJO class 实施 toString 方法。上面的代码打印:

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

两个 JSON 文件都在 resource 文件夹中。

另请参阅:

  • How use jackson ObjectMapper inside custom deserializer?
  • Jackson Streaming API - 如果你想以更快的方式实现 MockResponse 反序列化。