java Jackson反序列化,如果你完全依赖默认值,它到底是如何工作的?

tuwxkamq  于 2023-06-04  发布在  Java
关注(0)|答案(1)|浏览(297)

***一个快速说明:**是的,我读了this。不幸的是,这并没有回答我所有的问题 *
**默认情况下Jackson反序列化是如何工作的¹?**到目前为止我发现的是Jackson寻找一个无参数,然后用setter设置字段(或者,如果没有setter,则使用反射)。我有几个问题。

  1. no-args必须是public吗?
// consider this
public class Person {
    private int id;
    private String name;
    private Person() {}
}
  1. no-args必须是唯一可用的构造函数吗?例如,如果有一个显式的no-args和一个all-args呢?然后呢?
// consider this
public class Person {
    private int id;
    private String name;
    public Person() {}
    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

1.Jackson能想出如果没有参数该怎么办吗?要不要来个全参数的?

// consider this
public class Person {
    private int id;
    private String name;
    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

1.如果某些属性不是由构造函数设置的(假设它将采用“some-args”构造函数),Jackson会组装一个示例吗?Jackson会调用可用的setter,还是会求助于反射?

// consider this
public class Person {
    private int id;
    private String name;
    public Person(String name) {
        this.name = name;
    }
    public void setId(int id) {
        this.id = id;
    }
}

¹也就是说,不需要开发人员付出额外的努力:注解、自定义反序列化器等。

cyvaqqii

cyvaqqii1#

***注意。**您可以在jdoodle上运行所有片段。确保在maven-lib文件夹中包含Jackson依赖项。按...Manage libs,然后粘贴com.fasterxml.jackson.core:jackson-databind:2.15.2,最后按Add library。注意,Person类包含一个包声明。这很重要,别忘了加上 *

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import lib.Person;

public class MyClass {
   public static void main(String args[]) throws JsonProcessingException {
      ObjectMapper mapper = new ObjectMapper();
      String JSON = "{\"id\": 1, \"name\": \"Sergey\"}";
      Person me = mapper.readValue(JSON, Person.class);
       System.out.println(me);
    }
}

1.我需要包括setter吗?
是的,你需要(假设我们讨论的是你问题中定义的默认场景)。注意!如果不包含setter,那么将得到的异常信息量不会很大

package lib;

public class Person {
    private int id;
    private String name;
    
    public Person() {
        System.out.println("Person's public no-args is called...");
    }

    @Override
    public String toString() {
       return String.format("Person[id=%d, name=%s]", id, name);
    }
}
Person's public no-args is called...

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "id" (class lib.Person), not marked as ignorable (0 known properties: ])
 at [Source: (String)"{"id": 1, "name": "Sergey"}"; line: 1, column: 9] (through reference chain: lib.Person["id"])
...
  1. no-args必须是公开的吗?
    不,不是的。你也可以设置setter为private
package lib;

public class Person {
    private int id;
    private String name;
    
    private Person() {
        System.out.println("Person's private no-args is called...");
    }
    
    public void setId(int id) {
        System.out.println("setId() is called...");
        this.id = id;
    }
    
    public void setName(String name) {
        System.out.println("setName() is called...");
        this.name = name;
    }

    @Override
    public String toString() {
       return String.format("Person[id=%d, name=%s]", id, name);
    }
}
Person's private no-args is called...
setId() is called...
setName() is called...
Person[id=1, name=Sergey]
  1. no-args必须是唯一可用的构造函数吗?例如,如果有一个显式的no-args和一个all-args呢?然后呢?
    Jackson才不会在乎你的另一个建造师您甚至可以使您的无参数private,从而促使Jackson将您的public称为规范-它不会在意
package lib;

public class Person {
    private int id;
    private String name;
    
    private Person() {
        System.out.println("Person's private no-args is called...");
    }
    
    public Person(int id, String name) {
        System.out.println("Person's public canonical is called...");
        this.id = id;
        this.name = name;
    }
    
    public void setId(int id) {
        System.out.println("setId() is called...");
        this.id = id;
    }
    
    public void setName(String name) {
        System.out.println("setName() is called...");
        this.name = name;
    }

    @Override
    public String toString() {
       return String.format("Person[id=%d, name=%s]", id, name);
    }
}
Person's private no-args is called...
setId() is called...
setName() is called...
Person[id=1, name=Sergey]

1.Jackson能想出如果没有参数该怎么办吗?要不要来个全参数的?
不会的

package lib;

public class Person {
    private int id;
    private String name;
    
    public Person(int id, String name) {
        System.out.println("Person's public canonical is called...");
        this.id = id;
        this.name = name;
    }
    
    public void setId(int id) {
        System.out.println("setId() is called...");
        this.id = id;
    }
    
    public void setName(String name) {
        System.out.println("setName() is called...");
        this.name = name;
    }

    @Override
    public String toString() {
       return String.format("Person[id=%d, name=%s]", id, name);
    }
}
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `lib.Person` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{"id": 1, "name": "Sergey"}"; line: 1, column: 2]

由于这个问题是关于Jackson的默认行为的,所以我稍微偏离了一点,但是如果您将规范构造函数注解为@JsonCreator,并手动将构造函数参数Map为@JsonProperty,Jackson最终会屈尊使用它

Person's public canonical is called...
Person[id=1, name=Sergey]

让我再次强调这一点:如果省略@JsonProperty注解,假设Jackson会自动Map属性(毕竟名称和类型匹配),那么就会出现问题。同样,错误消息对您没有任何帮助

package lib;

import com.fasterxml.jackson.annotation.JsonCreator;

public class Person {
    private int id;
    private String name;
    
    @JsonCreator
    public Person(int id, String name) {
        System.out.println("Person's public canonical is called...");
        this.id = id;
        this.name = name;
    }
    
    public void setId(int id) {
        System.out.println("setId() is called...");
        this.id = id;
    }
    
    public void setName(String name) {
        System.out.println("setName() is called...");
        this.name = name;
    }

    @Override
    public String toString() {
       return String.format("Person[id=%d, name=%s]", id, name);
    }
}
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type `lib.Person`: Argument #0 of constructor [constructor for `lib.Person` (2 args), annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)} has no property name (and is not Injectable): can not use as property-based Creator
 at [Source: (String)"{"id": 1, "name": "Sergey"}"; line: 1, column: 1]

1.如果某些属性不是由构造函数设置的(假设它将采用“some-args”构造函数),Jackson会组装一个示例吗?Jackson会调用可用的setter,还是会求助于反射?
如上所述,在默认情况下,Jackson只会对无args感到满意。但是,如果您使用上一项中提到的注解,Jackson将执行以下操作:调用一个“some-args”构造函数,然后调用setter来初始化剩余的字段

package lib;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Person {
    private int id;
    private String name;
    
    @JsonCreator
    public Person(@JsonProperty("name") String name) {
        System.out.println("Person's public \"some-args\" is called...");
        this.name = name;
    }
    
    public void setId(int id) {
        System.out.println("setId() is called...");
        this.id = id;
    }
    
    public void setName(String name) {
        System.out.println("setName() is called...");
        this.name = name;
    }

    @Override
    public String toString() {
       return String.format("Person[id=%d, name=%s]", id, name);
    }
}
Person's public "some-args" is called...
setId() is called...
Person[id=1, name=Sergey]

相关问题