我研究了使用继承实现Objects类的最佳方法,并将其作为List包含在对象中。
如果您知道处理该方案的任何链接并提供正确的答案,我将很乐意接受此作为重复并关闭此项目。 我有以下课程:
动物园
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
import lombok.*;
@Getter
@Setter
@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Zoo {
private String zooName;
private String zooLocation;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private @Singular List<Animal> animals;
}
动物
@Getter
@Setter
@Builder(builderMethodName = "parentBuilder")
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NONE,
include = JsonTypeInfo.As.PROPERTY,
property = "type",
defaultImpl = Animal.class
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Elephant.class, name = "elephant")
})
public class Animal{
private String type;
private String nameTag;
}
狗
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@JsonPropertyOrder({ "type", "nameTag"})
@JsonTypeName("dog")
public class Dog extends Animal{
private String barkingPower;
}
象
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@JsonPropertyOrder({ "type", "nameTag"})
public class Elephant extends Animal{
private String weight;
}
ModelHelper
public final class ModelHelper {
private static final ObjectMapper MAPPER = new ObjectMapper();
.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.enableDefaultTypingAsProperty(
ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, "type");
public static <T> T fromJson(@NotNull final String json, final Class<T> objectClass) {
try {
return MAPPER.readValue(json, objectClass);
} catch (Exception e) {
final String message = String.format("Error de-serialising JSON '%s' to object of %s",
json, objectClass.getName());
log.warn(message, e);
return null;
}
}
}
我正在使用ModelHelper将JSON反序列化为Object:
String json = "{\"zooName\":\"Sydney Zoo\",\"zooLocation\":\"Sydney\",\"animals\":[{\"type\":\"dog\",\"nameTag\":\"Dog1\",\"barkingPower\":\"loud\"},{\"type\":\"elephant\",\"nameTag\":\"Elephant1\",\"weight\":\"heavy\"}]}";
mapper.readValue(json, Zoo.class);
目前,Zoo
的反序列化仅返回Animal
个属性,而不是Dog
或Elephant
属性。
我的问题是:
Zoo
中List<Animal>
的getter是基类类型,反序列化无法解决如何创建Dog
或Elephant
,根据签名生成Animal
。但是我想通过放JsonTypeName
和JsonSubTypes
注释我已经标记了相关的子类。是这样的吗?Animal
类定义为abstract
才能使其生效?如果代码不清楚,请告诉我,我会修复它。
答案 0 :(得分:1)
我在@programerB(感谢Bruce)发布的stackoverflow回答中解决了问题: Dynamic polymorphic type handling with Jackson
我最终使用示例6实现了它。
答案:我看到的问题是进入json的类型有&#34;作为检索JSON节点值时字符串的一部分。我认为这就是为什么基于JsonTypeName的自动处理无法识别正确的子类。
String json = "{\"zooName\":\"Sydney Zoo\",\"zooLocation\":\"Sydney\",\"animals\":[{\"type\":\"dog\",\"nameTag\":\"Dog1\",\"barkingPower\":\"loud\"},{\"type\":\"elephant\",\"nameTag\":\"Elephant1\",\"weight\":\"heavy\"}]}";
如果你在程序员布鲁斯解决方案中看到例6。为了在代码块之后识别正确的子类:
String name = element.getKey();
if (registry.containsKey(name))
{
animalClass = registry.get(name);
break;
}
这是我使用element.getValue来识别正确的子类的地方。在比较值之前,我不得不去除引号(&#34;)。此外,如果您使用的是fastxml。您必须替换以下
return mapper.readValue(root, animalClass);
与
return mapper.readValue(root.toString(), animalClass);
答案:动物类不需要是抽象的。非抽象类可以是基类。在我的例子中,我甚至已经实例化了这个类。
答案:基于我所观察到的,自定义反序列化是处理此问题的最佳/最佳方式。自定义反序列化器是Animal类所必需的,而不是Zoo类。如上所述,要遵循的链接是:http://programmerbruce.blogspot.com.au/2011/05/deserialize-json-with-jackson-into.html。
我可以将此标记为programmerbruce所回答的问题的副本,但我认为我解释问题并提供解决方案的方式可能会帮助其他人。
答案 1 :(得分:0)
您正在混合使用3种方法来反序列化多态类型,考虑到Jackson经历了多少次迭代,这并不奇怪。
您使用
标记了Animal
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE, include = JsonTypeInfo.As.PROPERTY, property = "type")
这告诉杰克逊2矛盾的事情:
include
和property
字段声明您要使用名为type
use
字段声明不应使用ID机制,因此无效地忽略您的类型信息。最简单的&#34;修复&#34;是使用JsonTypeInfo.Id.NAME
代替,它将输出(我已经添加了Lombok&#39; s @ToString
):
Zoo(zooName=Sydney Zoo, zooLocation=Sydney, animals=[Dog(barkingPower=loud), Elephant(weight=heavy)])
所以现在你有正确的类型识别。请注意,在这种情况下,不需要子类上的@JsonTypeName
注释,因为您已经指定了解析类型的方法:@JsonTypeInfo
告诉反序列化器查找type
属性并且@JsonSubTypes
告诉它将其值(name
属性)映射到其类(value
属性)。您也不需要指定任何ObjectMapper
配置。
当时使用@JsonTypeName
是什么?它取代了name
中的@JsonSubTypes
:
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Elephant.class) // no name
})
public class Animal { ... }
public class Dog extends Animal { ... }
@JsonTypeName("elephant") // name is here
public class Elephant extends Animal { ... }
有用,例如,如果您无法访问超类。
在没有较少注释的情况下完成所有这些操作的另一种方法(再次,如果您无法访问类,则非常有用)是通过配置映射器。而不是@JsonSubTypes
,您可以使用
MAPPER.registerSubtypes(new NamedType(Dog.class, "dog"), new NamedType(Elephant.class, "elephant"));
包括此处的类型名称类映射。
最有可能采用更多方法来混合配置,但这足以证明这一点并回答您的问题。
问题是我无法将超类属性与子类属性一起反序列化。它是一个或另一个。也许自定义解串器确实可以解决这个问题。