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

时间:2017-11-30 10:16:54

标签: java json jackson jackson2 jackson-databind

可以使用私有字段和自定义参数构造函数反序列化,而不使用注释而不使用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来说这很好,但杰克逊抛出:

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中是可能的,所以我希望杰克逊必须有一些方法而不修改Person类,没有Java 8,也没有明确的自定义反序列化器。有人知道解决方案吗?

- 更新,附加信息

Gson似乎跳过了参数构造函数,因此必须使用反射在幕后创建一个无参数的构造函数。

此外,有一个Kotlin Jackson module能够为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)

4 个答案:

答案 0 :(得分:7)

带有混合注释的解决方案

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

假设您的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);

答案 1 :(得分:1)

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

这里是一个代码段

ok
<input class=​"recherche_field" onkeyup=​"myFunction()​" type=​"text" id=​"test" name=​"search_query" placeholder=​"Ici">​
2

然后注册简单模块以处理您的类型

public class PersonDeserializer extends StdDeserializer<Person> { 
    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);
        }
    }
}

答案 2 :(得分:1)

杰克逊提供了解决问题的模块jackson-modules-java8

您必须构建ObjectMapper,如下所示:

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

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

maven的例子:

 <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>3.5.1</version>
       <configuration>
           <!--somecode-->

           <compilerArgument>-parameters</compilerArgument>

       </configuration>
 </plugin>

对于gradle:

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

答案 3 :(得分:-1)

仅当您可以更改类的实现时,以下解决方案才有效

一个简单的解决方案是创建一个默认构造函数

Person()Person类中

public class Person {
    private final String firstName;
    private final String lastName;
    private final int age;

    public Person() {
    }

    public Person(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    ...
}