Java Jackson,使用私有字段和没有注释的 arg-constructor 反序列化类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47570931/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 02:39:20  来源:igfitidea点击:

Hymanson, deserialize class with private fields and arg-constructor without annotations

javajsonHymansonHymanson2Hymanson-databind

提问by Devabc

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 Hymanson?

可以使用 Hymanson 反序列化为具有私有字段和自定义参数构造函数的类,而无需使用注释和修改类?

I know it's possible in Hymanson 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.

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

For example:

例如:

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("Hymanson: " + deserializeHymanson(json)); // error
    }

    public static Person deserializeHymanson(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);
    }
}

Wich works fine for GSON, but Hymanson throws:

Wich 对 GSON 工作正常,但 Hymanson 抛出:

Exception in thread "main" com.fasterxml.Hymanson.databind.exc.InvalidDefinitionException: Cannot construct instance of `HymansonParametersTest.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.Hymanson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)

It's possible in GSON, so I would expect that there must be some way in Hymanson without modifying the Person class, without Java 8, and without an explicit custom deserializer. Does anybody know a solution?

这在 GSON 中是可能的,所以我希望在 Hymanson 中必须有某种方式,而无需修改 Person 类,无需 Java 8,也无需显式自定义反序列化器。有人知道解决方案吗?

- Update, additional info

- 更新,附加信息

Gson seems to skip the argument constructor, so it must be creating a no-argument constructor behind the scenes using reflections.

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

Also, there exists a Kotlin Hymanson modulewhich 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 Hymanson.

此外,存在一个Kotlin Hymanson 模块,即使没有“-parameters”编译器标志,它也能够为 Kotlin 数据类执行此操作。所以奇怪的是,Java Hymanson 似乎不存在这样的解决方案。

This is the (nice and clean) solution available in Kotlin Hymanson (which IMO should also become available in Java Hymanson via a custom module):

这是 Kotlin Hymanson 中可用的(漂亮且干净的)解决方案(IMO 也应该通过自定义模块在 Java Hymanson 中可用):

val mapper = ObjectMapper()
    .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
    .registerModule(KotlinModule())     

val person: Person = mapper.readValue(json, Person::class.java)

回答by adia

Since there is no default constructor, Hymanson or gson want create instance by there own. you should tell to the API how to create such instance by providing custom deserialize.

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

here an snippet code

这里是一个片段代码

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);
        }
    }
}

Then register simple module as to handle your type

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

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

回答by Beno Arakelyan

Hymanson provides the module Hymanson-modules-java8for solve your problem.

Hymanson 提供了模块Hymanson-modules-java8来解决您的问题。

You must built your ObjectMapperas following:

您必须ObjectMapper按照以下方式构建:

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

You must add -parametersas compiler argument.

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

Example for maven:

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>

For gradle:

对于毕业:

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

回答by cassiomolin

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.

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

Assuming that your Personclass is defined as follows:

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

First define a mix-in annotation abstract class:

首先定义一个mix-in注解抽象类:

public abstract class PersonMixIn {

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

Then configure ObjectMapperto use the defined class as a mix-in for your POJO:

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

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

And deserialize the JSON:

并反序列化 JSON:

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

回答by Asad Shakeel

Only if you can change your class implementation, below solution works

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

A simple solution is just to create a default constructor

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

Person()in Personclass

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;
    }
    ...
}