Blog Infos
Author
Published
Topics
,
Published
Topics
,
How to deserialize raw JSON objects with Moshi & Retrofit

You have probably come across different JSON structures in a single response. The ways in which you parse it in the application can be different. Imagine we have the following JSON:

[
{
"type": "TRUCK",
"waterCannon": true
},
{
"type": "PLANE",
"wingsSpanInMeters": 20
}
]

We can deserialize this JSON in different ways. A bad way to deserialize would be to put each property into a single object.

@JsonClass(generateAdapter = true)
data class Vehicle(
@Json(name = "type") val type: VehicleType,
@Json(name = "waterCannon") val waterCannon: Boolean?,
@Json(name = "wingsSpanInMetres") val wingsSpanInMetres: Int?
)

This is where we confuse ourselves and others with whom we share the code.

Why is this way bad?

The answer is simple. With this we set that the truck has a property that is related to the wingspan, which isn’t true.

This is where polymorphism comes in handy.

What is a polymorphism?

Polymorphism is the ability of an object to take on many forms.

In our example:

enum class VehicleType {
TRUCK,
PLANE;
}
// 1
sealed interface Vehicle {
val type: VehicleType
}
// 2
@JsonClass(generateAdapter = true)
data class Truck (
@Json(name = "waterCannon") val waterCannon: Boolean
): Vehicle {
override val type = VehicleType.TRUCK
}
// 3
@JsonClass(generateAdapter = true)
data class Plane (
@Json(name = "wingsSpanInMeters") val wingsSpanInMeters: Int
): Vehicle {
override val type = VehicleType.PLANE
}

In the first step we defined the Vehicle sealed interface with abstract parameter type.

What is Sealed interface?

Sealed interfaces represent restricted class hierarchies that provide more control over inheritance.

You can read more about them here.

In the second and third steps, we added the appropriate classes for the different types, as well as Moshi’s codegen annotations.

Now, we need to create a factory using PolymorphicJsonAdapterFactory.

What is PolymorphicJsonAdapterFactory?

A JsonAdapter factory for objects that include type information in the JSON. When decoding JSON Moshi uses this type information to determine which class to decode to. When encoding Moshi uses the object’s class to determine what type information to include.

val vehicleFactory = PolymorphicJsonAdapterFactory.of(Vehicle::class.java, "type")
.withSubtype(Truck::class.java, VehicleType.TRUCK.name)
.withSubtype(Plane::class.java, VehicleType.PLANE.name)

Job Offers

Job Offers


    API Engineer

    American Express
    London
    • Full Time
    apply now

    Android Developer

    Small and Modern GmbH
    Hamburg, Remote (Germany)
    • Full Time
    apply now

    Senior Compiler Engineer C++/LLVM – Munich

    Guardsquare
    Munich
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

,

Writing backwards-compatible Gradle plugins

Gradle plugins are powerful – you can integrate custom logic into the build process to make it fit your needs. In the Android world, most Gradle plugins will have to interact with the Android Gradle…
Watch Video

Writing backwards-compatible Gradle plugins

SIMON SCHILLER
Software Engineer
Spotify

Writing backwards-compatible Gradle plugins

SIMON SCHILLER
Software Engineer
Spotify

Writing backwards-compatible Gradle plugins

SIMON SCHILLER
Software Engineer
Spotify

Jobs

Here we need to indicate which JSON property should be used to identify the specific class to be used. In our example it is type.

Finally, we need to add the factory to the Moshi builder.

val moshi = Moshi.Builder()
.add(vehicleFactory)
.build()
What if we get a non-enum type in the response?

There we have a problem, this factory will not work well then. One solution is to add UNKNOWN as a new type inside VehicleType and define a default value within the factory.

enum class VehicleType {
TRUCK,
PLANE,
UNKNOWN; //here
}
...
object Unknown: Vehicle {
override val type: VehicleType = VehicleType.UNKNOWN
}
val vehicleFactory = PolymorphicJsonAdapterFactory.of(Vehicle::class.java, "type")
.withSubtype(Truck::class.java, VehicleType.TRUCK.name)
.withSubtype(Plane::class.java, VehicleType.PLANE.name)
.withDefaultValue(Unknown) // here

Photo by Antonio Janeski on Unsplash

 

Thats it. I hope it was helpful. 🙂

This article was originally published on proandroiddev.com on July 09, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
Nowadays authentication has become common in almost all apps. And many of us know…
READ MORE
blog
Collections are a set of interfaces and classes that implement highly optimised data structures.…
READ MORE

Leave a Reply

Your email address will not be published.

Fill out this field
Fill out this field
Please enter a valid email address.

Menu