jackson,用私有字段和arg构造函数反序列化没有注释的类

使用Jackson可以反序列化为一个具有私有字段和自定义参数构造函数的类,而不使用注释,也不需要修改类。

我知道在使用这个组合的时候Jackson是可能的:1)Java 8,2)用“-parameters”选项编译; 3)参数名称与JSON匹配。 但是GSON默认也是可以的,没有这些限制。

例如:

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抛出:

 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中是可能的,所以我希望在没有Java 8的情况下修改Person类,并且没有显式的自定义反序列化器的情况下,Jackson中必须有某种方法。 有人知道解决方案吗?

– 更新,其他信息

Gson似乎跳过了参数构造函数,所以它必须使用reflection在幕后创建一个无参数构造函数。

此外,还有一个Kotlin Jackson模块可以为Kotlin数据类做到这一点,即使没有“-parameters”编译器标志。 所以奇怪的是,Java Jackson似乎并不存在这样的解决方案。

这是Kotlin Jackson提供的(清洁)解决方案(IMO也应该通过自定义模块在Java Jackson中提供):

 val mapper = ObjectMapper() .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES) .registerModule(KotlinModule()) val person: Person = mapper.readValue(json, Person::class.java) 

由于没有默认的构造函数,所以jackson或gson需要自己创建实例。 你应该告诉API如何通过提供自定义的反序列化来创建这样的实例。

这里是一个代码片段

 public class PersonDeserializer extends StdDeserializer { public PersonDeserializer() { super(Person.class); } @Override public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { try { final JsonNode node = jp.getCodec().readTree(jp); final ObjectMapper mapper = new ObjectMapper(); final Person person = (Person) mapper.readValue(node.toString(), Person.class); return person; } catch (final Exception e) { throw new IOException(e); } } } 

然后注册简单的模块来处理您的types

 final ObjectMapper mapper = jacksonBuilder().build(); SimpleModule module = new SimpleModule(); module.addDeserializer(Person.class, new PersonDeserializer()); 

Jackson提供模块jackson-modules-java8来解决你的问题。

你必须建立你的ObjectMapper如下:

 ObjectMapper mapper = new ObjectMapper() .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES) .registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES)); 

您必须添加-parameters作为编译器参数。

maven示例:

   org.apache.maven.plugins maven-compiler-plugin 3.5.1   -parameters   

对于gradle:

 compileJava { options.compilerArgs << "-parameters" } 

混合注释的解决方案

您可以使用混合注释 。 当修改类不是一个选项时,这是一个很好的选择。 您可以将其视为在运行时添加更多注释的一种面向方面的方式,以增强静态定义的方式。

假设你的Person类定义如下:

 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 } 

首先定义一个混合注解抽象类:

 public abstract class PersonMixIn { PersonMixIn(@JsonProperty("firstName") String firstName, @JsonProperty("lastName") String lastName, @JsonProperty("age") int age) { } } 

然后将ObjectMapper配置为使用定义的类作为POJO的混合:

 ObjectMapper mapper = new ObjectMapper(); mapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES); mapper.addMixIn(Person.class, PersonMixIn.class); 

并反序列化JSON:

 String json = "{firstName: \"Foo\", lastName: \"Bar\", age: 30}"; Person person = mapper.readValue(json, Person.class);