Jackson序列化和反序列化经常遇到的几个问题

1 阅读2分钟

一、反序列化抛出UnrecognizedPropertyException异常

@Test
public void testDeserializeWithUnknownField() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new Jdk8Module());

    String jsonString = "{"name":"Alice","optionalAge":25,"unknownField":true}";
    // 此处会抛出UnrecognizedPropertyException
    OptionalField optionalField = mapper.readValue(jsonString, OptionalField.class);
    System.out.println(optionalField.getName());
    System.out.println(optionalField.getOptionalAge().get());
}


private static class OptionalField {
    private String name;
    private Optional<Integer> optionalAge;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Optional<Integer> getOptionalAge() {
        return optionalAge;
    }

    public void setOptionalAge(Optional<Integer> optionalAge) {
        this.optionalAge = optionalAge;
    }
}

问题原因

Json字符串中包含的字段未在Java类型中声明

解决方案

第一种方式是设置忽略这种错误,对于整个ObjectMapper对象生效。

new ObjectMapper()
  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

第二种方式是在Java类型上表明忽略未知的字段,这种方式只对此Java类型生效。

@JsonIgnoreProperties(ignoreUnknown = true)
public class Person{
    private String name;
    private int age;
}

二、序列化排除字段

这个实际上是开发过程中经常遇到的一个场景,就是我们的Java类型序列化后的json字符串想不包括某些字段。 可以使用@JsonIgnore标记需要排除的字段,参考下面的代码。

@Test
public void testSerializeIgnoreField() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new Jdk8Module());

    OptionalField optionalField = new OptionalField();
    optionalField.setName("Alice");
    optionalField.setOptionalAge(Optional.of(25));
    optionalField.setIgnoreField("ignore");

    String jsonString = mapper.writeValueAsString(optionalField);
    Assertions.assertFalse((jsonString.contains("ignore")));
}


@JsonIgnoreProperties(ignoreUnknown = true)
private static class OptionalField {
    private String name;
    private Optional<Integer> optionalAge;
    @JsonIgnore
    private String ignoreField;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Optional<Integer> getOptionalAge() {
        return optionalAge;
    }

    public void setOptionalAge(Optional<Integer> optionalAge) {
        this.optionalAge = optionalAge;
    }

    public String getIgnoreField() {
        return ignoreField;
    }

    public void setIgnoreField(String ignoreField) {
        this.ignoreField = ignoreField;
    }
}

三、反序列化抽象字段类型

通过自定义反序列化器实现,参考下面的代码

public class Wrapper {

    private Vehicle vehicle;

    public Vehicle getVehicle() {
        return vehicle;
    }

    public void setVehicle(Vehicle vehicle) {
        this.vehicle = vehicle;
    }
}

public abstract class Vehicle {

    protected String  type;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}


public class Car extends Vehicle{

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}


public class Bicycle extends Vehicle{

    private String name;

    private BigDecimal weight;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getWeight() {
        return weight;
    }

    public void setWeight(BigDecimal weight) {
        this.weight = weight;
    }
}

// 自定义反序列化器
public class VehicleDeserializer extends StdDeserializer<Vehicle> {

    public VehicleDeserializer() {
        this(null);
    }

    protected VehicleDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Vehicle deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JacksonException {
        JsonNode node = jp.getCodec().readTree(jp);
        String type = node.get("type").asText();
        if ("car".equals(type)) {
            return jp.getCodec().treeToValue(node, Car.class);
        } else if ("bicycle".equals(type)) {
            return jp.getCodec().treeToValue(node, Bicycle.class);
        } else {
            throw new IllegalArgumentException("Unknown vehicle type: " + type);
        }
    }
}


@Test
public void abstractFieldDeserializableTest() throws Exception{
    String json = "{"vehicle":{"type":"car","name":"car","age":1}}";
    ObjectMapper mapper = new ObjectMapper();
    // 注册反序列化器
    SimpleModule module = new SimpleModule();
    module.addDeserializer(Vehicle.class, new VehicleDeserializer());
    mapper.registerModule(module);
    Wrapper wrapper = mapper.readValue(json, Wrapper.class);
    Assertions.assertTrue(wrapper.getVehicle().type.equals("car"));
}