網頁

2019/5/22

Jackson Object和JSON字串轉換,搭配 Java 8 Module 來支援Java 8的特性

Java 8新增了OptionalLocalDate等特性,而原本的jackson-databind套件由於不支援這些特性,當對含有以上特性的物件與json字串間的轉換時,會出現我們不想要的結果或錯誤,所以jackson加入了Java 8 Module等套件來支援以上特性。

例如下面是要被轉成json字串的類別Person,其屬性中含有Java 8 的OptionalLocalDate

class Person {

    private String name;

    private Optional<String> nickname; // <-- 其實這是種bad smell,不要把Optional用再class field
    private LocalDate birthday;

    public Person(String name, LocalDate birthday) {
        this.name = name;
        this.birthday= birthday;
    }

    // 省略getter, setter
}

使用ObjectMapperperson物件轉為json字串

import java.time.LocalDate;
import java.time.Month;
import java.util.Optional;

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

public class Main {

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();

        Person person = new Person("Bruce", LocalDate.of(1988, Month.APRIL, 5));
        person.setNickname(Optional.of("Chief Architect"));
        String jsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
        System.out.println(jsonString);
        
    }

}

則印出以下結果。可以看到Optional屬性nickname並沒有印出實際的值,LocalDate的格式也不是簡單的年月日。

{
  "name" : "Bruce",
  "nickname" : {
    "present" : true
  },
  "birthday" : {
    "year" : 1988,
    "month" : "APRIL",
    "chronology" : {
      "id" : "ISO",
      "calendarType" : "iso8601"
    },
    "era" : "CE",
    "dayOfMonth" : 5,
    "dayOfWeek" : "TUESDAY",
    "dayOfYear" : 96,
    "leapYear" : true,
    "monthValue" : 4
  }
}

所以可以使用jackson的Java 8 module,在Maven的pom.xml加入以下依賴。

<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-parameter-names</artifactId>
    <version>2.9.9</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
    <version>2.9.9</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.9.9</version>
</dependency>

然後在呼叫ObjectMapperregisterModule(Module module)方法來註冊Java 8相關的Module

import java.io.IOException;
import java.time.LocalDate;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.util.Optional;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;

public class Main {

    public static void main(String[] args) throws IOException {
        
        Person person1 = new Person("Bruce", LocalDate.of(1988, Month.APRIL, 5));
        person1.setNickname(Optional.of("Chief Architect"));
        
        
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new Jdk8Module())
                    .registerModule(new JavaTimeModule().addSerializer( // 設定序列化器
                            new StdSerializer<LocalDate>(LocalDate.class) {
                                private static final long serialVersionUID = 1L;

                                    @Override
                                    public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider provider) throws IOException {
                                        String dateString = value.format(DateTimeFormatter.ISO_DATE);
                                        gen.writeString(dateString);
                                        
                                    }
            
                            }).addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_DATE))); // 設定反序列化器
        
        String jsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(person1);
        System.out.println(jsonString);
        
        System.out.println("===================================================");
        
        Person person2 = objectMapper.readValue(jsonString, Person.class);
        System.out.println(person2);
        
    }

}

印出結果如下

{
  "name" : "Bruce",
  "nickname" : "Chief Architect",
  "birthday" : "1988-04-05"
}
===================================================
name=Bruce,
nickname=Optional[Chief Architect],
birthday=1988-04-05

參考:

沒有留言:

張貼留言