Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Say "hi" to Moshi (DevFest Romania 2021)

Say "hi" to Moshi (DevFest Romania 2021)

JSON serialization is an everyday task for Android developers, but it's still not a trivial one. Long used solutions such as Gson have serious pain points, especially when using them with Kotlin. Could Moshi be our saviour when it comes to dealing with JSON on Android? We'll look at how to use it, what problems it solves, and how nicely it plays with our favourite JVM language.

More resources and info: https://zsmb.co/talks/say-hi-to-moshi/

Márton Braun

November 13, 2021
Tweet

More Decks by Márton Braun

Other Decks in Programming

Transcript

  1. Gson 101 val gson = Gson() val burger: Burger =

    Gson().fromJson("{...}", Burger::class.java) val json: String = Gson().toJson(Burger()) @zsmb13
  2. Gson 101 val gson = Gson() val burger: Burger =

    gson.fromJson("{...}", Burger::class.java) val json: String = gson.toJson(Burger()) @zsmb13
  3. Gson val gson = Gson() val burger: Burger = gson.fromJson("{...}",

    Burger::class.java) val json: String = gson.toJson(Burger()) 101 @zsmb13
  4. Gson val gson = Gson() gson.toJson(Date()) : built-in types @zsmb13

    gson.toJson(Calendar.getInstance()) "Nov 13, 2021 : : AM" 10 56 11 {"year":2021,"month":10,"dayOfMonth":13,"hourOfDay": "minute": ,"second": } , 22 10 58
  5. Gson val gson = Gson() gson.toJson(Date()) "Nov 13, 2021 :

    : AM" gson.toJson(Calendar.getInstance()) {"year":2021,"month":10,"dayOfMonth":13,"hourOfDay": "minute": ,"second": } : built-in types @zsmb13 , 11 10 56 10 56 11
  6. Gson val gson = Gson() gson.toJson(Date()) gson.toJson(Calendar.getInstance()) : built-in types

    @zsmb13 gson.toJson(Random()) "Nov 13, 2021 : : AM" 10 56 11 {"year":2021,"month":10,"dayOfMonth":13,"hourOfDay": "minute": ,"second": } , 11 10 56
  7. Gson val gson = Gson() gson.toJson(Date()) gson.toJson(Calendar.getInstance()) : built-in types

    @zsmb13 gson.toJson(Random()) {"seed":{"value":137564286790437},"nextNextGaussian":0.0, "haveNextNextGaussian":false} "Nov 13, 2021 : : AM" 10 56 11 {"year":2021,"month":10,"dayOfMonth":13,"hourOfDay": "minute": ,"second": } , 11 10 56
  8. val gson = GsonBuilder() .setFieldNamingPolicy( FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .create() Gson: field naming

    data class Burger( val name: String, val totalCost: Int, val salesPrice: Int ) @zsmb13
  9. val gson = GsonBuilder() .setFieldNamingPolicy( FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .create() Gson: field naming

    data class Burger( val name: String, val totalCost: Int, val salesPrice: Int ) gson.toJson(Burger("Classic", 10, 13)) @zsmb13
  10. val gson = GsonBuilder() .setFieldNamingPolicy( FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .create() Gson: field naming

    data class Burger( val name: String, val totalCost: Int, val salesPrice: Int ) gson.toJson(Burger("Classic", 10, 13)) {"name":"Classic","total_cost":10,"sales_price":13} @zsmb13
  11. GsonBuilder() .setFieldNamingPolicy( FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .create() Gson: field naming gson.toJson(Burger("Classic", 10, 13))

    {"name":"Classic","total_cost":10,"sales_price":13} val gson = data class Burger( val name: String, val totalCost: Int, val salesPrice: Int ) @zsmb13
  12. @SerializedName("name") @SerializedName("total_cost") @SerializedName("sales_price") Gson() Gson: field naming gson.toJson(Burger("Classic", 10, 13))

    {"name":"Classic","total_cost":10,"sales_price":13} data class Burger( val gson = val name: String, val totalCost: Int, ) val salesPrice: Int @zsmb13
  13. data class Burger( val name: String, val price: Int )

    Gson: missing data Gson() val gson = @zsmb13
  14. data class Burger( val name: String, val price: Int )

    Gson: missing data val burger = gson.fromJson("{\"name\": \"Cheeseburger\"}", Burger::class.java) Gson() val gson = @zsmb13
  15. data class Burger( val name: String, val price: Int )

    Gson: missing data val burger = gson.fromJson("{\"name\": \"Cheeseburger\"}", Burger::class.java) Burger(name=Cheeseburger, price=0) Gson() val gson = @zsmb13
  16. data class Burger( val name: String, val description: String )

    val gson = Gson() Gson: missing data val burger = gson.fromJson("{\"name\": \"Cheeseburger\"}", Burger::class.java) @zsmb13
  17. data class Burger( val name: String, val description: String )

    val gson = Gson() Gson: missing data val burger = gson.fromJson("{\"name\": \"Cheeseburger\"}", Burger::class.java) Burger(name=Cheeseburger, description=null) @zsmb13
  18. data class Burger( val name: String, val description: String =

    "One of our best burgers!" ) val gson = Gson() Gson: missing data val burger = gson.fromJson("{\"name\": \"Cheeseburger\"}", Burger::class.java) @zsmb13
  19. data class Burger( val name: String, val description: String =

    "One of our best burgers!" ) val gson = Gson() Gson: missing data val burger = gson.fromJson("{\"name\": \"Cheeseburger\"}", Burger::class.java) Burger(name=Cheeseburger, description=null) @zsmb13
  20. val gson = Gson() Gson: malformed data data class Burger(val

    name: String, val price: Int) data class Restaurant(val menu: List<Burger>) @zsmb13
  21. val gson = Gson() Gson: malformed data data class Burger(val

    name: String, val price: Int) data class Restaurant(val menu: List<Burger>) val json = """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" @zsmb13
  22. val gson = Gson() Gson: malformed data data class Burger(val

    name: String, val price: Int) data class Restaurant(val menu: List<Burger>) val json = """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" @zsmb13
  23. val gson = Gson() Gson: malformed data data class Burger(val

    name: String, val price: Int) data class Restaurant(val menu: List<Burger>) val json = """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" val restaurant = gson.fromJson(json, Restaurant::class.java) @zsmb13
  24. Gson: malformed data val gson = Gson() data class Burger(val

    name: String, val price: Int) data class Restaurant(val menu: List<Burger>) val json = """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" val restaurant = gson.fromJson(json, Restaurant::class.java) Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "crocodile" @zsmb13
  25. val gson = Gson() Gson: malformed data data class Burger(val

    name: String, val price: Int) data class Restaurant(val menu: List<Burger>) val json = """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" val restaurant = gson.fromJson(json, Restaurant::class.java) Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "crocodile" @zsmb13
  26. val gson = Gson() Gson: malformed data data class Burger(val

    name: String, val price: Int) data class Restaurant(val menu: List<Burger>) val json = """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "" } ] }""" val restaurant = gson.fromJson(json, Restaurant::class.java) Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "" @zsmb13
  27. public JavaBurger(String name, int price) { this.name = name; this.price

    = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } Moshi 101 } public class JavaBurger { private String name; private int price; @zsmb13
  28. // ... public JavaBurger(String name, int price) { this.name =

    name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } Moshi 101 } public class JavaBurger { private String name; private int price; @zsmb13
  29. // ... public JavaBurger(String name, int price) { this.name =

    name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } Moshi 101 val moshi = Moshi.Builder().build() } public class JavaBurger { private String name; private int price; @zsmb13
  30. // ... public JavaBurger(String name, int price) { this.name =

    name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } Moshi 101 val moshi = Moshi.Builder().build() val json = moshi.toJson(JavaBurger("Classic", 10)) } public class JavaBurger { private String name; private int price; @zsmb13
  31. // ... Moshi 101 val moshi = Moshi.Builder().build() val json

    = moshi.toJson(JavaBurger("Classic", 10)) } public class JavaBurger { private String name; private int price; @zsmb13
  32. // ... Moshi 101 val moshi = Moshi.Builder().build() val json

    = moshi.toJson(JavaBurger("Classic", 10)) } public class JavaBurger { private String name; private int price; val burgerAdapter: JsonAdapter<JavaBurger> = moshi.adapter(JavaBurger::class.java) @zsmb13
  33. // ... Moshi 101 val moshi = Moshi.Builder().build() } public

    class JavaBurger { private String name; private int price; val burgerAdapter: JsonAdapter<JavaBurger> = moshi.adapter(JavaBurger::class.java) val json = burgerAdapter.toJson(JavaBurger("Classic", 10)) @zsmb13
  34. // ... Moshi 101 val moshi = Moshi.Builder().build() } public

    class JavaBurger { private String name; private int price; val burgerAdapter: JsonAdapter<JavaBurger> = moshi.adapter(JavaBurger::class.java) val json = burgerAdapter.toJson(JavaBurger("Classic", 10)) {"name":"Classic","price":10} @zsmb13
  35. // ... Moshi 101 val moshi = Moshi.Builder().build() } public

    class JavaBurger { private String name; private int price; val burgerAdapter: JsonAdapter<JavaBurger> = moshi.adapter(JavaBurger::class.java) val json = burgerAdapter.toJson(JavaBurger("Classic", 10)) {"name":"Classic","price":10} val burger = burgerAdapter.fromJson(json) @zsmb13
  36. Burger::class.java) // ... val moshi = Moshi.Builder().build() } public Java

    { private String ; private int ; Java val json = burgerAdapter.toJson(JavaBurger("Classic", 10)) {"name":"Classic","price":10} val burger = burgerAdapter.fromJson(json) Burger class val burgerAdapter: JsonAdapter< Burger> = moshi.adapter(Java name price @zsmb13 Moshi 101
  37. Burger::class.java) val moshi = Moshi.Builder().build() (val : String, val :

    Int) val burgerAdapter: JsonAdapter<Burger> = moshi.adapter( Burger class name price @zsmb13 Moshi ft. Kotlin
  38. Burger::class.java) val moshi = Moshi.Builder().build() (val : String, val :

    Int) val burgerAdapter: JsonAdapter<Burger> = moshi.adapter( Burger class name price Moshi ft. Kotlin Exception in thread "main" java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.example.Burger. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin- codegen artifact. @zsmb13
  39. Moshi ft. Kotlin val moshi = Moshi.Builder().build() val burgerAdapter: JsonAdapter<Burger>

    = moshi.adapter(Burger::class.java) class Burger(val name: String, val price: Int) Exception in thread "main" java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.example.Burger. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin- codegen artifact. @zsmb13
  40. Moshi ft. Kotlin val moshi = Moshi.Builder().build() val burgerAdapter: JsonAdapter<Burger>

    = moshi.adapter(Burger::class.java) class Burger(val name: String, val price: Int) Exception in thread "main" java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.example.Burger. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin- codegen artifact. @zsmb13
  41. val moshi = Moshi.Builder().build() val burgerAdapter: JsonAdapter<Burger> = moshi.adapter(Burger::class.java) class

    Burger(val name: String, val price: Int) Moshi ft. Kotlin Exception in thread "main" java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.example.Burger. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin- codegen artifact. @zsmb13
  42. val moshi = Moshi.Builder().build() val burgerAdapter: JsonAdapter<Burger> = moshi.adapter(Burger::class.java) class

    Burger(val name: String, val price: Int) def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" @zsmb13 Moshi: reflection
  43. val moshi = Moshi.Builder().build() Moshi: reflection val burgerAdapter: JsonAdapter<Burger> =

    moshi.adapter(Burger::class.java) class Burger(val name: String, val price: Int) def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" implementation "com.squareup.moshi:moshi-kotlin:$moshi_version" @zsmb13
  44. val moshi = Moshi.Builder() Moshi: reflection val burgerAdapter: JsonAdapter<Burger> =

    moshi.adapter(Burger::class.java) class Burger(val name: String, val price: Int) .build() @zsmb13
  45. Moshi: reflection .add(KotlinJsonAdapterFactory()) val burgerAdapter: JsonAdapter<Burger> = moshi.adapter(Burger::class.java) class Burger(val

    name: String, val price: Int) val json = burgerAdapter.toJson(Burger("Classic", 10)) .build() val moshi = Moshi.Builder() @zsmb13
  46. Moshi: reflection .add(KotlinJsonAdapterFactory()) val burgerAdapter: JsonAdapter<Burger> = moshi.adapter(Burger::class.java) class Burger(val

    name: String, val price: Int) val json = burgerAdapter.toJson(Burger("Classic", 10)) .build() val moshi = Moshi.Builder() @zsmb13 {"name":"Classic","price":10}
  47. Moshi: reflection .add(KotlinJsonAdapterFactory()) val burgerAdapter: JsonAdapter<Burger> = moshi.adapter(Burger::class.java) class Burger(val

    name: String, val price: Int) val json = burgerAdapter.toJson(Burger("Classic", 10)) .build() val moshi = Moshi.Builder() @zsmb13 {"name":"Classic","price":10} val burger = burgerAdapter.fromJson(json)
  48. Moshi: reflection def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"

    Exception in thread "main" java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.example.Burger. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin- codegen artifact. @zsmb13
  49. implementation moshi-kotlin:$moshi_version" "com.squareup.moshi: def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" Moshi:

    reflection Exception in thread "main" java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.example.Burger. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the artifact. moshi-kotlin- @zsmb13 codegen
  50. dependencies { } Moshi: codegen apply plugin: 'kotlin' apply plugin:

    'kotlin-kapt' def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" @zsmb13
  51. @JsonClass(generateAdapter = true) class Burger(val name: String, val price: Int)

    val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val json = burgerAdapter.toJson(Burger("Double", 18))) @zsmb13 Moshi: codegen
  52. dependencies { def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"

    kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" } apply plugin: 'kotlin' apply plugin: 'kotlin-kapt' val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build() @zsmb13 Moshi: mixed use
  53. Moshi: mixed use dependencies { def moshi_version = '1.12.0' implementation

    "com.squareup.moshi:moshi:$moshi_version" implementation "com.squareup.moshi:moshi-kotlin:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" } apply plugin: 'kotlin' apply plugin: 'kotlin-kapt' val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build() @zsmb13
  54. dependencies { def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"

    kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" } apply plugin: 'kotlin' apply plugin: 'kotlin-kapt' val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build() @zsmb13 Moshi: mixed use
  55. class CustomBurgerAdapter { TODO("Implement deserialization") @ToJson fun toJson(writer: JsonWriter, value:

    Burger?) { TODO("Implement serialization") } } @FromJson fun fromJson(reader: JsonReader): Burger? { } @zsmb13 Moshi: custom adapters
  56. Moshi: custom adapters @FromJson fun fromJson(reader: JsonReader): Burger? { var

    name: String? = null var price: Int? = null } @zsmb13
  57. Moshi: custom adapters @FromJson fun fromJson(reader: JsonReader): Burger? { var

    name: String? = null var price: Int? = null reader.beginObject() reader.endObject() } @zsmb13
  58. Moshi: custom adapters @FromJson fun fromJson(reader: JsonReader): Burger? { var

    name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { } reader.endObject() } @zsmb13
  59. Moshi: custom adapters @FromJson fun fromJson(reader: JsonReader): Burger? { var

    name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.nextName()) { "name" -> name = reader.nextString() "price" -> price = reader.nextInt() else -> reader.skipValue() } } reader.endObject() } @zsmb13
  60. @FromJson fun fromJson(reader: JsonReader): Burger? { var name: String? =

    null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.nextName()) { "name" -> name = reader.nextString() "price" -> price = reader.nextInt() else -> reader.skipValue() } } reader.endObject() return Burger(name!!, price!!) } @zsmb13 : custom adapters Moshi
  61. class BurgerJsonAdapter( moshi: Moshi ) : JsonAdapter<Burger>() { override fun

    toString(): String = buildString(28) { append("GeneratedJsonAdapter(").append("Burger").append(')') } private val options: JsonReader.Options = JsonReader.Options.of("name", "price") private val stringAdapter: JsonAdapter<String> = moshi.adapter(String::class.java, emptySet(), "name") private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") override fun fromJson(reader: JsonReader): Burger { var name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> name = stringAdapter.fromJson(reader) ?: throw Util.unexpectedNull("name", "name", reader) 1 -> price = intAdapter.fromJson(reader) ?: throw Util.unexpectedNull("price", "price", reader) -1 -> { reader.skipName() reader.skipValue() } } } reader.endObject() return Burger( name = name ?: throw Util.missingProperty("name", "name", reader), price = price ?: throw Util.missingProperty("price", "price", reader) ) } override fun toJson(writer: JsonWriter, value: Burger?) { if (value == null) { throw NullPointerException( "value was null! Wrap in .nullSafe() to write nullable values.") } writer.beginObject() writer.name("name") stringAdapter.toJson(writer, value.name) writer.name("price") intAdapter.toJson(writer, value.price) writer.endObject() } } @zsmb13 Moshi’s generated adapter
  62. class BurgerJsonAdapter( moshi: Moshi ) : JsonAdapter<Burger>() { override fun

    toString(): String = buildString(28) { append("GeneratedJsonAdapter(").append("Burger").append(')') } private val options: JsonReader.Options = JsonReader.Options.of("name", "price") private val stringAdapter: JsonAdapter<String> = moshi.adapter(String::class.java, emptySet(), "name") private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") @zsmb13 Moshi’s generated adapter
  63. class BurgerJsonAdapter( moshi: Moshi ) : JsonAdapter<Burger>() { override fun

    toString(): String = buildString(28) { append("GeneratedJsonAdapter(").append("Burger").append(')') } private val options: JsonReader.Options = JsonReader.Options.of("name", "price") private val stringAdapter: JsonAdapter<String> = moshi.adapter(String::class.java, emptySet(), "name") private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") Moshi’s generated adapter @zsmb13
  64. private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") override fun

    fromJson(reader: JsonReader): Burger { var name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> name = stringAdapter.fromJson(reader) ?: throw Util.unexpectedNull("name", "name", reader) 1 -> price = intAdapter.fromJson(reader) ?: throw Util.unexpectedNull("price", "price", reader) -1 -> { reader.skipName() reader.skipValue() } } } reader.endObject() Moshi’s generated adapter @zsmb13
  65. private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") override fun

    fromJson(reader: JsonReader): Burger { var name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> name = stringAdapter.fromJson(reader) ?: throw Util.unexpectedNull("name", "name", reader) 1 -> price = intAdapter.fromJson(reader) ?: throw Util.unexpectedNull("price", "price", reader) -1 -> { reader.skipName() reader.skipValue() } } } reader.endObject() Moshi’s generated adapter @zsmb13
  66. private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") override fun

    fromJson(reader: JsonReader): Burger { var name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> name = stringAdapter.fromJson(reader) ?: throw Util.unexpectedNull("name", "name", reader) 1 -> price = intAdapter.fromJson(reader) ?: throw Util.unexpectedNull("price", "price", reader) -1 -> { reader.skipName() reader.skipValue() } } } reader.endObject() Moshi’s generated adapter @zsmb13
  67. private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") override fun

    fromJson(reader: JsonReader): Burger { var name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> name = stringAdapter.fromJson(reader) ?: throw Util.unexpectedNull("name", "name", reader) 1 -> price = intAdapter.fromJson(reader) ?: throw Util.unexpectedNull("price", "price", reader) -1 -> { reader.skipName() reader.skipValue() } } } reader.endObject() Moshi’s generated adapter @zsmb13
  68. private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") override fun

    fromJson(reader: JsonReader): Burger { var name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> name = stringAdapter.fromJson(reader) ?: throw Util.unexpectedNull("name", "name", reader) 1 -> price = intAdapter.fromJson(reader) ?: throw Util.unexpectedNull("price", "price", reader) -1 -> { reader.skipName() reader.skipValue() } } } reader.endObject() Moshi’s generated adapter @zsmb13
  69. override fun toString(): String = buildString(28) { append("GeneratedJsonAdapter(").append("Burger").append(')') } private

    val options: JsonReader.Options = JsonReader.Options.of("name", "price") private val stringAdapter: JsonAdapter<String> = moshi.adapter(String::class.java, emptySet(), "name") private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") override fun fromJson(reader: JsonReader): Burger { var name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> name = stringAdapter.fromJson(reader) ?: throw Util.unexpectedNull("name", "name", reader) Moshi’s generated adapter @zsmb13
  70. private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") override fun

    fromJson(reader: JsonReader): Burger { var name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> name = stringAdapter.fromJson(reader) ?: throw Util.unexpectedNull("name", "name", reader) 1 -> price = intAdapter.fromJson(reader) ?: throw Util.unexpectedNull("price", "price", reader) -1 -> { reader.skipName() reader.skipValue() } } } reader.endObject() Moshi’s generated adapter @zsmb13
  71. private val options: JsonReader.Options = JsonReader.Options.of("name", "price") private val stringAdapter:

    JsonAdapter<String> = moshi.adapter(String::class.java, emptySet(), "name") private val intAdapter: JsonAdapter<Int> = moshi.adapter(Int::class.java, emptySet(), "price") override fun fromJson(reader: JsonReader): Burger { var name: String? = null var price: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> name = stringAdapter.fromJson(reader) ?: throw Util.unexpectedNull("name", "name", reader) 1 -> price = intAdapter.fromJson(reader) ?: throw Util.unexpectedNull("price", "price", reader) -1 -> { Moshi’s generated adapter @zsmb13
  72. throw Util.unexpectedNull("price", "price", reader) -1 -> { reader.skipName() reader.skipValue() }

    } } reader.endObject() return Burger( name = name ?: throw Util.missingProperty("name", "name", reader), price = price ?: throw Util.missingProperty("price", "price", reader) ) } } Moshi’s generated adapter @zsmb13
  73. throw Util.unexpectedNull("price", "price", reader) -1 -> { reader.skipName() reader.skipValue() }

    } } reader.endObject() return Burger( name = name ?: throw Util.missingProperty("name", "name", reader), price = price ?: throw Util.missingProperty("price", "price", reader) ) } } @zsmb13 Moshi’s generated adapter
  74. Moshi: built-in types val moshi = Moshi.Builder().build() val adapter: JsonAdapter<Date>

    = moshi.adapter(Date::class.java) Exception in thread "main" java.lang.IllegalArgumentException: Platform class java.util.Date requires explicit JsonAdapter to be registered @zsmb13
  75. Moshi: built-in types def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" kapt

    "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" val moshi = Moshi.Builder().build() val adapter: JsonAdapter<Date> = moshi.adapter(Date::class.java) @zsmb13
  76. Moshi: built-in types def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" kapt

    "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" implementation "com.squareup.moshi:moshi-adapters:$moshi_version" val moshi = Moshi.Builder().build() val adapter: JsonAdapter<Date> = moshi.adapter(Date::class.java) @zsmb13
  77. val adapter: JsonAdapter<Date> = moshi.adapter(Date::class.java) def moshi_version = '1.12.0' implementation

    "com.squareup.moshi:moshi:$moshi_version" implementation "com.squareup.moshi:moshi-adapters:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" .add(Date::class.java, Rfc3339DateJsonAdapter()) .build() val moshi = Moshi.Builder() @zsmb13 Moshi: built-in types 2021-11-13T : : Z 22 08 54
  78. val adapter: JsonAdapter<Date> = moshi.adapter(Date::class.java) def moshi_version = '1.12.0' implementation

    "com.squareup.moshi:moshi:$moshi_version" implementation "com.squareup.moshi:moshi-adapters:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" .add(Date::class.java, Rfc3339DateJsonAdapter()) 2021-11-13T : : Z .build() val moshi = Moshi.Builder() @zsmb13 Moshi: built-in types 11 10 56
  79. @JsonClass(generateAdapter = true) data class Burger( @Json(name = "name") val

    name: String, @Json(name = "total_cost") val totalCost: Int, @Json(name = "sales_price") val salesPrice: Int ) @zsmb13 Moshi: field naming
  80. @JsonClass(generateAdapter = true) data class Burger( @Json(name = "name") val

    name: String, @Json(name = "total_cost") val totalCost: Int, @Json(name = "sales_price") val salesPrice: Int ) moshi.adapter(Burger::class.java) .toJson(Burger("Classic", 10, 13)) @zsmb13 Moshi: field naming
  81. @JsonClass(generateAdapter = true) data class Burger( @Json(name = "name") val

    name: String, @Json(name = "total_cost") val totalCost: Int, @Json(name = "sales_price") val salesPrice: Int ) moshi.adapter(Burger::class.java) .toJson(Burger("Classic", 10, 13)) {"name":"Classic","total_cost":10,"sales_price":13} @zsmb13 Moshi: field naming
  82. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    total_cost: Int, val sales_price: Int ) moshi.adapter(Burger::class.java) .toJson(Burger("Classic", 10, 13)) {"name":"Classic","total_cost":10,"sales_price":13} @zsmb13 Moshi: field naming
  83. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    price: Int ) Moshi: missing data val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) @zsmb13
  84. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    price: Int ) Moshi: missing data val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) Exception in thread "main" com.squareup.moshi.JsonDataException: Required value 'price' missing at $ @zsmb13
  85. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    description: String ) Moshi: missing data val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) @zsmb13
  86. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    description: String ) Moshi: missing data val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) @zsmb13
  87. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    description: String ) Moshi: missing data val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) Exception in thread "main" com.squareup.moshi.JsonDataException: Required value 'description' missing at $ @zsmb13
  88. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    description: String? ) Moshi: missing data val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) @zsmb13
  89. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    description: String? ) Moshi: missing data val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) Burger(name=Cheeseburger, description=null) @zsmb13
  90. Burger(name=Cheeseburger, description=null) @JsonClass(generateAdapter = true) data class Burger( val name:

    String, val description: String = "One of our best burgers!" ) Moshi: missing data val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) @zsmb13
  91. Burger(name=Cheeseburger, description=null) @JsonClass(generateAdapter = true) data class Burger( val name:

    String, val description: String = "One of our best burgers!" ) Moshi: missing data val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\"}" ) Burger(name=Cheeseburger, description=One of our best burgers!) @zsmb13
  92. val moshi = Moshi.Builder() Moshi: customization options @JsonClass(generateAdapter = true)

    data class Burger(val name: String, val price: Int) .build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\", \"price\": 13}" ) @zsmb13
  93. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) .add(...) val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\", \"price\": 13}" ) .build() val moshi = Moshi.Builder() @zsmb13
  94. val moshi = Moshi.Builder() Moshi: customization options @JsonClass(generateAdapter = true)

    data class Burger(val name: String, val price: Int) .build() val burgerAdapter = moshi.adapter(Burger::class.java) val burger = burgerAdapter.fromJson( "{\"name\": \"Cheeseburger\", \"price\": 13}" ) @zsmb13
  95. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) JsonAdapter<Burger> @zsmb13
  96. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) @zsmb13
  97. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int?) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) @zsmb13
  98. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int?) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) burgerAdapter.toJson(Burger("Cancelled", null)) @zsmb13
  99. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int?) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) burgerAdapter.toJson(Burger("Cancelled", null)) {"name":"Cancelled"} @zsmb13
  100. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int?) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) .serializeNulls() burgerAdapter.toJson(Burger("Cancelled", null)) {"name":"Cancelled","price":null} @zsmb13
  101. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int?) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) .serializeNulls() burgerAdapter.toJson(Burger("Cancelled", null)) {"name":"Cancelled","price":null} @zsmb13 JsonAdapter<Burger>
  102. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int?) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) .serializeNulls() burgerAdapter.toJson(Burger("Cancelled", null)) {"name":"Cancelled","price":null} @zsmb13
  103. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) burgerAdapter.toJson(null) @zsmb13
  104. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) burgerAdapter.toJson(null) null @zsmb13
  105. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) .nullSafe() burgerAdapter.toJson(null) null @zsmb13
  106. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) .nonNull() burgerAdapter.toJson(null) @zsmb13
  107. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) .nonNull() burgerAdapter.toJson(null) Exception in thread "main" com.squareup.moshi.JsonDataException: Unexpected null at $ @zsmb13
  108. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) @zsmb13
  109. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) burgerAdapter.fromJson( "{\"name\":\"Cheeseburger\",\"price\": 13}" ) @zsmb13
  110. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) burgerAdapter.fromJson( "{\"name\":\"Cheeseburger\",\"price\": 13}" ) Burger(name=Cheeseburger, price=13) @zsmb13
  111. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) burgerAdapter.fromJson( "{\"name\":\"Cheeseburger\",\"price\": 13,\"rating\":4.2}" ) @zsmb13
  112. Moshi: customization options @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) burgerAdapter.fromJson( "{\"name\":\"Cheeseburger\",\"price\": 13,\"rating\":4.2}" ) Burger(name=Cheeseburger, price=13) @zsmb13
  113. @JsonClass(generateAdapter = true) data class Burger(val name: String, val price:

    Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) .failOnUnknown() burgerAdapter.fromJson( "{\"name\":\"Cheeseburger\",\"price\": 13,\"rating\":4.2}" ) Moshi: customization options @zsmb13
  114. @JsonClass(generateAdapter = true) data class Burger(val name: String, val price:

    Int) val moshi = Moshi.Builder().build() val burgerAdapter = moshi.adapter(Burger::class.java) .failOnUnknown() burgerAdapter.fromJson( "{\"name\":\"Cheeseburger\",\"price\": 13,\"rating\":4.2}" ) Moshi: customization options Exception in thread "main" com.squareup.moshi.JsonDataException: Cannot skip unexpected NAME at $.rating @zsmb13
  115. @JsonClass(generateAdapter = true) data class Burger(val name: String, val price:

    Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) @zsmb13 Moshi: malformed data
  116. @JsonClass(generateAdapter = true) data class Burger(val name: String, val price:

    Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) val json = """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" @zsmb13 Moshi: malformed data
  117. @JsonClass(generateAdapter = true) data class Burger(val name: String, val price:

    Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) val json = """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" val restaurant = adapter.fromJson(json, Restaurant::class.java) @zsmb13 Moshi: malformed data
  118. Moshi: malformed data @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) val json = """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" val restaurant = adapter.fromJson(json, Restaurant::class.java) Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was crocodile at path $.menu[2].price @zsmb13
  119. val json = val restaurant = adapter.fromJson(json, Restaurant::class.java) """{ "menu":

    [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" Moshi: malformed data @JsonClass(generateAdapter = true) data class Burger(val name: String, val price: Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was crocodile at path $.menu[2].price @zsmb13
  120. """{ "menu": [ { "name": "Classic", "price": 10 }, {

    "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was crocodile at path $.menu[2].price @zsmb13
  121. Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was

    crocodile at path $.menu[2].price """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" @zsmb13
  122. Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was

    crocodile at path $.menu[2].price """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" @zsmb13
  123. Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was

    crocodile at path $.menu[2].price """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" @zsmb13
  124. Moshi: malformed data @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) val json = val restaurant = adapter.fromJson(json, Restaurant::class.java) """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was crocodile at path $.menu[2].price @zsmb13
  125. Moshi: malformed data @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) val json = val restaurant = adapter.fromJson(json, Restaurant::class.java) """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was crocodile at path $.menu[2].price Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "crocodile" @zsmb13
  126. Moshi: malformed data @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) val json = val restaurant = adapter.fromJson(json, Restaurant::class.java) """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "crocodile" } ] }""" Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was crocodile at path $.menu[2].price Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "crocodile" @zsmb13
  127. Moshi: malformed data @JsonClass(generateAdapter = true) data class Burger(val name:

    String, val price: Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) val json = val restaurant = adapter.fromJson(json, Restaurant::class.java) """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "" } ] }""" Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was at path $.menu[2].price Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "" @zsmb13
  128. @JsonClass(generateAdapter = true) data class Burger(val name: String, val price:

    Int) @JsonClass(generateAdapter = true) data class Restaurant(val menu: List<Burger>) val json = val restaurant = adapter.fromJson(json, Restaurant::class.java) """{ "menu": [ { "name": "Classic", "price": 10 }, { "name": "Cheeseburger", "price": 13 }, { "name": "Double", "price": "" } ] }""" Moshi: malformed data Exception in thread "main" com.squareup.moshi.JsonDataException: Expected an int but was at path $.menu[2].price Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "" @zsmb13
  129. Retrofit.Builder() .baseUrl("...") .addConverterFactory(MoshiConverterFactory.create()) .build() def retrofit_version = '2.9.0' implementation "com.squareup.retrofit2:retrofit:$retrofit_version"

    implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .add(...) .build() @zsmb13 Moshi: Retrofit integration
  130. def retrofit_version = '2.9.0' implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" val moshi

    = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .add(...) .build() @zsmb13 Moshi: Retrofit integration )) Retrofit.Builder() .baseUrl("...") .addConverterFactory(MoshiConverterFactory.create( .build()
  131. Retrofit.Builder() .baseUrl("...") .addConverterFactory(MoshiConverterFactory.create( .build() def retrofit_version = '2.9.0' implementation "com.squareup.retrofit2:retrofit:$retrofit_version"

    implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .add(...) .build() @zsmb13 Moshi: Retrofit integration )) moshi
  132. More Moshi… •Other built-in adapters •Intermediate objects while parsing •Annotated

    types •Parsing arrays and other generic types •KSP support •A lot more under-the-hood details! @zsmb13
  133. Resources •A Few Ok Libraries (Jake Wharton, Square) – Droidcon

    Montreal 2015 • https://www.youtube.com/watch?v=WvyScM_S88c •Upgrading to Moshi (Eric Cochran, Pinterest) - droidcon NYC 2017 • https://www.youtube.com/watch?v=xHFs7cccnXU •JSON Explained (Jesse Wilson, Square) – Chicago Roboto 2019 • https://www.youtube.com/watch?v=1PwdqkKDCSo @zsmb13
  134. Resources •Moshi, another JSON Processor (Jesse Wilson) • https://developer.squareup.com/blog/moshi-another-json-processor/ •Exploring

    Moshi’s codegen (Zac Sweers) • https://www.zacsweers.dev/exploring-moshis-kotlin-code-gen/ •Reflection Machines (Jesse Wilson) • https://publicobject.com/2016/03/24/reflection-machines/ •Advanced JSON parsing techniques using Moshi and Kotlin (Christophe Beyls) • https://medium.com/@BladeCoder/advanced-json-parsing-techniques- using-moshi-and-kotlin-daf56a7b963d @zsmb13
  135. zsmb.co/talks zsmb13 Márton Braun › Reflection or codegen › Kotlin-friendly

    › Really fast › Great failure modes › Neat APIs Say “hi” to Moshi