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

Dev*_*abc 15 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

对于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)
Run Code Online (Sandbox Code Playgroud)

这在GSON中是可能的,所以我希望杰克逊必须有一些方法而不修改Person类,没有Java 8,也没有明确的自定义反序列化器.有人知道解决方案吗?

- 更新,其他信息

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

此外,还有一个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)
Run Code Online (Sandbox Code Playgroud)

cas*_*lin 10

带有混合注释的解决方案

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

假设您的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
}
Run Code Online (Sandbox Code Playgroud)

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

public abstract class PersonMixIn {

    PersonMixIn(@JsonProperty("firstName") String firstName,
                @JsonProperty("lastName") String lastName,
                @JsonProperty("age") int age) {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

ObjectMapper mapper = new ObjectMapper();
mapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
mapper.addMixIn(Person.class, PersonMixIn.class);
Run Code Online (Sandbox Code Playgroud)

并反序列化JSON:

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


小智 0

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

这是一个片段代码

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

final ObjectMapper mapper = jacksonBuilder().build();
SimpleModule module = new SimpleModule();
module.addDeserializer(Person.class, new PersonDeserializer());
Run Code Online (Sandbox Code Playgroud)

  • 代码在``Person person = (Person) mapper.readValue(node.toString(), Person.class);``处给出错误:``无法构造`jacksonParametersTest.Person`的实例``这也是一个自定义解串器解决方案,而我正在寻找一个没有解串器的解决方案。Gson默认就可以做到这一点。Jackson 的 Kotlin 模块也可以做到这一点:https://github.com/FasterXML/jackson-module-kotlin。我还发现 Gson 跳过了参数构造函数,因此它在幕后创建了一个默认的无参数构造函数。 (2认同)