【问题标题】:jackson does not read back data from json when fields are Optional当字段为可选时,杰克逊不会从 json 读回数据
【发布时间】:2016-08-05 22:27:15
【问题描述】:

我正在使用Java 8 来执行此任务。我还关注 JDK8 数据类型的依赖关系。

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jdk8</artifactId>
            <version>2.6.3</version>
        </dependency>

我有一个看起来像的类

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

import java.util.Optional;

public class Person {
    private String firstName;
    private String lastName;
    private int age;
    private Optional<Address> address;
    private Optional<String> phone;

    private Person() {
    }

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

    public Person(String firstName, String lastName, int age,
                  Optional<Address> address, Optional<String> phone) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.address = address;
        this.phone = phone;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public int getAge() {
        return age;
    }

    @JsonIgnore
    public Optional<Address> getAddress() {
        return address;
    }

    @JsonIgnore
    public Optional<String> getPhone() {
        return phone;
    }

    @JsonProperty("address")
    private Address getAddressForJson(){
        return address.orElse(null);
    }

    @JsonProperty("phone")
    private String getPhoneForJson() {
        return phone.orElse(null);
    }
}

public class Address {
    private String street;
    private String city;
    private String state;
    private int zip;
    private String country;

    public Address(String street, String city, String state, int zip, String country) {
        this.street = street;
        this.city = city;
        this.state = state;
        this.zip = zip;
        this.country = country;
    }

    public String getStreet() {
        return street;
    }

    public String getCity() {
        return city;
    }

    public String getState() {
        return state;
    }

    public int getZip() {
        return zip;
    }

    public String getCountry() {
        return country;
    }
}

我编写了一个测试,将有效的Person 对象写入文件并将其读回Person 对象。我的测试是

@Test
    public void writeAndReadPersonAsJsonOnFile() throws Exception {
        Address address = new Address("1 Infinite Loop", "Cupertino", "CA", 95014, "USA");
        String phone = "1-800-My-Apple";
        Person person = new Person("john", "doe", 21, Optional.of(address), Optional.of(phone));
        ObjectMapper objectMapper = registerJdkModuleAndGetMapper();
        File file = temporaryFolder.newFile("person.json");
        objectMapper.writeValue(file, person);

        assertTrue(file.exists());
        assertTrue(file.length() > 0);

        Person personFromFile = objectMapper.readValue(file, Person.class);
        assertEquals(person, personFromFile);

    }

    private ObjectMapper registerJdkModuleAndGetMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new Jdk8Module());
        return objectMapper;
    }

作为测试的一部分创建的file 具有以下内容

{
    "firstName": "john",
    "lastName": "doe",
    "age": 21,
    "address": {
        "street": "1 Infinite Loop",
        "city": "Cupertino",
        "state": "CA",
        "zip": 95014,
        "country": "USA"
    },
    "phone": "1-800-My-Apple"
}

但是在回读时,我得到personFromFile,看起来像下面

personFromFile = {Person@1178} 
 firstName = "john"
 lastName = "doe"
 age = 21
 address = null
 phone = null

如您所见,addressphone 均为 null,即使它们存在于文件中。

这里有什么问题?

更新 代码库是https://github.com/101bits/java8-optional-json。这也包含失败的测试

【问题讨论】:

  • 您的示例对我来说失败了,因为Address 中缺少构造函数。你确定你有完整的代码吗?
  • 我刚刚添加了codebase的链接,也试过你说的,但是还是不行
  • 它可能使用了 3-arg 构造函数。尝试使用@JsonCreator 标记您的构造函数。
  • 你能发布你从 JUnit 得到的失败吗?
  • 这里真正的问题是@JsonIgnore。摆脱它,你不需要它。

标签: java json java-8 jackson optional


【解决方案1】:

尝试用@JsonCreator 标记其中一个构造函数,告诉Jackson 使用哪个构造函数。注意:这也需要你用@JsonProperty 标记每个构造函数的参数

should use the @JsonCreator annotation 当您希望 Jackson 使用构造函数或工厂方法构造对象而不是让 Jackson 使用 setter 或公共(非最终)字段时

此外,您的测试将不会通过,直到您覆盖“人”和“地址”的“等于”

public class Person {
    private String firstName;
    private String lastName;
    private int age;
    private Optional<Address> address;
    private Optional<String> phone;

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

    @JsonCreator
    public Person(
            @JsonProperty("firstName") String firstName,
            @JsonProperty("lastName") String lastName,
            @JsonProperty("age") int age,
            @JsonProperty("address") Optional<Address> address,
            @JsonProperty("phone") Optional<String> phone) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.address = address;
        this.phone = phone;
    }

更新:Pull Request 通过测试

【讨论】:

  • 考虑到他们的编辑,你能解释一下这会有什么不同吗?
  • @JsonProperty 参数的替代方法是使用parameter names module
  • 我不知道参数名称模块,谢谢@shmosel
  • @SotiriosDelimanolis 我添加了代码示例和文档链接
  • 感谢您的链接。但是,@JsonCreator 在此处严格不是必需的。只要有无参数的构造函数和 proper 属性描述符,这将起作用。
【解决方案2】:

据我所知, optional 不会被序列化,因此,在反序列化时,如果您使用默认的 java 序列化,则不会获得该值。但是,如果您使用的是序列化,应该没问题。

更多详情请参考此链接: Why java.util.Optional is not Serializable, how to serialize the object with such fields

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-16
    • 2015-03-20
    • 2022-11-04
    • 1970-01-01
    • 1970-01-01
    • 2020-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多