我正在尝试使用Jackson在Java中使用两个子类在JSON文件和抽象类之间进行转换。理想情况下,我想使用JSON如下:
没有包装的Json文档
[ {
"type" : "lion",
"name" : "Simba",
"endangered" : true,
"action" : "running"
}, {
"type" : "elephant",
"name" : "Dumbo",
"endangered" : false,
"table" : [ 1.0, 2.0, 3.0 ]
} ]
我在http://www.studytrails.com/java/json/java-jackson-Serialization-polymorphism.jsp
上注明了Animal
抽象类
@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文档中读取没有包装器但是如果我尝试编写它,输出文件将如下所示:
output.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: 正如评论中所提到的,我最终使用Object数组进行序列化/反序列化,如下所示:
// 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);
答案 0 :(得分:5)
这里的问题是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)
该解决方案也可以使用,但我更喜欢主要解决方案,因为它更干净,您不需要实例化抽象&#39; TypeReference&#39;类...