简体   繁体   English

Jackson:将缺少的字段反序列化为 Kotlin/Java 列表

[英]Jackson: deserialize missing field to Kotlin/Java List

I have the answer to my own question, so I post both the answer and solution, as explicitly encouraged by Jeff Atwood.我有自己问题的答案,因此我发布了答案和解决方案,这是 Jeff Atwood 明确鼓励的。 My question was originally for Kotlin, but while trying to find a solution, I also tried Java, so I provide the question and solution in both Java and Kotlin.) My question was originally for Kotlin, but while trying to find a solution, I also tried Java, so I provide the question and solution in both Java and Kotlin.)

Question in Kotlin Kotlin 中的问题

Given this deserializable Product class:鉴于此可反序列化Product class:

data class Product(val name: String, val prices: List<Int>)

and this json string that lacks the prices field:而这个缺少prices字段的 json 字符串:

{"name": "Computer"}

how can I deserialize the json string to a Product object using Jackson?如何使用 Jackson 将 json 字符串反序列化为Product object?

What I have tried in Kotlin我在 Kotlin 中尝试过的

I tried this:我试过这个:

data class Product(val name: String, val prices: List<Int>)

// Missing "prices" field
val json = """{"name": "Computer"}"""

// "prices" field included works fine
// val json = """{"name": "Computer", "prices": [1,2,3]}"""

val mapper = ObjectMapper().registerKotlinModule()
val product = mapper.readValue<Product>(json)
println(product)

but it results in this exception:但这会导致此异常:

com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of 
[simple type, class MainKt$main$Product] value failed for JSON property prices due to 
missing (therefore NULL) value for creator parameter prices which is a non-nullable type
 at [Source: (String)"{"name": "Computer"}"; line: 1, column: 20] 
(through reference chain: MainKt$main$Product["prices"])

When using Java使用 Java 时

For Java the Product class would be:对于 Java, Product class 将是:

class Product {
    private String name;
    private List<Integer> prices;

    public Product(String name, List<Integer> prices) {
        this.name = name;
        this.prices = prices;
    }

    @Override
    public String toString() {
        return "Product{name='" + name + "\", prices=" + prices + '}';
    }
}

with this Jackson code:使用此 Jackson 代码:

String json = "{\"name\": \"Computer\"}";
// String json = "{\"name\": \"Computer\", \"prices\": [1,2,3]}";

ObjectMapper mapper = new ObjectMapper();
// ParameterNamesModule is needed for non-arg constructor when not using Jackson annotations
mapper.registerModule(new ParameterNamesModule());
Product product = mapper.readValue(json, Product.class);

// Shows "prices=null", while "prices=[]" is required
System.out.println(product);

But this sets prices to null instead of an empty list.但这将prices设置为null而不是空列表。

Solution in Kotlin Kotlin 中的解决方案

This solution is for Jackson 2.11 and higher.此解决方案适用于 Jackson 2.11 及更高版本。 It uses the jackson-module-kotlin Maven artifact.它使用jackson-module-kotlin Maven 工件。

val kotlinModule = KotlinModule.Builder()
    .configure(KotlinFeature.NullToEmptyCollection, true)
    .build()
val mapper = ObjectMapper().registerModule(kotlinModule)

val product = mapper.readValue(json, Product::class.java)
println(product)

So the solution uses KotlinFeature.NullToEmptyCollection , which has the following documentation:所以该解决方案使用KotlinFeature.NullToEmptyCollection ,它具有以下文档:

Default: false.默认值:假。 Whether to deserialize null values for collection properties as empty collections.是否将集合属性的 null 值反序列化为空 collections。

There is also a map version: KotlinFeature.NullToEmptyMap .还有一个 map 版本: KotlinFeature.NullToEmptyMap

For version 2.9 and 2.10 you can use the nullToEmptyCollection default parameter of the KotlinModule constructor.对于 2.9 和 2.10 版本,您可以使用KotlinModule构造函数的nullToEmptyCollection默认参数。

Solution in Java using annotations Java中使用注解的解决方案

Annotated Product class:注释Product class:

class Product {
    private String name;
    private List<Integer> prices;

    public Product(@JsonProperty("name") String name, 
                   @JsonProperty("prices") 
                   @JsonSetter(nulls = Nulls.AS_EMPTY) List<Integer> prices
    ) {
        this.name = name;
        this.prices = prices;
    }

    @Override
    public String toString() {
        return "Product{name='" + name + "\', prices=" + prices + '}';
    }
}

Jackson code: Jackson 代码:

String json = "{\"name\": \"Computer\"}";
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(json, Product.class);
System.out.println(product); // Product{name='Computer', prices=[]}

The key part in this solution is @JsonSetter(nulls = Nulls.AS_EMPTY) , which sets the missing or null json field to an empty list in Java.该解决方案的关键部分是@JsonSetter(nulls = Nulls.AS_EMPTY) ,它将缺失的或 null json 字段设置为 ZD52387880E1EA22817A72D3759Z23 中的空列表。

The number of verbose annotations, such as @JsonProperty("prices") can be reduced by using the jackson-module-parameter-names Maven artifact.可以通过使用jackson-module-parameter-names Maven 工件来减少详细注释的数量,例如@JsonProperty("prices") Then only @JsonSetter(nulls = Nulls.AS_EMPTY) is needed.然后只需要@JsonSetter(nulls = Nulls.AS_EMPTY)

Solution in Java without annotations Java中没有注释的解决方案

This solution requires the jackson-module-parameter-names Maven artifact.此解决方案需要jackson-module-parameter-names Maven 工件。 When using this module/artifact, don't forget to add the -parameters compiler argument.使用此模块/工件时,不要忘记添加-parameters编译器参数。

Product class Jackson without annotations: Product class Jackson 无注释:

class Product {
    private String name;
    private List<Integer> prices;

    public Product(String name, List<Integer> prices) {
        this.name = name;
        this.prices = prices;
    }

    @Override
    public String toString() {
        return "Product{name='" + name + "\", prices=" + prices + '}';
    }
}

Jackson code: Jackson 代码:

String json = "{\"name\": \"Computer\"}";

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ParameterNamesModule());
mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));

Product product = mapper.readValue(json, Product.class);
System.out.println(product);

The ParameterNamesModule model is required to allow Jackson to reflect the Product constructor parameters by name, so that @JsonProperty("prices") isn't required anymore.需要ParameterNamesModule model 以允许 Jackson 按名称反映Product构造函数参数,因此不再需要@JsonProperty("prices") And JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY) is used to convert missing or null json fields to a list.JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)用于将缺失或 null json 字段转换为列表。

Your Product class need to implement Serializable class.您的产品 class 需要实现可序列化 class。 It can make consistency data.它可以制作一致性数据。

class Product implements Serializable {
    ..............
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM