Java 默认情况下,多态对象和字符串文本的Jackson自定义反序列化程序
我想在Spring Boot应用程序中使用Jackson从YAML反序列化具有以下属性的对象:Java 默认情况下,多态对象和字符串文本的Jackson自定义反序列化程序,java,spring,jackson,yaml,Java,Spring,Jackson,Yaml,我想在Spring Boot应用程序中使用Jackson从YAML反序列化具有以下属性的对象: 抽象类车辆,由船和车 为简单起见,假设两者都有一个名称,但只有船还有一个适航属性,而车有一个最高速度 使用@JsonTypeInfo和@JsonSubTypes在我的注释子类中,这一切都可以正常工作 现在,我想创建一个仅使用字符串值的速记,默认情况下,它应该创建一个名为: mode-of-transport: 'KITT' 我尝试创建自己的自定义序列化程序,但在大多数相关细节上遇到了问题。如果这
- 抽象类
,由车辆
和船
车
- 为简单起见,假设两者都有一个名称,但只有
还有一个船
属性,而适航
有一个车
最高速度
@JsonTypeInfo
和@JsonSubTypes
在我的注释子类中,这一切都可以正常工作
现在,我想创建一个仅使用字符串值的速记,默认情况下,它应该创建一个名为:
mode-of-transport: 'KITT'
我尝试创建自己的自定义序列化程序,但在大多数相关细节上遇到了问题。如果这是正确的方法,请帮助我填写:
公共类车辆序列化程序扩展StdDeserializer{
/*这里的构造器*/
@凌驾
公共车辆反序列化(JSONP,反序列化上下文ctxt)引发IOException{
如果(/*它是一个对象*/){
//使用默认的多态反序列化程序
}else如果(/*是字符串*/){
汽车=新车();
setName(/*字符串值*/);
返回车;
}
返回???;/*在这里返回什么*/
}
}
我发现这两个答案很有启发性,但将其与多态类型结合起来似乎更难做到:以及
与这些问题中提供的解决方案不同的是:
- 我正在处理YAML,而不是JSON。我不确定那里的细微差别
- 我在反序列化程序中对字符串的“默认”类型进行硬编码没有问题,希望能使它更简单
因此,有一种解决方案要求您使用反序列化ProblemHandler处理jackson错误(因为您希望使用不同的输入解析同一类型,所以使用常规方法很难实现这一点):
公共类MyTest{
@试验
public void doTest()抛出JsonParseException、JsonMappingException、IOException{
最终ObjectMapper om=新ObjectMapper();
addHandler(新的反序列化ProblemHandler(){
@凌驾
公共对象handleMissingInstallator(最终反序列化上下文ctxt、最终类instClass、最终JsonParser p、最终字符串msg)引发IOException{
if(仪表等级等于(汽车等级)){
final JsonParser parser=ctxt.getParser();
final String text=parser.getText();
开关(文本){
案例“凯特”:
归还新车();
}
}
未处理退货;
}
@凌驾
公共JavaType handleMissingTypeId(最终反序列化上下文ctxt、最终JavaType baseType、最终类型idResolver、最终字符串failureMsg)引发IOException{
//if(基本类型ISTYPE或子类型OF(车辆等级)){
final JsonParser parser=ctxt.getParser();
final String text=parser.getText();
开关(文本){
案例“凯特”:
返回TypeFactory.defaultInstance().constructType(Car.class);
}
返回super.handleMissingTypeId(ctxt、baseType、idResolver、failureMsg);
}
});
最终容器objectValue=om.readValue(getObjectJson(),Container.class);
assertTrue(汽车的objectValue.getModeOfTransport()实例);
最终容器stringValue=om.readValue(getStringJson(),Container.class);
assertTrue(stringValue.getModeOfTransport()Car的instanceof);
}
私有字符串getObjectJson(){
返回“{\”modeOfTransport\”:{\“type\”:“car\”,“name\”:“KITT\”,“speed\”:1}}”;
}
私有字符串getStringJson(){
返回“{\”modeOfTransport\”:\“KITT\”}”;
}
}
类容器{
私家车运输模式;
公共车辆getModeOfTransport(){
返回运输方式;
}
公共运输模式(最终车辆运输模式){
this.modeOfTransport=运输方式;
}
}
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.PROPERTY,PROPERTY=“type”,visible=true)
@JsonSubTypes({
@类型(name=“car”,value=car.class)
})
抽象类车辆{
保护字符串类型;
受保护的字符串名称;
公共字符串getType(){
返回类型;
}
公共void集合类型(最终字符串类型){
this.type=type;
}
公共字符串getName(){
返回名称;
}
public void setName(最终字符串名){
this.name=名称;
}
}
@JsonTypeName(“汽车”)
二等车{
私人整数速度;
public int getSpeed(){
返回速度;
}
公共无效设置速度(最终整型速度){
速度=速度;
}
}
请注意,我使用的是JSON,而不是YAML,您还需要添加其他子类型。这实际上比我想象的更容易解决。我使用以下方法实现了它:
公共类车辆序列化程序扩展StdDeserializer{
公共车辆柴油机(){
超级(车辆级);
}
@凌驾
公共车辆反序列化(JsonParser jp,反序列化上下文ctxt)引发IOException{
if(jp.currentToken()==JsonToken.VALUE\u字符串){
汽车=新车();
setName(jp.readValueAs(String.class));
返回车;
}
返回jp.readValueAs(车辆类别);
}
}
@JsonTypeInfo
和@JsonSubTypes
一起工作
mode-of-transport: 'KITT'
public class MyTest {
@Test
public void doTest() throws JsonParseException, JsonMappingException, IOException {
final ObjectMapper om = new ObjectMapper();
om.addHandler(new DeserializationProblemHandler() {
@Override
public Object handleMissingInstantiator(final DeserializationContext ctxt, final Class<?> instClass, final JsonParser p, final String msg) throws IOException {
if (instClass.equals(Car.class)) {
final JsonParser parser = ctxt.getParser();
final String text = parser.getText();
switch (text) {
case "KITT":
return new Car();
}
}
return NOT_HANDLED;
}
@Override
public JavaType handleMissingTypeId(final DeserializationContext ctxt, final JavaType baseType, final TypeIdResolver idResolver, final String failureMsg) throws IOException {
// if (baseType.isTypeOrSubTypeOf(Vehicle.class)) {
final JsonParser parser = ctxt.getParser();
final String text = parser.getText();
switch (text) {
case "KITT":
return TypeFactory.defaultInstance().constructType(Car.class);
}
return super.handleMissingTypeId(ctxt, baseType, idResolver, failureMsg);
}
});
final Container objectValue = om.readValue(getObjectJson(), Container.class);
assertTrue(objectValue.getModeOfTransport() instanceof Car);
final Container stringValue = om.readValue(getStringJson(), Container.class);
assertTrue(stringValue.getModeOfTransport() instanceof Car);
}
private String getObjectJson() {
return "{ \"modeOfTransport\": { \"type\": \"car\", \"name\": \"KITT\", \"speed\": 1}}";
}
private String getStringJson() {
return "{ \"modeOfTransport\": \"KITT\"}";
}
}
class Container {
private Vehicle modeOfTransport;
public Vehicle getModeOfTransport() {
return modeOfTransport;
}
public void setModeOfTransport(final Vehicle modeOfTransport) {
this.modeOfTransport = modeOfTransport;
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true)
@JsonSubTypes({
@Type(name = "car", value = Car.class)
})
abstract class Vehicle {
protected String type;
protected String name;
public String getType() {
return type;
}
public void setType(final String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
@JsonTypeName("car")
class Car extends Vehicle {
private int speed;
public int getSpeed() {
return speed;
}
public void setSpeed(final int speed) {
this.speed = speed;
}
}