【问题标题】:Unable to perform custom deserialisation when using a builder使用构建器时无法执行自定义反序列化
【发布时间】:2020-04-25 00:01:27
【问题描述】:

我有一种情况,我从外部源接收一个布尔值作为字符串“0”或“1”。 映射时,杰克逊不接受这一点并抛出以下错误:

InvalidFormatException:无法反序列化 boolean 类型的值 从字符串“1”

因此,我正在执行以下有效的自定义反序列化。

在没有构建器的情况下完成的工作示例

@Getter
public class MyClass {

    @JsonDeserialize(using = NumericBooleanDeserializer.class)
    @JsonProperty("bool")
    private boolean bool;

    // many other fields
}

class NumericBooleanDeserializer extends JsonDeserializer<Boolean> {

    @Override
    public Boolean deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        return "1".equals(parser.getText());
    }
}

但我需要它来与构建器一起工作并按如下方式编写它。这不再有效。它抛出与上面相同的错误。 有没有办法让构建者使用自定义反序列化?请注意,除非根本不可能,否则我想坚持使用 Lombok 的 Builder。谢谢。

与建设者合作失败的例子。 (我想修复它以使其正常工作)

@Getter
@JsonDeserialize(builder = MyClass.MyClassBuilder.class)
@Builder
public class MyClass {

    @JsonDeserialize(using = NumericBooleanDeserializer.class)
    @JsonProperty("bool")
    private boolean bool;

    // many other fields
}

class NumericBooleanDeserializer extends JsonDeserializer<Boolean> {

    @Override
    public Boolean deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        return "1".equals(parser.getText());
    }
}

如果您愿意,可以使用快速测试用例进行测试。

public class ATest {
    @Test
    public void myTest() throws IOException {
        ObjectMapper mapper = new ObjectMapper();

        // test passes if MyClass doesn't use builder. 
        MyClass myClass = mapper.readValue("{\"bool\":\"1\"}", MyClass.class);
        assertTrue(myClass.isBool());
    }
}

编辑:按照下面的答案尝试使用建议的选项

选项 1:

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MyClass {

    @JsonDeserialize(using = NumericBooleanDeserializer.class)
    @JsonProperty("bool")
    private boolean bool;

    // many other fields

    @JsonSetter
    private void setBool(String value){
        this.bool = "1".equals(value);
    }
}

class NumericBooleanDeserializer extends JsonDeserializer<Boolean> {

    @Override
    public Boolean deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        return !"0".equals(parser.getText());
    }
}

得到以下错误:

com.fasterxml.jackson.databind.JsonMappingException: 问题 反序列化属性'bool'(预期类型:[简单类型,类 java.lang.String];实际类型:java.lang.Boolean),问题: [Source: (String)"{"bool":"1"}" 处的参数类型不匹配;线:1, 列:9](通过引用链:a.a.a.MyClass["bool"])

选项2:

@Getter
@JsonDeserialize(builder = MyClass.MyClassBuilder.class)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MyClass {

    @JsonDeserialize(using = NumericBooleanDeserializer.class)
    @JsonProperty("bool")
    private boolean bool;
}

得到以下错误:

com.fasterxml.jackson.databind.exc.InvalidFormatException: 不能 从字符串“1”反序列化 boolean 类型的值:仅“真”或 “假”在 [Source: (String)"{"bool":"1"}";线:1, 栏:9](通过参考链: a.a.a.MyClass$MyClassBuilder["bool"])

【问题讨论】:

    标签: java jackson lombok


    【解决方案1】:

    2020-11-03 更新:Lombok 1.18.16 发布。

    从 Lombok 1.18.16 开始,您可以使用 @Jacksonized 自动生成 Jackson 使用 Lombok @(Super)Builder 所需的一切:

    @Getter
    @Jacksonized
    @Builder
    public class MyClass {
        @JsonDeserialize(using = NumericBooleanDeserializer.class)
        private boolean bool;
    
        // many other fields
    }
    

    对于早期的 Lombok 版本,您必须自定义构建器,以便将注释添加到构建器的 setter 方法。在您的情况下,它的工作原理如下:

    @Getter
    @Builder
    @JsonDeserialize(builder = MyClass.MyClassBuilder.class)
    public class MyClass {
        private boolean bool;
    
        // many other fields
    
        public static class MyClassBuilder {
            @JsonDeserialize(using = NumericBooleanDeserializer.class)
            @JsonProperty("bool")
            public MyClassBuilder bool(boolean bool) {
                this.bool = bool;
                return this;
            }
        }
    }
    

    Lombok 认识到已经有一个构建器的类定义,并且只为您没有手动实现的方法生成代码。

    【讨论】:

    • 完美这个作品!但是,考虑到现在为 bool 方法添加了注释,我是否可以说不再需要注释字段本身?当我删除 bool 变量的注释时,测试它工作正常。想确认您在两端都注释的任何原因吗?
    • 是的。我只是将注释留在那里以保持一致性,但如果您仅使用反序列化,则不需要它们。
    • 我更新了答案(删除了字段上的注释)。
    • 我用新的 @Jacksonized 方法更新了答案(使用 lombok >= 1.18.16)。
    • 感谢您的回答。有效!但是,我有两个带有构建器注释的模型。其中一个模型(它只包含字符串变量)被反序列化没有任何问题。对于一个模型(它只包含一个链接列表),我必须添加这个 @Jacksonized 来反序列化它。为什么?
    猜你喜欢
    • 2020-05-27
    • 2013-09-09
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多