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

@Jacksonize反序列化不适用于混合-“没有从字符串值反序列化的字符串参数构造函数/工厂方法”

陶和歌
2023-03-14

我试图从包含地图的非标准表示形式的csv字符串中反序列化一个数组(该数组通过Lombok使用生成器模式,带有@Jacksonized)。

我的主题:

@JsonPropertyOrder({
        "strField",
        "mapField",
})
@Getter
@Jacksonized
@Builder(toBuilder = true)
@EqualsAndHashCode
@ToString
public class MyObject {

    private final String strField;

    @Builder.Default
    private final Map<String, Float> mapField = new HashMap<>();
}

具有非标准映射域表示的csv示例:

strField,mapField
abc,"key1=2.0;key2=3.0"

我正在使用混合来尝试在不重写整个对象的情况下实现这一点:

@JsonPropertyOrder({
            "strField",
            "mapField",
})
public abstract class MyObjectDeserializerMixin {

    @JsonDeserialize(using = StringToMapDeserializer.class)
    private Map<String, Float> mapField;
}

...正如您在上面看到的,它指向一个自定义反序列化器:

public class StringToMapDeserializer extends JsonDeserializer<Map<String, Float>> {

    @Override
    public Map<String, Float> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        String csvFormattedMap = jsonParser.getText().trim();

        return Arrays.stream(csvFormattedMap.split(";"))
                .map(keyValue -> keyValue.split("="))
                .collect(Collectors.toMap(keyValue -> keyValue[0], keyValue -> Float.parseFloat(keyValue[1])));
    }
}

总而言之,我正在配置和使用我的CsvMapper,如下所示:

    CsvMapper csvMapper = new CsvMapper();
    csvMapper.addMixIn(MyObject.class, MyObjectDeserializerMixin.class);

    CsvSchema csvSchema = csvMapper
            .schemaFor(MyObject.class)
            .withHeader();

    ObjectReader csvReader = csvMapper.readerFor(MyObject.class).with(csvSchema);
    List<MyObject> myObjects = csvReader.<MyObject>readValues(theCsvString).readAll();

但是,我得到以下例外:

通用域名格式。fasterxml。杰克逊。数据绑定。exc.MismatchedInputException:无法构造java的实例。util。LinkedHashMap(尽管至少存在一个创建者):在[源:(StringReader);行:2,列:53](通过引用链:myPackage.MyObject$MyObjectBuilder[“mapField”])处,没有从字符串值('key1=2.0;key2=3.0)反序列化的字符串参数构造函数/工厂方法

at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1432)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1062)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
at com.fasterxml.jackson.databind.deser.ValueInstantiator.createFromString(ValueInstantiator.java:258)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:357)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:29)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:158)
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.vanillaDeserialize(BuilderBasedDeserializer.java:269)
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:193)
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1719)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1261)
...

堆栈跟踪似乎试图使用一个BuilderBasedDeserializer,它试图使用映射反序列化器。java,因此它似乎不知道我的自定义反序列化程序。我使用了一个非常类似的工作流和一个自定义序列化程序来编写相同的csv,所以我不明白为什么这不起作用。排除故障的下一步是什么?

共有1个答案

柳钟展
2023-03-14

当使用生成器反序列化时,Jackson只考虑生成器类上的注释,而不考虑要反序列化的实际类上的注释。龙目的Jacksonized帮助您自动将所有相关注释复制到生成器类及其setter方法

然而,Lombok只能在类上静态显示注释的情况下才能做到这一点。来自mixin的任何动态注释都无法复制,因为龙目岛不知道它们。您可以将@jsondeseialize放在实际类的映射字段中,这样龙目就能将其复制到生成器中。但这显然与混合的目的背道而驰。

幸运的是,还有更好的方法。您还可以将mixin添加到builder类中,如下所示:

csvMapper.addMixIn(MyObjectBuilder.class, MyObjectDeserializerMixin.class);

严格来说,您不再需要MyObject上的mixin。但是,如果您也进行了序列化,并且mixin中有与序列化相关的注释,那么您应该将mixin添加到这两者中。

然而,在您的情况下,这还不够,因为您使用的是构建器。默认值。通过该注释,Lombok在生成器中创建了一个名为mapField$value的字段。Jackson不会将mixin中的字段(及其注释)与该字段匹配,因为它们的名称不同。您可以通过在mixin中定义和注释setter方法来解决这个问题:

public abstract class MyObjectDeserializerMixin {
    @JsonDeserialize(using = StringToMapDeserializer.class)
    public abstract void mapField(Map<String, Float> mapField);
}

您可以在此处使用构建器的实际返回类型,但val也足够了。由于@JsonDeserializ仅用于反序列化目的,您可以安全地从混合类中删除mapField及其注释。

用龙目1.18.20和杰克逊2.12.2测试。

 类似资料: