mon*_*jbl 16 java serialization json jackson
杰克逊正在做一些真正奇怪的事情,我找不到任何解释.我正在进行多态序列化,当一个对象独立时它可以很好地工作.但是,如果将相同的对象放入列表并将列表序列化,则会删除类型信息.
它丢失类型信息的事实将导致人们怀疑类型擦除.但是在序列化列表内容期间会发生这种情况; 杰克逊所要做的就是检查它正在序列化的当前对象以确定其类型.
我用Jackson 2.5.1创建了一个例子:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;
public class Test {
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
@Type(value = Dog.class, name = "dog"),
@Type(value = Cat.class, name = "cat")})
public interface Animal {}
@JsonTypeName("dog")
public static class Dog implements Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@JsonTypeName("cat")
public static class Cat implements Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) throws JsonProcessingException {
List<Cat> list = new ArrayList<>();
list.add(new Cat());
System.out.println(new ObjectMapper().writeValueAsString(list));
System.out.println(new ObjectMapper().writeValueAsString(list.get(0)));
}
}
Run Code Online (Sandbox Code Playgroud)
这是输出:
[{"name":null}]
{"@type":"cat","name":null}
Run Code Online (Sandbox Code Playgroud)
如您所见,当对象在列表中时,Jackson不会添加类型信息.有谁知道为什么会这样?
Sot*_*lis 20
这里和这里讨论了发生这种情况的各种原因.我不一定与理由同意,但杰克逊,因为类型擦除,并不蝙蝠知道元素的类型List(或Collection或Map)含有.它选择使用不能解释注释的简单序列化程序.
您可以在这些链接中建议两个选项:
首先,您可以创建一个实现的类,List<Cat>适当地实例化它并序列化实例.
class CatList implements List<Cat> {...}
Run Code Online (Sandbox Code Playgroud)
泛型类型参数Cat不会丢失.杰克逊可以使用它并使用它.
其次,您可以实例化并使用ObjectWriter类型List<Cat>.例如
System.out.println(new ObjectMapper().writerFor(new TypeReference<List<Cat>>() {}).writeValueAsString(list));
Run Code Online (Sandbox Code Playgroud)
将打印
[{"@type":"cat","name":"heyo"}]
Run Code Online (Sandbox Code Playgroud)
mon*_*jbl 11
Sotirios Delimanolis给出的答案是正确的.但是,我认为将此解决方法作为单独的答案发布是很好的.如果你所处的环境中你不能为你需要返回的每种类型的事物更改ObjectMapper(比如Jersey/SpringMVC webapp),那么还有另一种选择.
您可以在包含该类型的类中包含一个私有final字段.该字段对于类外的任何内容都不可见,但如果您使用@JsonProperty("@type")(或"@class"或任何类型字段命名)对其进行注释,则无论对象位于何处,Jackson都会对其进行序列化.
@JsonTypeName("dog")
public static class Dog implements Animal {
@JsonProperty("@type")
private final String type = "dog";
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Run Code Online (Sandbox Code Playgroud)
我也遇到了这个问题,这是我更喜欢的解决方法(我使用的是 Kotlin,但使用 Java 时几乎相同)
父类配置@JsonTypeInfo使用现有属性作为标记,打破子类型的歧义
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY)
@JsonSubTypes(
JsonSubTypes.Type(value = Bob::class, name = "bob"),
JsonSubTypes.Type(value = Alice::class, name = "alice")
)
abstract class Person {
abstract val jacksonMarker: String
@JsonProperty("@type")
get
// ... rest of the class
}
Run Code Online (Sandbox Code Playgroud)
子类:
class Bob: Person {
override val jacksonMarker: String
get() = "bob"
// ... rest of the class
}
class Alice: Person {
override val jacksonMarker: String
get() = "alice"
// ... rest of the class
}
Run Code Online (Sandbox Code Playgroud)
你已经准备好了。
| 归档时间: |
|
| 查看次数: |
9697 次 |
| 最近记录: |