Moshi — modern JSON serialization library for Java and Kotlin

Chetan Gaikwad
5 min readNov 17, 2020

--

Moshi is a modern JSON serialization library for Java and Kotlin. It makes the process of Parsing and Converting a JSON object into Java and Kotlin objects much easier. Moshi is built by the same people who have built the Android networking library Retrofit, Image loader library Picasso and Memory leak detection library LeakCanary and the list of useful libraries and utility is never-ending. You guessed it right, the Square developer.

The first question which will pop up in the reader's mind will be “We already have GSON library for the same purpose, then why Moshi?”. The answer to this question is “GSON work absolutely fine but it still has some pitfall which is handled by Moshi library and top of that also added few performance optimizations over GSON”

Why Moshi over GSON

  1. Reflection and non-reflection based processing:

GSON uses reflection and Moshi uses an annotation processor which makes JSON serialization and deserialization operation much faster than GSON.

2. Handling missing data:
Case 1: Missing Int data
Suppose we have product JSON data which have 3 fields id, name, and price. A normal JSON object looks similar to

//Normal Product JSON
{
“id”: 1,
“name”: “Puma T-shirt”,
“price”: 38.00
}

Suppose due to some issue the price field is missing, now let’s see how GSON handles this issue

//JSON with missing price field
{
“id”: 1,
“name”: “Puma T-shirt”
}

We have to create a Product data class with fields id, name, and price.

data class Product(
val id: Int,
val name: String,
val price: Double
)

Parsing JSON to the Product class using GSON

val product = Gson().fromJson<Product>(productJson, Product::class.java)Log.d(“Logs”, product.toString()) //Output will be 
Logs: Product(id=1, name=John, price=0.0)

Did you notice even if the price field was missing from the JSON the GSON conversion assigned a 0.0 value to the price variable in the converted object? It will not throw any error for missing type. This converted product object now can be passed to any method or class and treat the value of this product as 0.0.

Now let’s see how Moshi will handle this case.

//Parsing JSON to the Product class using Moshival moshi = Moshi.Builder().build() 
val jsonAdapter = moshi.adapter<Product>(Product::class.java!!)
val product = jsonAdapter.fromJson(productJson)
Log.d(“Logs”, product.toString())

When you run the app, the app will crash and the output will be

Caused by: com.squareup.moshi.JsonDataException: Required property ‘price’ missing at $

Did you notice, Moshi didn’t assign any false value and raise an exception clearly stating the cause of the issue.

Case 2: Missing String data
Suppose we have product JSON data which have 3 fields id, name, and details. A normal JSON object looks similar to

//Normal JSON{
“id”: 1,
“name”: “T-Shirt”,
“details”: “This is a full sized T-Shirt”
}

Suppose due to some issue the details field is missing, now let’s see how GSON handles this issue

//JSON with missing details field
{
“id”: 1,
“name”: “Puma T-shirt”
}

Product class will look similar to

data class Product(
val id: Int,
val name: String,
val details: String
)

When we parse the missing data JSON using GSON it will generate the following output

Logs: Product(id=1, name=Puma T — shirt, details=null) 

Did you notice the GSON conversion has assigned a null value to the not-null field detail? But if we use Moshi for the same conversion it will give a clear exception as follow

Caused by: com.squareup.moshi.JsonDataException: Required property ‘details’ missing at $

3. Handling malformed JSON
Suppose we have product JSON data which have 3 fields id, name, and details. A normal JSON object looks similar to

//Normal JSON
{
“id”: 1,
“name”: “T-Shirt”,
“details”: “This is a full sized T-Shirt”
}

Suppose due to some issue the id field is an integer but the server returns a string value as following

//Malformed JSON data
{
“id”: “1”,
“name”: “Puma T-shirt”,
“details”: “This is a full sized T-Shirt”
}

Product class will look similar to

data class Product(
val id: Int,
val name: String,
val details: String
)

When we parse the missing data JSON using GSON it will raise the following exception

Caused by com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: ““2””

When we parse the missing data JSON using Moshi it will raise the following exception

JsonEncodingException: Use JsonReader.setLenient(true) to accept malformed JSON at path $.id

From the above exception, the exception raised by the Moshi library is more clear since it properly informs you about the variable which is malformed. Also if you have a JSON array with multiple objects Moshi exception will provide you the path and the index of the Object which is malformed.

4. Rich built-in support
Moshi has rich built-in support for Primitives types, Arrays, Collections, Lists, Sets, and Maps, Strings, Enums.

Till now we have seen why to use Moshi. Now let's see How to use Moshi.
How to use Moshi

1. Dependencies

//Add at the start of the app level build.gradle
apply plugin: ‘kotlin-kapt’

Add the following dependencies

implementation ‘com.squareup.moshi:moshi:x.x.x’ 
kapt ‘com.squareup.moshi:moshi-kotlin-codegen:x.x.x’
//Replace x with the latest version.

2. Building a Moshi object, Moshi provides a handy builder class to create a Moshi object

val moshi = Moshi.Builder().build()

3. Moshi works on the concept of Adapter, to convert a JSON to an Object or to convert an Object to a JSON we have to build an Adapter as follows. Suppose we are converting the product JSON mentioned earlier in the Handling missing data section

val jsonAdapter = moshi.adapter<Product>(Product::class.java)

4. We have to add the following annotation to the Product class to make the Adapter at compile time

@JsonClass(generateAdapter = true)

5. If the JSON key name is different from the variable name of the data class, use the following annotation to map the JSON key to the data class variable

JSON for a Product

{
“id”: 1,
“name”: “T-Shirt”,
“details”: “This is a full sized T-Shirt”
}

Data class representing the Product JSON

data class Product(
val id: Int,
@Json(name = “name”) val productName: String,
@Json(name = “details”) val productDetails: String
)

The name field will be mapped to productName and details will be mapped to productDetails

6. Finally, call the fromJson method of the adapter to get the converted class object

val product = jsonAdapter.fromJson(productJson)

Final classes will look like this

Let’s get connected

For any query, suggestion, or improvement on my blog ping me

--

--