当前位置: 首页 > 知识库问答 >
问题:

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

微生良策
2023-03-14

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

  • 抽象车辆,由BoatCar
  • 实现
  • 为简单起见,想象两者都有一个名字,但只有Boat也有一个适航属性,而Car有一个极速
mode-of-transport:
  type: boat
  name: 'SS Boatface'
  seaworthy: true
----
mode-of-transport:
  type: car`
  name: 'KITT'
  top-speed: 123

使用@JsonTypeInfo@JsonSubTypes在我的注释子类中,这一切都可以正常工作!

现在,我想只使用一个字符串值创建一个速记,默认情况下,它应该创建一个带有该名称的Car

mode-of-transport: 'KITT'

我尝试创建自己的自定义序列化程序,但在大多数相关细节上遇到了问题。如果这是正确的方法,请帮助我填写:

public class VehicleDeserializer extends StdDeserializer<Merger> {

   /* Constructors here */

   @Override
   public Vehicle deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
      if (/* it is an OBJECT */){
         // Use the default polymorphic deserializer 
      } else if (/* it is a STRING */) {
         Car car = new Car();
         car.setName( /* the String value */ );
         return car;
      }
      return ???; /* what to return here? */
   }
}

我发现这两个答案很有启发性,但将其与多态类型相结合似乎会使问题变得更加困难:如何从Jackson中的自定义反序列化程序调用默认反序列化程序,并使用Jackson将其反序列化为字符串或对象

与这些问题中提供的解决方案不同的是:

  • 我正在处理YAML,而不是JSON。我不确定那里的细微差别
  • 我在反序列化程序中对字符串的“默认”类型进行硬编码没有问题,希望能使它更简单

共有2个答案

淳于慎之
2023-03-14

因此,有一种解决方案要求您使用反序列化ProblemHandler来处理jackson错误(因为您希望使用不同的输入解析相同的类型,使用常规方法很难实现这一点):

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;
    }
}

请注意,我使用的是JSON,而不是YAML,您还需要添加其他子类型。

诸葛彦
2023-03-14

这实际上比我想象的更容易解决。我得到了它使用以下工作:

  1. 自定义反序列化程序实现:
public class VehicleDeserializer extends StdDeserializer<Vehicle> {

    public VehicleDeserializer() {
        super(Vehicle.class);
    }

    @Override
    public Vehicle deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
        if (jp.currentToken() == JsonToken.VALUE_STRING) {
            Car car = new Car();
            car.setName(jp.readValueAs(String.class));
            return car;
        }
        return jp.readValueAs(Vehicle.class);
    }
}
public class Transport {

    @JsonDeserialize(using = VehicleDeserializer.class)
    @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
    private Vehicle modeOfTransport;

    // Getter, setters
}

这意味着默认情况下,车辆被反序列化为多态对象,除非显式指定使用我的自定义反序列化器反序列化它。如果输入不是String,则反序列化器将反过来遵从多态性。

希望这将帮助遇到此问题的人:)

 类似资料:
  • 我有下面的JSON,我正试图使用Jackson API反序列化它 我基本上需要一个附件类,它有一个AttachmentFile对象列表,如下所示: 如何使用自定义反序列化器实现这一点? 谢谢

  • I'va是一个OID接口,可以由许多具体类型实现: 现在我有一个具有两个字段的对象,一个使用抽象接口类型(OID)定义,另一个使用具体类型(MyOID)定义 我想使用jackson以不同的方式序列化/反序列化字段,无论它们是使用抽象接口类型还是具体类型定义的: 注意,被序列化,包括类型信息(多态序列化),而被序列化为文本 为此,我将OID接口注释为: 并为每个具体类型分配了类型id: 最后,对容器

  • 我想通过扩展默认的反序列化器来创建自己的反序列化器,在其后面设置更多的值: 如您所见,我还想将此DTO母类重用于其他DTO。 我没有找到任何这样的例子。我真的是世界上第一个 反序列化的“AsUsual”(p,ctxt)应该是什么 我应该使用什么motherclass?JsonDeserializer/StdDeserializer/UntypedObjectDeserializer 反序列化程序会

  • 如何基于json中指定的类类型,在Jackson中实现从json到Java对象的转换。 Java类型示例:

  • 我在Jackson的自定义反序列化程序中有一个问题。我想访问默认序列化程序来填充我要反序列化到的对象。在填充之后,我将执行一些自定义操作,但首先我要使用默认的Jackson行为反序列化对象。 这是我现在有的代码。 我需要的是一种初始化默认反序列化器的方法,这样我就可以在开始我的特殊逻辑之前预填充我的POJO。 当从自定义反序列化程序内调用deserialize时,无论我如何构造序列化程序类,该方法

  • 假设我正在为某个类编写自定义序列化,但希望使用默认方法处理其中一个字段。 那要怎么做? 序列化时,我们有。 但反序列化的相应方法是什么? 请注意以下代码: 如何实现MyOuterDeserializer?