Jackson,使用私有字段和 arg-constructor 反序列化类,没有注释 [英] Jackson, deserialize class with private fields and arg-constructor without annotations
问题描述
是否可以使用 Jackson 反序列化为具有私有字段和自定义参数构造函数的类,而无需使用注释和修改类?
It is possible to deserialize to a class with private fields and a custom argument constructor without using annotations and without modifying the class, using Jackson?
我知道在 Jackson 中使用这种组合是可能的:1) Java 8, 2) 使用-parameters"编译;选项,以及 3) 参数名称匹配 JSON.但默认情况下,在 GSON 中也可以没有所有这些限制.
I know it's possible in Jackson when using this combination: 1) Java 8, 2) compile with "-parameters" option, and 3) the parameters names match JSON. But it's also possible in GSON by default without all these restrictions.
例如:
public class Person {
private final String firstName;
private final String lastName;
private final int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public static void main(String[] args) throws IOException {
String json = "{firstName: "Foo", lastName: "Bar", age: 30}";
System.out.println("GSON: " + deserializeGson(json)); // works fine
System.out.println("Jackson: " + deserializeJackson(json)); // error
}
public static Person deserializeJackson(String json) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
return mapper.readValue(json, Person.class);
}
public static Person deserializeGson(String json) {
Gson gson = new GsonBuilder().create();
return gson.fromJson(json, Person.class);
}
}
这对 GSON 来说效果很好,但 Jackson 抛出了:
Which works fine for GSON, but Jackson throws:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `jacksonParametersTest.Person` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{firstName: "Foo", lastName: "Bar", age: 30}"; line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
这在 GSON 中是可能的,所以我希望在 Jackson 中必须有某种方式而无需修改 Person 类,无需 Java 8,也无需显式自定义反序列化器.有人知道解决办法吗?
It's possible in GSON, so I would expect that there must be some way in Jackson without modifying the Person class, without Java 8, and without an explicit custom deserializer. Does anybody know a solution?
- 更新,附加信息
Gson 似乎跳过了参数构造函数,因此它必须使用反射在幕后创建一个无参数构造函数.
Gson seems to skip the argument constructor, so it must be creating a no-argument constructor behind the scenes using reflections.
此外,还有一个 Kotlin Jackson 模块 可以为Kotlin 数据类,即使没有-parameters"编译器标志.所以奇怪的是,Java Jackson 似乎不存在这样的解决方案.
Also, there exists a Kotlin Jackson module which is able to do this for Kotlin data classes, even without the "-parameters" compiler flag. So it is strange that such a solution doesn't seem to exist for Java Jackson.
这是 Kotlin Jackson 中可用的(漂亮且干净的)解决方案(IMO 也应该通过自定义模块在 Java Jackson 中可用):
This is the (nice and clean) solution available in Kotlin Jackson (which IMO should also become available in Java Jackson via a custom module):
val mapper = ObjectMapper()
.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
.registerModule(KotlinModule())
val person: Person = mapper.readValue(json, Person::class.java)
推荐答案
带有混合注解的解决方案
您可以使用混合注释.当修改类不是一种选择时,这是一个很好的选择.您可以将其视为一种在运行时添加更多注释的面向方面的方式,以增强静态定义的注释.
Solution with mix-in annotations
You could use mix-in annotations. It's a great alternative when modifying the classes is not an option. You can think of it as kind of aspect-oriented way of adding more annotations during runtime, to augment the statically defined ones.
假设您的 Person
类定义如下:
Assuming that your Person
class is defined as follows:
public class Person {
private final String firstName;
private final String lastName;
private final int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
// Getters omitted
}
首先定义一个mix-in注解抽象类:
First define a mix-in annotation abstract class:
public abstract class PersonMixIn {
PersonMixIn(@JsonProperty("firstName") String firstName,
@JsonProperty("lastName") String lastName,
@JsonProperty("age") int age) {
}
}
然后配置 ObjectMapper
以使用定义的类作为 POJO 的混合:
Then configure ObjectMapper
to use the defined class as a mix-in for your POJO:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
mapper.addMixIn(Person.class, PersonMixIn.class);
并反序列化 JSON:
And deserialize the JSON:
String json = "{firstName: "Foo", lastName: "Bar", age: 30}";
Person person = mapper.readValue(json, Person.class);
这篇关于Jackson,使用私有字段和 arg-constructor 反序列化类,没有注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!