使用 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
}
我能做的是首先使用 Jackson
将 testCase.json
映射到 TestCase
,然后将 responseMap.json
映射到 Map<String, MockResponse>
,然后在我的代码中在地图中搜索 mockResponseKey
。
但我想做的是当我使用 Jackson
将 testCase.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 编写自定义解串器。在自定义反序列化器中,您可以解析基本属性:responseMapFileLocation
、mockResponseKey
并从其他文件加载 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
反序列化。
{
"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
}
我能做的是首先使用 Jackson
将 testCase.json
映射到 TestCase
,然后将 responseMap.json
映射到 Map<String, MockResponse>
,然后在我的代码中在地图中搜索 mockResponseKey
。
但我想做的是当我使用 Jackson
将 testCase.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 编写自定义解串器。在自定义反序列化器中,您可以解析基本属性:responseMapFileLocation
、mockResponseKey
并从其他文件加载 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
反序列化。