Moshi Polymorphic Adapter is a library written by Kotlin, which provides polymorphic Adapters
for Moshi
. It's based on thePolymorphicJsonAdapterFactory
of Moshi, but more flexible.
A polymorphic adapter factory creates an adapter that uses the unique value to determine which type to decode to.
The ValuePolymorphicAdapterFactory
is almost same as the PolymorphicJsonAdapterFactory
, but it supports Int
, Long
, Double
and Boolean
, not only String
.
Suppose we want to decode a JSON object to a Kotlin or a Java type, and the JSON object is like:
[
{
"type": 1,
"typeOneData": "test"
},
{
"type": 2,
"typeTwoData": 1
}
]
and the type is like:
sealed class Parent(val type: Int)
data class FirstChild(val typeOneData: String) : Parent(1)
data class SecondChild(val typeTwoData: Int) : Parent(2)
or
interface Parent {
}
class FirstChild implements Parent {
String typeOneData;
public FirstChild(String typeOneData) {
this.typeOneData = typeOneData;
}
}
class SecondChild implements Parent {
int typeTwoData;
public SecondChild(int typeTwoData) {
this.typeTwoData = typeTwoData;
}
}
Base types may be classes or interfaces. Subtypes are encoded as JSON objects. Base types don't have to include a type
label like type
, which is optional.
Configure the adapter factory:
Kotlin
val valuePolymorphicAdapterFactory = ValuePolymorphicAdapterFactory.of(Parent::class.java, "type", Int::class.java)
.withSubtype(FirstChild::class.java, 1)
.withSubtype(SecondChild::class.java, 2)
val moshi = Moshi.Builder().add(valuePolymorphicAdapterFactory).build()
Java
ValuePolymorphicAdapterFactory<Parent, Integer> valuePolymorphicAdapterFactory=ValuePolymorphicAdapterFactory.of(Parent.class,"type",int.class)
.withSubtype(FirstChild.class,1)
.withSubtype(SecondChild.class,2);
Moshi moshi=new Moshi.Builder().add(valuePolymorphicAdapterFactory).build();
This adapter factory is also similar to PolymorphicJsonAdapterFactory
, but it creates an adapter that determines which
type to decode to by the JSON field name, not value.
For example, We have a JSON object that doesn't have a type label field:
[
{
"unique name": 1,
"commonData": "data",
"data": "data"
},
{
"uniqueSecondName": 1,
"commonData": "data",
"data": 1
}
]
and the type is like:
sealed class Parent
data class FirstChild(@Json(name = "unique name") val uniqueName: Int, val commonData: Sting, val data: String) : Parent()
data class SecondChild(val uniqueSecondName: Int, val commonData: Sting, val data: Int) : Parent()
or
interface Parent {
}
class FirstChild implements Parent {
@Json(name = "unique name")
int uniqueName;
String commonData;
String data;
public FirstChild(int uniqueName, String commonData, String data) {
this.uniqueName = uniqueName;
this.commonData = commonData;
this.data = data;
}
}
class SecondChild implements Parent {
int uniqueSecondName;
String commonData;
int data;
public SecondChild(int uniqueSecondName, String commonData, int data) {
this.uniqueSecondName = uniqueSecondName;
this.commonData = commonData;
this.data = data;
}
}
Configure the adapter factory:
Kotlin
val namePolymorphicAdapterFactory = NamePolymorphicAdapterFactory.of(Parent::class.java)
.withSubtype(FirstChild::class.java, "unique name")
.withSubtype(SecondChild::class.java, "uniqueSecondName")
val moshi = Moshi.Builder().add(namePolymorphicAdapterFactory).build()
Java
NamePolymorphicAdapterFactory<Parent> namePolymorphicAdapterFactory = NamePolymorphicAdapterFactory.of(Parent.class)
.withSubtype(FirstChild.class,"unique name")
.withSubtype(SecondChild.class,"uniqueSecondName");
Moshi moshi=new Moshi.Builder().add(namePolymorphicAdapterFactory).build();
Moshi Polymorphic adapter can set the default value or the fallback adapter using the withDefaultValue
and withFallbackJsonAdapter
methods, which are derived from the PolymorphicJsonAdapterFactory
. Please refer to
the PolymorphicJsonAdapterFactory
for more details.
Depend via Maven:
<dependency>
<groupId>dev.onenowy.moshipolymorphicadapter</groupId>
<artifactId>moshi-polymorphic-adapter</artifactId>
<version>{version}</version>
</dependency>
or Gradle:
implementation("dev.onenowy.moshipolymorphicadapter:moshi-polymorphic-adapter:{version}")
Moshi Polymorphic Adapter provides convenience ways to create adapters for Kotlin sealed classes. It uses @JsonClass
of Moshi and generator
tag in the annotation to configure the adapter type. PolymorphicAdapterType
has constant
values that represent the type of the adapter.
For ValuePolymorphicAdapter
:
@JsonClass(generateAdapter = true, generator = PolymorphicAdapterType.VALUE_POLYMORPHIC_ADAPTER_INT + ":" + "type")
sealed class Parent
@ValueLabel(1.toString())
data class FirstChild(val type: Int, val typeOneData: String) : Parent()
@ValueLabel(2.toString())
class SecondChild(val typeTwoData: Int) : Parent()
thegenerator
tag value must be set as PolymorphicAdapterType.VALUE_POLYMORPHIC_ADAPTER_{value type}:{type label}
.
and for NamePolymorphicAdapter
:
@JsonClass(generateAdapter = true, generator = PolymorphicAdapterType.NAME_POLYMORPHIC_ADAPTER)
sealed class Parent
@NameLabel("unique Name")
data class FirstChild(@Json(name = "unique name") val uniqueName: Int, val commonData: Sting, val data: String) :
Parent()
@NameLabel("uniqueSecondName")
data class SecondChild(val uniqueSecondName: Int, val commonData: Sting, val data: Int) : Parent()
It supports null
as a default value using @DefaultNull
.
@JsonClass(generateAdapter = true, generator = PolymorphicAdapterType.NAME_ADAPTER)
@DefaultNull
sealed class Parent()
You can use this feature with reflection or codegen.
To use the reflection feature, you need to add the KotlinSealedPolymorphicAdapterFactory
. If you
use KotlinJsonAdapterFactory
of Moshi, the KotlinSealedPolymorphicAdapterFactory
must be added before it.
val moshi = Moshi.Builder()
.add(KotlinSealedPolymorphicAdapterFactory())
.add(KotlinJsonAdapterFactory())
.build()
The reflection feature requires the following additional dependency:
<dependency>
<groupId>dev.onenowy.moshipolymorphicadapter</groupId>
<artifactId>kotlin-sealed-reflect</artifactId>
<version>{version}</version>
</dependency>
implementation("dev.onenowy.moshipolymorphicadapter:kotlin-sealed-reflect:{version}")
The codgen feature requires kapt plugin and the following additional dependency:
<dependency>
<groupId>dev.onenowy.moshipolymorphicadapter</groupId>
<artifactId>kotlin-sealed-codegen</artifactId>
<version>{version}</version>
</dependency>
kapt("dev.onenowy.moshipolymorphicadapter:kotlin-sealed-codegen:{version}")