使用 Jackson 根据条件注入 json 属性
Injecting json property based on condition using Jackson
我有一个 json 格式,我正在使用 Jackson API
将其转换为 Java 对象模型。我正在使用 Jaxsonxml
2.1.5 解析器。 json 响应如下所示。
{
"response": {
"name": "states",
"total-records": "1",
"content": {
"data": {
"name": "OK",
"details": {
"id": "1234",
"name": "Oklahoma"
}
}
}
}
}
现在 json 响应格式已更改。如果 total-records
是 1
,则 details
将是一个具有 id
和 name
属性的对象。但是如果 total-records
大于 1
那么 details
将是一个对象数组,如下所示:
{
"response": {
"name": "states",
"total-records": "4",
"content": {
"data": {
"name": "OK",
"details": [
{
"id": "1234",
"name": "Oklahoma"
},
{
"id": "1235",
"name": "Utah"
},
{
"id": "1236",
"name": "Texas"
},
{
"id": "1237",
"name": "Arizona"
}
]
}
}
}
}
我的 Java 映射器 class 看起来像下面的早期 json
响应。
@JsonIgnoreProperties(ignoreUnknown = true)
public class MapModelResponseList {
@JsonProperty("name")
private String name;
@JsonProperty("total-records")
private String records;
@JsonProperty(content")
private Model model;
public Model getModelResponse() {
return model;
}
public void setModel(Model model) {
this.model = model;
}
}
客户代码
package com.test.deserializer;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com..schema.model.Person;
public class TestClient {
public static void main(String[] args) {
String response1="{\"id\":1234,\"name\":\"Pradeep\"}";
TestClient client = new TestClient();
try {
Person response = client.readJSONResponse(response1, Person.class);
} catch (Exception e) {
e.printStackTrace();
}
}
public <T extends Object> T readJSONResponse(String response, Class<T> type) {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
T result = null;
try {
result = mapper.readValue(response, type);
} catch (Exception e) {
e.printStackTrace();
}
return (T) result;
}
}
现在基于 total-records
如何处理映射到 Model
或 Model
对象列表。请告诉我。
您需要自定义反序列化器。这个想法是将对象处理与树处理混合和匹配。尽可能解析对象,但使用树 (JSONNode
) 进行自定义处理。
在 MapModelResponseList
上,删除 records
属性 并添加一个 List<Data>
数组,其中 Data
只是一个持有者 class id/name 对。您可以通过返回此列表的大小来获取总记录数。
在解串器中,执行以下操作:
public final class MapModelDeserializer extends BeanDeserializer {
public MapModelDeserializer(BeanDeserializerBase src) {
super(src);
}
protected void handleUnknownProperty(JsonParser jp, DeserializationContext ctxt, Object beanOrClass, String propName) throws IOException, JsonProcessingException {
if ("content".equals(propName)) {
MapModelResponseList response = (MapModelResponseList) beanOrClass;
// this probably needs null checks!
JsonNode details = (JsonNode) jp.getCodec().readTree(jp).get("data").get("details");
// read as array and create a Data object for each element
if (details.isArray()) {
List<Data> data = new java.util.ArrayList<Data>(details.size());
for (int i = 0; i < details.size(); i++) {
Data d = jp.getCodec().treeToValue(details.get(i), Data.class);
data.add(d);
}
response.setData(data);
}
// read a single object
else {
Data d = jp.getCodec().treeToValue(details, Data.class);
response.setData(java.util.Collections.singletonList(d));
}
super.handleUnknownProperty(jp, ctxt, beanOrClass, propName);
}
请注意,您没有实施 deserialize()
- 默认实施用于正常创建 MapModelResponseList
。 handleUknownProperty()
用于处理content
元素。由于超级调用中的@JsonIgnoreProperties(ignoreUnknown = true)
,您不关心的其他数据将被忽略。
这是一个迟到的答案,但我用不同的方式解决了它。它可以通过像这样在 Object
中捕获它来工作:
@JsonProperty("details")
public void setDetails(Object details) {
if (details instanceof List) {
setDetails((List) details);
} else if (details instanceof Map) {
setDetails((Map) details);
}
}
public void setDetails(List details) {
// your list handler here
}
public void setDetails(Map details) {
// your map handler here
}
我有一个 json 格式,我正在使用 Jackson API
将其转换为 Java 对象模型。我正在使用 Jaxsonxml
2.1.5 解析器。 json 响应如下所示。
{
"response": {
"name": "states",
"total-records": "1",
"content": {
"data": {
"name": "OK",
"details": {
"id": "1234",
"name": "Oklahoma"
}
}
}
}
}
现在 json 响应格式已更改。如果 total-records
是 1
,则 details
将是一个具有 id
和 name
属性的对象。但是如果 total-records
大于 1
那么 details
将是一个对象数组,如下所示:
{
"response": {
"name": "states",
"total-records": "4",
"content": {
"data": {
"name": "OK",
"details": [
{
"id": "1234",
"name": "Oklahoma"
},
{
"id": "1235",
"name": "Utah"
},
{
"id": "1236",
"name": "Texas"
},
{
"id": "1237",
"name": "Arizona"
}
]
}
}
}
}
我的 Java 映射器 class 看起来像下面的早期 json
响应。
@JsonIgnoreProperties(ignoreUnknown = true)
public class MapModelResponseList {
@JsonProperty("name")
private String name;
@JsonProperty("total-records")
private String records;
@JsonProperty(content")
private Model model;
public Model getModelResponse() {
return model;
}
public void setModel(Model model) {
this.model = model;
}
}
客户代码
package com.test.deserializer;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com..schema.model.Person;
public class TestClient {
public static void main(String[] args) {
String response1="{\"id\":1234,\"name\":\"Pradeep\"}";
TestClient client = new TestClient();
try {
Person response = client.readJSONResponse(response1, Person.class);
} catch (Exception e) {
e.printStackTrace();
}
}
public <T extends Object> T readJSONResponse(String response, Class<T> type) {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
T result = null;
try {
result = mapper.readValue(response, type);
} catch (Exception e) {
e.printStackTrace();
}
return (T) result;
}
}
现在基于 total-records
如何处理映射到 Model
或 Model
对象列表。请告诉我。
您需要自定义反序列化器。这个想法是将对象处理与树处理混合和匹配。尽可能解析对象,但使用树 (JSONNode
) 进行自定义处理。
在 MapModelResponseList
上,删除 records
属性 并添加一个 List<Data>
数组,其中 Data
只是一个持有者 class id/name 对。您可以通过返回此列表的大小来获取总记录数。
在解串器中,执行以下操作:
public final class MapModelDeserializer extends BeanDeserializer {
public MapModelDeserializer(BeanDeserializerBase src) {
super(src);
}
protected void handleUnknownProperty(JsonParser jp, DeserializationContext ctxt, Object beanOrClass, String propName) throws IOException, JsonProcessingException {
if ("content".equals(propName)) {
MapModelResponseList response = (MapModelResponseList) beanOrClass;
// this probably needs null checks!
JsonNode details = (JsonNode) jp.getCodec().readTree(jp).get("data").get("details");
// read as array and create a Data object for each element
if (details.isArray()) {
List<Data> data = new java.util.ArrayList<Data>(details.size());
for (int i = 0; i < details.size(); i++) {
Data d = jp.getCodec().treeToValue(details.get(i), Data.class);
data.add(d);
}
response.setData(data);
}
// read a single object
else {
Data d = jp.getCodec().treeToValue(details, Data.class);
response.setData(java.util.Collections.singletonList(d));
}
super.handleUnknownProperty(jp, ctxt, beanOrClass, propName);
}
请注意,您没有实施 deserialize()
- 默认实施用于正常创建 MapModelResponseList
。 handleUknownProperty()
用于处理content
元素。由于超级调用中的@JsonIgnoreProperties(ignoreUnknown = true)
,您不关心的其他数据将被忽略。
这是一个迟到的答案,但我用不同的方式解决了它。它可以通过像这样在 Object
中捕获它来工作:
@JsonProperty("details")
public void setDetails(Object details) {
if (details instanceof List) {
setDetails((List) details);
} else if (details instanceof Map) {
setDetails((Map) details);
}
}
public void setDetails(List details) {
// your list handler here
}
public void setDetails(Map details) {
// your map handler here
}