Qualifiers: Different Encodings for the Same Type
Sometimes you need to encode the same data type in different ways within the same JSON object. For example, an int
might represent a simple number in one field but a hex color string (e.g., "#ff0000"
) in another.
Moshi uses qualifier annotations to solve this problem. A qualifier is a custom annotation that you create, which is itself annotated with @JsonQualifier
.
Example: A Hex Color Adapter
Let's build an adapter that serializes an int
as a hex color string.
The Problem
Consider this class representing a rectangle:
data class Rectangle(
val width: Int,
val height: Int,
val color: Int
)
By default, Moshi will serialize this to:
{
"width": 1024,
"height": 768,
"color": 16711680
}
We want the color
field to be a hex string like "#ff0000"
.
Step 1: Define a Qualifier Annotation
First, create your own annotation and annotate it with @JsonQualifier
. The annotation must have RUNTIME
retention.
import com.squareup.moshi.JsonQualifier
import kotlin.annotation.Retention
@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class HexColor
Step 2: Apply the Qualifier to the Property
Next, apply your new @HexColor
annotation to the color
property.
data class Rectangle(
val width: Int,
val height: Int,
@HexColor val color: Int
)
This tells Moshi that the color
field requires a special adapter—one that is associated with the @HexColor
qualifier.
Step 3: Create a Qualified Adapter
Now, create a custom adapter whose @ToJson
and @FromJson
methods are also annotated with @HexColor
. This links the adapter to the property.
class ColorAdapter {
@ToJson
fun toJson(@HexColor rgb: Int): String {
return "#%06x".format(rgb)
}
@FromJson
@HexColor
fun fromJson(rgb: String): Int {
return rgb.substring(1).toInt(16)
}
}
- The
@ToJson
method takes anint
annotated with@HexColor
and returns aString
. - The
@FromJson
method takes aString
and returns anint
, and the method itself is annotated with@HexColor
to signify what it produces.
Step 4: Register the Adapter
Finally, add an instance of your ColorAdapter
to your Moshi.Builder
.
val moshi = Moshi.Builder()
.add(ColorAdapter())
.addLast(KotlinJsonAdapterFactory())
.build()
val adapter = moshi.adapter(Rectangle::class.java)
Now, when Moshi serializes or deserializes a Rectangle
, it will use the default Int
adapter for width
and height
, but it will use your special ColorAdapter
for the @HexColor
-annotated color
property.
Resulting JSON:
{
"width": 1024,
"height": 768,
"color": "#ff0000"
}