我正在尝试使用Programmer Bruce的一个教程,该教程应该允许对多态JSON进行反序列化。
完整列表可在此处找到 Programmer Bruce tutorials(很棒的东西顺便说一下)
我已经完成了前五个问题但没有遇到任何问题,但我在最后一个问题上遇到了障碍(例6),这当然是我真正需要工作的那个。
我在编译时遇到以下错误
ObjectMapper类型中的方法readValue(JsonParser,Class)不适用于参数(ObjectNode,Class)
并且它是由代码块引起的
public Animal deserialize(
JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
ObjectNode root = (ObjectNode) mapper.readTree(jp);
Class<? extends Animal> animalClass = null;
Iterator<Entry<String, JsonNode>> elementsIterator =
root.getFields();
while (elementsIterator.hasNext())
{
Entry<String, JsonNode> element=elementsIterator.next();
String name = element.getKey();
if (registry.containsKey(name))
{
animalClass = registry.get(name);
break;
}
}
if (animalClass == null) return null;
return mapper.readValue(root, animalClass);
}
}
特别是行
return mapper.readValue(root,animalClass);
之前是否有人遇到此问题,如果有,是否有解决方案?
我很感激任何人都能给予的帮助 提前致谢 Jon D.
答案 0 :(得分:99)
正如所承诺的,我正在举例说明如何使用注释来序列化/反序列化多态对象,我在你正在阅读的教程的Animal
类中基于这个例子。
首先,您的Animal
类包含子类的Json Annotations。
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "Dog"),
@JsonSubTypes.Type(value = Cat.class, name = "Cat") }
)
public abstract class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后是您的子类Dog
和Cat
。
public class Dog extends Animal {
private String breed;
public Dog() {
}
public Dog(String name, String breed) {
setName(name);
setBreed(breed);
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
}
public class Cat extends Animal {
public String getFavoriteToy() {
return favoriteToy;
}
public Cat() {}
public Cat(String name, String favoriteToy) {
setName(name);
setFavoriteToy(favoriteToy);
}
public void setFavoriteToy(String favoriteToy) {
this.favoriteToy = favoriteToy;
}
private String favoriteToy;
}
正如您所看到的,Cat
和Dog
没有什么特别之处,唯一了解它们的是abstract
类Animal
,因此在反序列化时,您将定位到Animal
,ObjectMapper
将返回实际实例,如以下测试中所示:
public class Test {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Animal myDog = new Dog("ruffus","english shepherd");
Animal myCat = new Cat("goya", "mice");
try {
String dogJson = objectMapper.writeValueAsString(myDog);
System.out.println(dogJson);
Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);
System.out.println("Deserialized dogJson Class: " + deserializedDog.getClass().getSimpleName());
String catJson = objectMapper.writeValueAsString(myCat);
Animal deseriliazedCat = objectMapper.readValue(catJson, Animal.class);
System.out.println("Deserialized catJson Class: " + deseriliazedCat.getClass().getSimpleName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行Test
类后输出:
{"@type":"Dog","name":"ruffus","breed":"english shepherd"}
Deserialized dogJson Class: Dog
{"@type":"Cat","name":"goya","favoriteToy":"mice"}
Deserialized catJson Class: Cat
希望这有帮助,
何塞路易斯答案 1 :(得分:5)
虽然@jbarrueta 的回答是完美的,但在 2.12 版本的 Jackson 中引入了期待已久的新类型 @JsonTypeInfo
注释,DEDUCTION
。
当您无法更改传入的 json 或不得更改时,它很有用。在无法确定使用哪个子类型的复杂情况下,我仍然建议使用 use = JsonTypeInfo.Id.NAME
作为新方法 may throw an exception。
现在你可以简单地写
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes({
@JsonSubTypes.Type(Dog.class),
@JsonSubTypes.Type(Cat.class) }
)
public abstract class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
它会产生 {"name":"ruffus", "breed":"english shepherd"}
和 {"name":"goya", "favoriteToy":"mice"}
再说一次,如果某些字段可能不存在,则使用 NAME
会更安全,例如 breed
或 favoriteToy
。
答案 2 :(得分:2)
通过Jackson库启用多态序列化/反序列化的一种简单方法是全局配置Jackson对象映射器(jackson.databind.ObjectMapper)以为某些类添加信息,例如具体的类类型,例如abstract类。
为此,请确保正确配置了映射器。 例如:
选项1:支持抽象类(和对象类型类)的多态序列化/反序列化
jacksonObjectMapper.enableDefaultTyping(
ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
选项2:支持抽象类(和对象类型类)的多态序列化/反序列化,以及这些类型的数组。
jacksonObjectMapper.enableDefaultTyping(
ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
参考:https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization
答案 3 :(得分:1)
您只需在类Animal
的声明之前一行,即可进行正确的多态序列化/反序列化:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class Animal {
...
}
此行的意思是:在序列化上添加元属性,或者在反序列化(include = JsonTypeInfo.As.PROPERTY
)上读取元属性,称为“ @class”(property = "@class"
),其中包含标准Java类名。 (use = JsonTypeInfo.Id.CLASS
。
因此,如果直接创建JSON(不进行序列化),请记住添加具有所需类名的元属性“ @class”,以进行正确的反序列化。
更多信息here
答案 4 :(得分:0)
如果使用的是fastxml,那么
可能需要进行这些更改
var mode;
initialize: function(element, options){
if(element.isTextbox1){
mode = 'textbox1';
}
else{
mode = 'textbox2';
}
return element[mode](options);
}
主要方法中的-
使用
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
而不是
SimpleModule module =
new SimpleModule("PolymorphicAnimalDeserializerModule");
并在Animal deserialize()函数中,进行以下更改
new SimpleModule("PolymorphicAnimalDeserializerModule",
new Version(1, 0, 0, null));
这适用于fasterxml.jackson。如果它仍然抱怨类字段。使用与json中相同的格式作为字段名称(使用&#34; _&#34; -underscore)。如此
//Iterator<Entry<String, JsonNode>> elementsIterator = root.getFields();
Iterator<Entry<String, JsonNode>> elementsIterator = root.fields();
//return mapper.readValue(root, animalClass);
return mapper.convertValue(root, animalClass);
可能不受支持。
//mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy());
答案 5 :(得分:0)
处理多态要么是模型绑定的,要么需要大量带有各种自定义反序列化器的代码。我是 JSON Dynamic Deserialization Library 的合著者,它允许独立于模型的 json 反序列化库。可以在下面找到 OP 问题的解决方案。请注意,这些规则的声明方式非常简短。
public class SOAnswer {
@ToString @Getter @Setter
@AllArgsConstructor @NoArgsConstructor
public static abstract class Animal {
private String name;
}
@ToString(callSuper = true) @Getter @Setter
@AllArgsConstructor @NoArgsConstructor
public static class Dog extends Animal {
private String breed;
}
@ToString(callSuper = true) @Getter @Setter
@AllArgsConstructor @NoArgsConstructor
public static class Cat extends Animal {
private String favoriteToy;
}
public static void main(String[] args) {
String json = "[{"
+ " \"name\": \"pluto\","
+ " \"breed\": \"dalmatian\""
+ "},{"
+ " \"name\": \"whiskers\","
+ " \"favoriteToy\": \"mouse\""
+ "}]";
// create a deserializer instance
DynamicObjectDeserializer deserializer = new DynamicObjectDeserializer();
// runtime-configure deserialization rules;
// condition is bound to the existence of a field, but it could be any Predicate
deserializer.addRule(DeserializationRuleFactory.newRule(1,
(e) -> e.getJsonNode().has("breed"),
DeserializationActionFactory.objectToType(Dog.class)));
deserializer.addRule(DeserializationRuleFactory.newRule(1,
(e) -> e.getJsonNode().has("favoriteToy"),
DeserializationActionFactory.objectToType(Cat.class)));
List<Animal> deserializedAnimals = deserializer.deserializeArray(json, Animal.class);
for (Animal animal : deserializedAnimals) {
System.out.println("Deserialized Animal Class: " + animal.getClass().getSimpleName()+";\t value: "+animal.toString());
}
}
}
pretius-jddl 的 Maven 依赖(在 maven.org/jddl 查看最新版本:
<dependency>
<groupId>com.pretius</groupId>
<artifactId>jddl</artifactId>
<version>1.0.0</version>
</dependency>