Menggunakan Pilihan dengan Jackson

1. Pengenalan

Dalam artikel ini, kami akan memberikan gambaran keseluruhan kelas Pilihan , dan kemudian menerangkan beberapa masalah yang mungkin kami hadapi ketika menggunakannya dengan Jackson.

Berikutan ini, kami akan memperkenalkan penyelesaian yang akan memberi Jackson untuk memperlakukan Opsional seolah-olah itu adalah objek biasa yang tidak dapat dilepaskan.

2. Tinjauan Masalah

Pertama, mari kita lihat apa yang berlaku ketika kita cuba membuat siri dan mendeserisasi Optionals dengan Jackson.

2.1. Ketergantungan Maven

Untuk menggunakan Jackson, pastikan kami menggunakan versi terbarunya:

 com.fasterxml.jackson.core jackson-core 2.11.1 

2.2. Objek Buku Kami

Kemudian, mari buat Buku kelas , yang mengandungi satu bidang biasa dan satu Pilihan :

public class Book { String title; Optional subTitle; // getters and setters omitted }

Perlu diingat bahawa Pilihan tidak boleh digunakan sebagai bidang dan kami melakukan ini untuk menggambarkan masalahnya.

2.3. Serialisasi

Sekarang, mari kita buat buku :

Book book = new Book(); book.setTitle("Oliver Twist"); book.setSubTitle(Optional.of("The Parish Boy's Progress"));

Dan akhirnya, mari cuba sirikannya menggunakan Jackson ObjectMapper :

String result = mapper.writeValueAsString(book);

Kami akan melihat bahawa output medan Pilihan , tidak mengandungi nilainya, melainkan objek JSON bersarang dengan medan yang disebut sekarang :

{"title":"Oliver Twist","subTitle":{"present":true}}

Walaupun ini kelihatan pelik, sebenarnya inilah yang seharusnya kita harapkan.

Dalam kes ini, isPresent () adalah peserta awam di kelas Pilihan . Ini bermaksud ia akan bersiri dengan nilai benar atau salah , bergantung pada sama ada kosong atau tidak. Ini adalah tingkah laku serialisasi default Jackson.

Sekiranya kita memikirkannya, apa yang kita mahukan adalah nilai medan sari kata sebenarnya .

2.4. Deserialisasi

Sekarang, mari kita membalikkan contoh sebelumnya, kali ini cuba mendeserialisasikan objek menjadi Pilihan. Kita akan melihat bahawa sekarang kita mendapat JsonMappingException:

@Test(expected = JsonMappingException.class) public void givenFieldWithValue_whenDeserializing_thenThrowException String bookJson = "{ \"title\": \"Oliver Twist\", \"subTitle\": \"foo\" }"; Book result = mapper.readValue(bookJson, Book.class); } 

Mari lihat jejak timbunan:

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.util.Optional: no String-argument constructor/factory method to deserialize from String value ('The Parish Boy's Progress')

Tingkah laku ini masuk akal lagi. Pada dasarnya, Jackson memerlukan konstruktor yang dapat mengambil nilai sari kata sebagai hujah. Ini tidak berlaku dengan bidang Pilihan kami .

3. Penyelesaian

Yang kami mahukan, adalah untuk Jackson memperlakukan Pilihan yang kosong sebagai nol, dan memperlakukan Pilihan yang ada sebagai medan yang mewakili nilainya.

Nasib baik, masalah ini telah diselesaikan untuk kita. Jackson mempunyai satu set modul yang menangani jenis data JDK 8, termasuk Pilihan .

3.1. Ketergantungan dan Pendaftaran Maven

Pertama, mari tambahkan versi terbaru sebagai pergantungan Maven:

 com.fasterxml.jackson.datatype jackson-datatype-jdk8 2.9.6 

Sekarang, yang perlu kita lakukan ialah mendaftarkan modul dengan ObjectMapper kami :

ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new Jdk8Module());

3.2. Serialisasi

Sekarang, mari kita mengujinya. Sekiranya kita mencuba dan membuat siri objek Buku kita lagi, kita akan melihat bahawa sekarang ada sari kata, berbanding JSON bersarang:

Book book = new Book(); book.setTitle("Oliver Twist"); book.setSubTitle(Optional.of("The Parish Boy's Progress")); String serializedBook = mapper.writeValueAsString(book); assertThat(from(serializedBook).getString("subTitle")) .isEqualTo("The Parish Boy's Progress");

Sekiranya kita mencuba membuat siri buku kosong, buku itu akan disimpan sebagai batal :

book.setSubTitle(Optional.empty()); String serializedBook = mapper.writeValueAsString(book); assertThat(from(serializedBook).getString("subTitle")).isNull();

3.3. Deserialisasi

Sekarang, mari ulangi ujian kami untuk deserialisasi. Sekiranya kami membaca semula Buku kami, kami akan melihat bahawa kami tidak lagi mendapat JsonMappingException:

Book newBook = mapper.readValue(result, Book.class); assertThat(newBook.getSubTitle()).isEqualTo(Optional.of("The Parish Boy's Progress"));

Akhirnya, mari kita ulangi lagi ujian, kali ini dengan sifar. Kami akan melihat bahawa sekali lagi kami tidak mendapat JsonMappingException, dan sebenarnya, mempunyai Pilihan kosong :

assertThat(newBook.getSubTitle()).isEqualTo(Optional.empty());

4. Kesimpulan

Kami telah menunjukkan cara mengatasi masalah ini dengan memanfaatkan modul JDK 8 DataTypes, menunjukkan bagaimana ia membolehkan Jackson memperlakukan Pilihan yang kosong sebagai kosong, dan Pilihan yang ada sebagai bidang biasa.

Pelaksanaan contoh-contoh ini boleh didapati di GitHub; ini adalah projek berasaskan Maven, jadi seharusnya mudah dijalankan seperti sedia kala.