Serialisasi pada Json.Net dengan ContractResolver khusus

  • 0
Kali ini saya catatkan pengalaman dalam serialisasi object dengan format JSON menggunakan Json.Net dari Newtonsoft. Beberapa waktu lalu saya menghadapi masalah tentang ini, dan akhirnya hasil pencarian Google-lah yang membantu memecahkannya. Beberapa referensi artikel hasil pencarian penting, tercantum di bagian bawah artikel ini. Semoga bermanfaat bagi sesama programmer pemula.


Serialisasi Object dengan Json.Net


Kita coba mulai dengan serialisasi object sederhana.
Public Class Buku

    Public Id As Integer
    Public Judul As String
    Public Keterangan As String

End Class
Sekarang untuk serialisasi-nya:
Imports Newtonsoft.Json

Dim buku As New Buku With {.Id = 1, _
                           .Judul = "Belajar ASP.Net", _
                           .Keterangan = "Buku yang menarik."}
Dim hasil As String = JsonConvert.SerializeObject(buku)
Debug.Print(hasil)
Hasil serialisasi:
{
  "Id": 1,
  "Judul": "Belajar ASP.Net",
  "Keterangan": "Buku yang menarik."
}
Hasil ini, saya yakin sudah pas dan cocok untuk semua kebutuhan. object sederhana, dan hasil serialisasi sederhana. Selesai.

... ... ...

Sekarang mari kita kembangkan object kita lebih lanjut dengan menambah property Pengarang pada Buku. Berikut defenisi Buku dan Pengarang yang baru:
Public Class Pengarang

    Public Id As Integer
    Public Nama As String
    Public Alamat As String

End Class

Public Class Buku

    Public Id As Integer
    Public Judul As String
    Public Keterangan As String
    Public Pengarang As Pengarang

End Class
Kita serialisasi dengan:
Imports Newtonsoft.Json

'membuat pengarang baru...
Dim pengarang As New Pengarang With {.Id = 1, _
                                     .Nama = "Indra Hulu", _
                                     .Alamat = "Indonesia"}
'membuat buku baru...
Dim buku As New Buku With {.Id = 1, _
                           .Judul = "Belajar ASP.Net", _
                           .Keterangan = "Buku yang menarik.", _
                           .Pengarang = pengarang}
'proses serialisasi...
Dim hasil As String = JsonConvert.SerializeObject(buku)
Debug.Print(hasil)
Hasil serialisasi menjadi:
{
  "Id": 1,
  "Judul": "Belajar ASP.Net",
  "Keterangan": "Buku yang menarik.",
  "Pengarang": {
    "Id": 1,
    "Nama": "Indra Hulu",
    "Alamat": "Indonesia"
  }
}
Sampai disini, hasil serialisasi juga sangat cocok dan pas, namun kembali kepada kebutuhan masing-masing. Pertanyaannya: apakah Anda membutuhkan (1) serialisasi Buku saja atau (2) serialisasi Buku dan Pengarang-nya?

... ... ...

Mari coba membuat yang lebih kompleks lagi. Setiap Buku memiliki satu Pengarang. Lalu setiap Pengarang dapat menulis satu atau dua atau lebih Buku. Sekarang kita menambahkan property DaftarBuku yang bertipe Colletion pada Pengarang, sebagai berikut:
Public Class Pengarang

    Public Id As Integer
    Public Nama As String
    Public Alamat As String
    Public DaftarBuku As List(Of Buku)

End Class

Public Class Buku

    Public Id As Integer
    Public Judul As String
    Public Keterangan As String
    Public Pengarang As Pengarang

End Class
Kemudian buat Pengarang baru. Pengarang ini telah menulis dua Buku, dan kedua Buku ini kita masukkan dalam property DaftarBuku dari si Pengarang.
Imports Newtonsoft.Json

'membuat pengarang baru..
Dim pengarang As New Pengarang With {.Id = 1, _
                                     .Nama = "Indra Hulu", _
                                     .Alamat = "Indonesia"}

'membuat dua buku baru dengan Pengarang yang sudah dibuat sebelumnya...
Dim buku1 As New Buku With {.Id = 1, _
                           .Judul = "Belajar ASP.Net Jilid 1", _
                           .Keterangan = "Buku yang menarik.", _
                           .Pengarang = pengarang}
Dim buku2 As New Buku With {.Id = 2, _
                           .Judul = "Belajar ASP.Net Jilid 2", _
                           .Keterangan = "Buku yang hebat.", _
                           .Pengarang = pengarang}

'masukkan kedua buku ke DaftarBuku si Pengarang
pengarang.DaftarBuku = New List(Of Buku)
pengarang.DaftarBuku.Add(buku1)
pengarang.DaftarBuku.Add(buku2)

'anggap kali ini kita melakukan serialisasi pada object buku1
Dim hasil As String = JsonConvert.SerializeObject(buku1)
Debug.Print(hasil)
Pada tahap ini, aplikasi akan error. Hal ini dikarenakan object buku1 menyimpan referensi ke pengarang, dan kemudian pengarang juga menyimpan referensi ke buku1 (melalui property DaftarBuku). Ini yang disebut 'self referencing loop'.

... ... ...

Umumnya, ketika menggunakan ADO.Net Entity untuk membuat entity dari sebuah database, setiap tabel dari database didefenisikan menjadi sebuah class. Bila dalam struktur database ada beberapa tabel yang memiliki relasi dengan tabel lain, maka relasi itu diimplementasikan sebagai property tambahan pada class tabel-tabel yang berhubungan. Untuk konteks contoh diatas, kita bisa mengasumsikan bahwa tabel Buku dan Pengarang saling memiliki relasi database satu sama lain.

Hal seperti ini pernah sekali menjadi masalah dalam project aplikasi web yang sedang saya kerjakan, dimana saya ingin melakukan serialisasi sebuah entity agar dapat kemudian di-deserialisasi kembali saat dibutuhkan. Bagaimana caranya melakukan serialisasi (dalam format JSON) terhadap sebuah entity tanpa mengikutkan entity lain yang terkait dengannya? Bagaimana caranya melakukan serialisasi terhadap buku1 tanpa harus mengikutkan pengarang dan buku2?


Saatnya ContractResolver menjadi pahlawan!


Json.Net mengekspos sebuah interface penting dalam proses serialisasinya, yakni IContractResolver. Interface ini menjadi bagian dari class lain yang mengatur bagaimana proses serialisasi akan dilakukan, yakni class JsonSerializerSettings. Class ini kemudian kita gunakan melalui fungsi:
JsonConvert.SerializeObject(
                value as Object, 
                settings as JsonSerializerSettings
)
Fungsi tersebut menerima 2 parameter, yakni 'value' sebagai Object yang akan di-serialisasi dan  'settings'  sebagai JsonSerializerSettings yang menentukan bagaimana proses serialisasi akan dilakukan. Pada JsonSerializerSettings inilah kita dapat menentukan IContractResolver mana yang akan digunakan selama proses serialisasi. Sebagai bawaannya, Json.Net telah memiliki dua class turunan dari IContractResolver yang siap digunakan, yakni DefaultContractResolver dan CamelCasePropertyNamesContractResolver.

Untuk kepentingan yang akan dicapai lewat tulisan ini, dan untuk menjawab pertanyaan yang sebelumnya diatas, kita akan membuat class baru yang khusus dari DefaultContractResolver. Menggunakan class yang khusus ini, kita akan membuat sebuah JsonSerializerSettings yang akan menugaskan JsonConvert untuk hanya melakukan serialisasi pada property yang bertipe primitive atau tipe lain yang bukan referensi ke Object lainnya. Ini kita capai dengan meng-override fungsi CreateProperty dari DefaultContractResolver. Pada fungsi yang di-override ini, kita menentukan apakah sebuah property tertentu akan diproses serialisasinya atau tidak.
Public Class ContractResolverSaya
    Inherits DefaultContractResolver

    Protected Overrides Function CreateProperty(member As Reflection.MemberInfo, memberSerialization As Newtonsoft.Json.MemberSerialization) As JsonProperty
        'terlebih dahulu buat property dengan memanggil fungsi CreateProperty yang asli...
        Dim prop As JsonProperty = MyBase.CreateProperty(member, memberSerialization)

        'sebelum property kita kembalikan ke dalam proses, periksa tipe property.
        'bila tipe property BUKAN tipe primitif, maka tandai untuk tidak ikut di-serialisasi...
        If Not (prop.PropertyType.IsPrimitive Or prop.PropertyType = GetType(Decimal) Or prop.PropertyType = GetType(String)) Then
            prop.ShouldSerialize = Function(f) False
        End If

        'kembalikan property ke proses lebih lanjut.
        'properti yang ShouldSerialize-nya berisi ekspresi False tidak akan di-serialisasi...
        Return prop
    End Function

End Class
Oke, sekarang mari kita uji dengan menulis ulang baris kode #8 sebagai berikut:
'membuat pengarang baru..
Dim pengarang As New Pengarang With {.Id = 1, _
                                     .Nama = "Indra Hulu", _
                                     .Alamat = "Indonesia"}

'membuat dua buku baru dengan Pengarang yang sudah dibuat sebelumnya...
Dim buku1 As New Buku With {.Id = 1, _
                           .Judul = "Belajar ASP.Net Jilid 1", _
                           .Keterangan = "Buku yang menarik.", _
                           .Pengarang = pengarang}
Dim buku2 As New Buku With {.Id = 2, _
                           .Judul = "Belajar ASP.Net Jilid 2", _
                           .Keterangan = "Buku yang hebat.", _
                           .Pengarang = pengarang}

'masukkan kedua buku ke DaftarBuku si Pengarang
pengarang.DaftarBuku = New List(Of Buku)
pengarang.DaftarBuku.Add(buku1)
pengarang.DaftarBuku.Add(buku2)

'pertama kita membuat JsonSerializerSettings yang akan kita gunakan dalam proses serialisasi
Dim settings As New JsonSerializerSettings
'pada settings ini, kita membuat dan memasukkan class yang telah kita defenisikan sebagai IContractResolver
settings.ContractResolver = New ContractResolverSaya
'barulah kita melakukan serialisasi dengan menggunakan setting di atas
Dim hasil As String = JsonConvert.SerializeObject(buku1, settings)
Debug.Print(hasil)
dan hasilnya adalah.....
{
  "Id": 1,
  "Judul": "Belajar ASP.Net Jilid 1",
  "Keterangan": "Buku yang menarik."
}
Disini kita dapat melihat bahwa ketika melakukan serialisasi pada buku1 yang sebenarnya menyimpan referensi ke pengarang dan juga referensi ulang kembali ke buku1 dan buku2, hasil serialisasi hanya menyertakan data dari buku1 satu saja.

Target tercapai. Selesai. :)

Artikel terkait lainnya sebagai bahan bacaan tambahan:

Tidak ada komentar :

Posting Komentar