0

I am using Java 8 to perform this task. I also following dependency work with JDK8 datatypes.

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

I have a class that looks like

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

and

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

I write a test to write a valid Person object to a file and and read it back to a Person object. My test is

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

The file created as part of test has following contents

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

But when reading back, I get personFromFile which looks like following

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

as you can see, the address and phone they both are null, even though they are present in the file.

What is wrong here?

UPDATE The codebase is https://github.com/101bits/java8-optional-json. This also contains the failing test

daydreamer
  • 87,243
  • 191
  • 450
  • 722

2 Answers2

2

Try marking one of the constructors with @JsonCreator to tell Jackson which constructor to use. Note: this also requires you to mark each of the constructor's parameters with @JsonProperty

You should use the @JsonCreator annotation when you want Jackson to constructor objects with a constructor or factory method as opposed letting Jackson use setters or public (non-final) fields

Additionally, your test will not pass until you override "equals" for both Person and Address

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

Update: Pull Request with passing tests

jdgilday
  • 866
  • 7
  • 21
  • Can you explain what this will do differently, considering their edit? – Sotirios Delimanolis Aug 05 '16 at 22:52
  • An alternative to the `@JsonProperty` parameters is to use the [parameter names module](https://github.com/FasterXML/jackson-module-parameter-names). – shmosel Aug 05 '16 at 22:57
  • I didn't know about the parameter names module, thanks @shmosel – jdgilday Aug 05 '16 at 23:08
  • @SotiriosDelimanolis I added code examples and documentation links – jdgilday Aug 05 '16 at 23:08
  • Thanks for the links. However, `@JsonCreator` is not _strictly_ necessary here. As long as there is a parameterless constructor and _proper_ property descriptors, this will work. – Sotirios Delimanolis Aug 05 '16 at 23:09
  • @SotiriosDelimanolis good to know that json creator is not strictly necessary. I'm going to keep the answer as is because `@JsonCreator` is consistent with Jackson documentation and it communicates intent – jdgilday Aug 05 '16 at 23:21
0

As far as i have read optional does not get serialized and hence, while deserializing you wont get the value if you are using default java serialization. However, if you are using your serialization, it should be fine.

Refer this link for more details: Why java.util.Optional is not Serializable, how to serialize the object with such fields

Community
  • 1
  • 1
vaski thakur
  • 92
  • 1
  • 9