Java 默认情况下,多态对象和字符串文本的Jackson自定义反序列化程序

Java 默认情况下,多态对象和字符串文本的Jackson自定义反序列化程序,java,spring,jackson,yaml,Java,Spring,Jackson,Yaml,我想在Spring Boot应用程序中使用Jackson从YAML反序列化具有以下属性的对象: 抽象类车辆,由船和车 为简单起见,假设两者都有一个名称,但只有船还有一个适航属性,而车有一个最高速度 使用@JsonTypeInfo和@JsonSubTypes在我的注释子类中,这一切都可以正常工作 现在,我想创建一个仅使用字符串值的速记,默认情况下,它应该创建一个名为: mode-of-transport: 'KITT' 我尝试创建自己的自定义序列化程序,但在大多数相关细节上遇到了问题。如果这

我想在Spring Boot应用程序中使用Jackson从YAML反序列化具有以下属性的对象:

  • 抽象类
    车辆
    ,由
  • 为简单起见,假设两者都有一个名称,但只有
    还有一个
    适航
    属性,而
    有一个
    最高速度
使用
@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;
        }
    }