当前位置: 首页 > 知识库问答 >
问题:

Jackson,使用私有字段反序列化类,并使用无注释的arg构造函数

梁修贤
2023-03-14

可以反序列化到具有私有字段和自定义参数构造函数的类,而无需使用注释,也无需使用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中是可能的,所以我认为Jackson中一定有某种方法,不需要修改Person类,不需要Java 8,也不需要显式的自定义反序列化器。有人知道解决方案吗?

  • 更新,其他信息

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

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

这是静态编程语言Jackson中可用的(漂亮而干净)解决方案(IMO也应该通过自定义模块在JavaJackson中可用):

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

共有3个答案

杨星纬
2023-03-14

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

您必须按以下方式构建您的ObjectMapper

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

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

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"
}
于意智
2023-03-14

由于没有默认构造函数,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);
        }
    }
}

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

final ObjectMapper mapper = jacksonBuilder().build();
SimpleModule module = new SimpleModule();
module.addDeserializer(Person.class, new PersonDeserializer());
娄嘉石
2023-03-14

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

假设您的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);
 类似资料:
  • 我成功地将Jackson配置为序列化一个类,而不在类内部的私有字段上使用任何getter或注释 但我无法使这个非静态类的JSON字符串反序列化为一个没有任何参数化构造函数或设置器的对象。这是不是可能的--我想应该通过思考,但我不知道具体是怎么... 这里有两个测试,需要通过,以达到我的需要:

  • 问题内容: 我如何使用该字段注入包含Map的属性文件,以用作其他构造函数arg。 从属性文件加载地图 该bean当前使用以下命令进行设置: 相当于Java: 谢谢 问题答案: 像这样: 这利用了使用任意静态工厂方法创建bean的能力,在这种情况下,使用工厂工厂方法来创建bean,然后将其注入到的适当构造函数中。

  • 问题内容: 我正在杰克逊1.6.2中使用REST Web服务/ ApacheWink。我如何注释枚举字段,以便Jackson对其进行反序列化? 内部阶层 Jackson的文档指出可以通过@JsonValue/来做到这一点,@JsonCreator但没有提供示例。 是否有人愿意像往常一样洒(java)bean? 问题答案: 如果您使用的是Jackson 1.9,则可以通过以下方式完成序列化: 因此,

  • 我想使用jackson注释反序列化2级多态子类型。基本上,我想使用json注释和jacksons将以下json转换为xml,如下所示 我想要的最终Xml是 我有以下几门课——主课 它具有抽象子类 MySubItem有具体的子类,比如MySubItemA、MySubItemB和MySubItemC 最后,我们创建一个客户端类,它包含抽象类的对象列表 我尝试为我的项目创建 Mixin 类作为 和for

  • 问题内容: 我正在尝试使用Jackson 1.9.10反序列化此类的实例: 当我尝试这个我得到以下 冲突的基于属性的创建者:已经有… {界面org.codehaus.jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator()}],遇到了…,注释:{接口org.codehaus。 jackson.annotat

  • 例如: 我知道有jackson注解可以这样做,但是我的字段已经用我的持久性注解进行了注解,所以我想避免重复,因为我已经有了我想忽略的注解的字段