Json 反序列化 Java /w Jackson 混合类型,包含在一个数组中
Json Deserialization in Java /w Jackson of mixed types, contained in one array
考虑以下 json,从 public API 获得:
anyObject : {
attributes: [
{
"name":"anyName",
"value":"anyValue"
},
{
"name":"anyName",
"value":
{
"key":"anyKey",
"label":"anyLabel"
}
}
]
}
如您所见,有时值是一个简单的字符串,有时是一个对象。是否有可能以某种方式将这些 json-results 反序列化为:
class AnyObject {
List<Attribute> attributes;
}
class Attribute {
private String key;
private String label;
}
我将如何设计我的模型来涵盖这两种情况。那可能吗 ?
尽管如其他人所指出的那样难以管理,但您可以随心所欲。添加自定义反序列化器来处理这种情况。我重写了你的 bean,因为我觉得你的属性 class 有点误导。对象中的 AttributeEntry class 是 "attributes" 列表中的一个条目。 ValueObject 是代表 "key"/"label" 对象的 class。这些 bean 在下面,但这里是自定义反序列化器。这个想法是检查 JSON 中的类型,并根据其 "value" 类型实例化适当的 AttributeEntry。
public class AttributeDeserializer extends JsonDeserializer<AttributeEntry> {
@Override
public AttributeEntry deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode root = p.readValueAsTree();
String name = root.get("name").asText();
if (root.get("value").isObject()) {
// use your object mapper here, this is just an example
ValueObject attribute = new ObjectMapper().readValue(root.get("value").asText(), ValueObject.class);
return new AttributeEntry(name, attribute);
} else if (root.get("value").isTextual()) {
String stringValue = root.get("value").asText();
return new AttributeEntry(name, stringValue);
} else {
return null; // or whatever
}
}
}
由于这种不明确的类型带来的不便,您将不得不在整个代码库中进行一些类型检查。
然后您可以将此自定义反序列化器添加到您的对象映射器中,如下所示:
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(AttributeEntry.class, new AttributeDeserializer());
objectMapper.registerModule(simpleModule);
这是 AttributeEntry:
public class AttributeEntry {
private String name;
private Object value;
public AttributeEntry(String name, String value) {
this.name = name;
this.value = value;
}
public AttributeEntry(String name, ValueObject attributes) {
this.name = name;
this.value = attributes;
}
/* getters/setters */
}
这是值对象:
public class ValueObject {
private String key;
private String label;
/* getters/setters */
}
考虑以下 json,从 public API 获得:
anyObject : {
attributes: [
{
"name":"anyName",
"value":"anyValue"
},
{
"name":"anyName",
"value":
{
"key":"anyKey",
"label":"anyLabel"
}
}
]
}
如您所见,有时值是一个简单的字符串,有时是一个对象。是否有可能以某种方式将这些 json-results 反序列化为:
class AnyObject {
List<Attribute> attributes;
}
class Attribute {
private String key;
private String label;
}
我将如何设计我的模型来涵盖这两种情况。那可能吗 ?
尽管如其他人所指出的那样难以管理,但您可以随心所欲。添加自定义反序列化器来处理这种情况。我重写了你的 bean,因为我觉得你的属性 class 有点误导。对象中的 AttributeEntry class 是 "attributes" 列表中的一个条目。 ValueObject 是代表 "key"/"label" 对象的 class。这些 bean 在下面,但这里是自定义反序列化器。这个想法是检查 JSON 中的类型,并根据其 "value" 类型实例化适当的 AttributeEntry。
public class AttributeDeserializer extends JsonDeserializer<AttributeEntry> {
@Override
public AttributeEntry deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode root = p.readValueAsTree();
String name = root.get("name").asText();
if (root.get("value").isObject()) {
// use your object mapper here, this is just an example
ValueObject attribute = new ObjectMapper().readValue(root.get("value").asText(), ValueObject.class);
return new AttributeEntry(name, attribute);
} else if (root.get("value").isTextual()) {
String stringValue = root.get("value").asText();
return new AttributeEntry(name, stringValue);
} else {
return null; // or whatever
}
}
}
由于这种不明确的类型带来的不便,您将不得不在整个代码库中进行一些类型检查。
然后您可以将此自定义反序列化器添加到您的对象映射器中,如下所示:
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(AttributeEntry.class, new AttributeDeserializer());
objectMapper.registerModule(simpleModule);
这是 AttributeEntry:
public class AttributeEntry {
private String name;
private Object value;
public AttributeEntry(String name, String value) {
this.name = name;
this.value = value;
}
public AttributeEntry(String name, ValueObject attributes) {
this.name = name;
this.value = attributes;
}
/* getters/setters */
}
这是值对象:
public class ValueObject {
private String key;
private String label;
/* getters/setters */
}