Moshi Tutorial and Examples
Moshi Tutorial and Examples.
Moshi is a modern JSON library for Android and Java.
Moshi is able to read and write Java core's data types
- Primitives (int, float, char...) and their boxed counterparts (Integer, Float, Character...).
- Arrays, Collections, Lists, Sets, and Maps
- Strings
- Enums
It's very easy to customize how values are converted to and from JSON.
A type adapter is any class that has methods annotated @ToJson
and @FromJson
.
Morevover Moshi is designed to help you out when things go wrong.
It does this by always throwing a standard java.io.IOException
if there is an error reading the JSON document, or if it is malformed. It throws a JsonDataException
if the JSON document is well-formed, but doesn't match the expected format.
Moshi also uses Okio for simple and powerful I/O. It’s a fine complement to OkHttp, which can share buffer segments for maximum efficiency.
Moshi is very similar to Gson. It uses the same streaming and binding mechanisms as Gson.
Basically it helps us easily parse JSON into Java objects:
String json = ...;
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class);
BlackjackHand blackjackHand = jsonAdapter.fromJson(json);
System.out.println(blackjackHand);
It also allows us easily serialize java objects into JSON:
BlackjackHand blackjackHand = new BlackjackHand(
new Card('6', SPADES),
Arrays.asList(new Card('4', CLUBS), new Card('A', HEARTS)));
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class);
String json = jsonAdapter.toJson(blackjackHand);
System.out.println(json);
Moshi Hello World
Let's look at a hello world in moshi.
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import java.io.IOException;
/**
* Moshi Hello World
*
*/
public class MoshiHelloWorld {
public static void main(String[] args) throws IOException {
// init class
Destination destination = new Destination();
destination.setName("Alpha Centauri");
Starcraft starcraft = new Starcraft();
starcraft.setName("Enterprise");
starcraft.setDestination(destination);
// convert to json
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Starcraft> jsonAdapter = moshi.adapter(Starcraft.class);
String jsonString = jsonAdapter.toJson(starcraft);
System.out.println("json " + jsonString); //print "json {"name":"Enterprise","destination":{"name":"Alpha Centauri"}}"
// convert from json
Starcraft newStarcraft = jsonAdapter.fromJson(jsonString);
newStarcraft.show(); // print "Enterprise , Alpha Centauri!"
}
private static class Starcraft {
private String name;
private Destination destination;
String getName() {
return name;
}
void setName(String name) {
this.name = name;
}
Destination getDestination() {
return destination;
}
void setDestination(Destination destination) {
this.destination = destination;
}
void show() {
System.out.println();
System.out.println(getName() + " , " + getDestination().getName() + "!");
}
}
private static class Destination {
private String name;
String getName() {
return name;
}
void setName(String name) {
this.name = name;
}
}
}
More Examples
Here are more examples
Kotlin Android Convert To and From JSON using Moshi
This example will show you how to convert to and from JSON using Moshi.
The example is written in Kotlin and is easy to understand.
Step 1: Install Moshi
Start by installing Moshi by adding the following dependencies in your app/build.gradle
:
implementation "com.squareup.moshi:moshi:1.12.0"
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.12.0"
Step 2: Desigin Layout
The next step is two design a layout with two buttons: one to convert to JSON, the other from JSON:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button_to_json"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TO JSON"
app:layout_constraintBottom_toTopOf="@+id/button_from_json"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<Button
android:id="@+id/button_from_json"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="FROM JSON"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_to_json" />
</androidx.constraintlayout.widget.ConstraintLayout>
Step 3: Extend the JsonAdapter.Factory
Create our MoshiEnumAdapterFactory
by extending the JsonAdapter.Factory
as follows:
package com.star_zero.moshi.enumadapter
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapters.EnumJsonAdapter
import com.squareup.moshi.rawType
import java.lang.reflect.Type
annotation class MoshiEnumFallback
/**
* Annotating @MoshiEnumFallback will automatically add a fallback when an unknown value comes in the Enum.
*/
class MoshiEnumAdapterFactory : JsonAdapter.Factory {
@Suppress("TYPE_MISMATCH_WARNING", "UNCHECKED_CAST")
override fun create(
type: Type,
annotations: MutableSet<out Annotation>,
moshi: Moshi
): JsonAdapter<*>? {
val rawType = type.rawType
if (!Enum::class.java.isAssignableFrom(rawType)) {
return null
}
var fallbackEnum: Enum<*>? = null
rawType.enumConstants.forEach {
val enumElement = it as Enum<*>
val fallback =
rawType.getField(enumElement.name).getAnnotation(MoshiEnumFallback::class.java)
if (fallback != null) {
fallbackEnum = enumElement
return@forEach
}
}
return if (fallbackEnum == null) {
EnumJsonAdapter.create(type as Class<Enum<*>>)
} else {
EnumJsonAdapter.create(type as Class<Enum<*>>).withUnknownFallback(fallbackEnum)
}
}
}
Step 4: Create Model class and MainActivity
Create them as follows:
MainActivity.kt
package com.star_zero.moshi.enumadapter
import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
@JsonClass(generateAdapter = true)
data class Sample(
@Json(name = "id")
val id: Int,
@Json(name = "name")
val name: String,
@Json(name = "type")
val type: Type,
@Json(name = "kind")
val kind: Kind,
)
@JsonClass(generateAdapter = false)
enum class Type {
@Json(name = "a")
A,
@Json(name = "b")
B,
@MoshiEnumFallback
@Json(name = "unknown")
UNKNOWN
}
@JsonClass(generateAdapter = false)
enum class Kind {
@MoshiEnumFallback
@Json(name = "foo")
Foo,
@Json(name = "bar")
Bar,
}
class MainActivity : AppCompatActivity() {
private val moshi = Moshi.Builder()
.add(MoshiEnumAdapterFactory())
.build()
private val moshiAdapter = moshi.adapter(Sample::class.java)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.button_to_json).setOnClickListener {
toJson()
}
findViewById<Button>(R.id.button_from_json).setOnClickListener {
fromJson()
}
}
private fun toJson() {
val sample = Sample(1, "sample", Type.A, Kind.Bar)
val json = moshiAdapter.toJson(sample)
Log.d(TAG, "json = $json")
}
private fun fromJson() {
val json1 = "{\"id\":1,\"name\":\"sample\",\"type\":\"a\",\"kind\":\"bar\"}"
val sample1 = moshiAdapter.fromJson(json1)
// unknown value
val json2 = "{\"id\":1,\"name\":\"sample\",\"type\":\"c\",\"kind\":\"piyo\"}"
val sample2 = moshiAdapter.fromJson(json2)
Log.d(TAG, "sample1 = $sample1")
Log.d(TAG, "sample2 = $sample2")
}
companion object {
private val TAG = MainActivity::class.java.simpleName
}
}