gpt4 book ai didi

java - 在不使用包装类的情况下使用 Jackson 序列化 java 对象时维护子类型信息

转载 作者:搜寻专家 更新时间:2023-10-31 20:01:58 24 4
gpt4 key购买 nike

我正在尝试使用 Jackson 在 JSON 文件和具有两个 Java 子类的抽象类之间进行转换。理想情况下,我想使用如下 JSON:

没有封装的Json文档

[ {
"type" : "lion",
"name" : "Simba",
"endangered" : true,
"action" : "running"
}, {
"type" : "elephant",
"name" : "Dumbo",
"endangered" : false,
"table" : [ 1.0, 2.0, 3.0 ]
} ]

我已经注释了 Animal 抽象类,如 http://www.studytrails.com/java/json/java-jackson-Serialization-polymorphism.jsp 所示。

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type")
@JsonSubTypes({ @Type(value = Lion.class, name = "lion"),
@Type(value = Elephant.class, name = "elephant") })
public abstract class Animal {
String name;
boolean endangered;

//Skipped constructor/getters/setters

}

我可以使用包装器对象成功读/写它,但生成的 JSON 文件将包含一个额外的 animals 对象。

带包装器的 JSON 文档

{
"animals" : [ {
"type" : "lion",
"name" : "Simba",
"endangered" : true,
"action" : "running"
}, {
"type" : "elephant",
"name" : "Dumbo",
"endangered" : false,
"table" : [ 1.0, 2.0, 3.0 ]
} ]
}

当我使用普通 Java 列表时,我可以在没有包装器的情况下成功地从 Json 文档中读取,但是如果我尝试编写它,输出文件将如下所示:

输出.json

[ {
"name" : "Simba",
"endangered" : true,
"action" : "running"
}, {
"name" : "Dumbo",
"endangered" : false,
"table" : [ 1.0, 2.0, 3.0 ]
} ]

Java代码

// Test reading using raw list
JavaType listType = mapper.getTypeFactory()
.constructCollectionType(List.class, Animal.class);
List<Animal> jsonList = mapper.readValue(new FileInputStream(
"demo.json"), listType);
jsonDocument = new File(outputFile);
// Test writing using raw list
mapper.writerWithDefaultPrettyPrinter().writeValue(jsonDocument,
jsonList);

有什么想法可以在将对象序列化为 JSON 时包含类型信息吗?

可以在以下位置找到完整的 Eclipse 项目: https://github.com/nkatsar/json-subclass

编辑:简化了 Java 代码 以使用 JavaType 而不是 TypeReference。GitHub 上的代码也更新了正确的解决方案

编辑 2:如评论中所述,我最终使用对象数组进行序列化/反序列化,如下所示:

// Test reading using array
Animal[] jsonArray = mapper.readValue(
new FileInputStream(demoFile), Animal[].class);
System.out.println("Reading using array:\nObject: "
+ Arrays.toString(jsonArray));

// Test writing using array
outputJson = mapper.writeValueAsString(jsonArray);
System.out.println("Writing using array:\nJSON: " + outputJson);

最佳答案

这里的问题是Java类型删除,也就是说List对象的标称类型是List。并且由于 java.lang.Object 没有@JsonTypeInfo,它不会被启用。

但您可以配置 Jackson 使用特定的 Java 类型来转换 List 元素:

JavaType listJavaType = mapper.getTypeFactory().constructCollectionType(List.class, Animal.class);
String outputJson = mapper.writerWithType(listJavaType).writeValueAsString(myList);

修改了你的代码:

public static void main(String[] args) {

List<Animal> myList = new ArrayList<Animal>();
myList.add(new Lion("Simba"));
// myList.add(new Lion("Nala"));
myList.add(new Elephant("Dumbo"));
// myList.add(new Elephant("Lucy"));
AnimalList wrapperWrite = new AnimalList(myList);

try {
ObjectMapper mapper = new ObjectMapper();
JavaType listJavaType = mapper.getTypeFactory().constructCollectionType(List.class, Animal.class);

String outputJson = mapper.writerWithType(listJavaType).writeValueAsString(myList);
System.out.println(outputJson);

List wrapperRead = mapper.readValue(outputJson, List.class);
System.out.println("Read recently generated data: " + wrapperRead);

// Test reading using raw list
List<Animal> jsonList = mapper.readValue(new FileInputStream( "demo.json"), listJavaType);
System.out.println("Read from demo.json \ndata: " + jsonList);

outputJson = mapper.writerWithType(listJavaType).writeValueAsString(jsonList);
System.out.println(outputJson);

} catch (JsonGenerationException e) {
System.out.println("Could not generate JSON: " + e.getMessage());
} catch (JsonMappingException e) {
System.out.println("Invalid JSON Mapping: " + e.getMessage());
} catch (IOException e) {
System.out.println("File I/O error: ");
}

结果:

[{"type":"lion","name":"Simba","endangered":false,"action":null},{"type":"elephant","name":"Dumbo","endangered":false,"table":null}]
Read recently generated data: [{type=lion, name=Simba, endangered=false, action=null}, {type=elephant, name=Dumbo, endangered=false, table=null}]
Read from demo.json
data: [Animal(name=Nala, endangered=true, action=running}, Animal(name=Lucy, endangered=false, table=[1.0, 2.0, 3.0]}]
[{"type":"lion","name":"Nala","endangered":true,"action":"running"},{"type":"elephant","name":"Lucy","endangered":false,"table":[1.0,2.0,3.0]}]

另一个解决方案:您可以使用自定义 TypeReference 来通知 Jackson 需要使用什么类来转换 List:

mapper.writerWithType(new TypeReference<List<Animal>>() {}).writeValueAsString(myList)

该解决方案也可以工作,但我更喜欢主解决方案,因为它更干净,您不需要实例化抽象的“TypeReference”类...

关于java - 在不使用包装类的情况下使用 Jackson 序列化 java 对象时维护子类型信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27426327/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com