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

Say "hi" to Moshi (DevFest Romania 2021)

4047c64e3a1e2f81addd4ba675ddc451?s=47 Marton Braun
November 13, 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/

4047c64e3a1e2f81addd4ba675ddc451?s=128

Marton Braun

November 13, 2021
Tweet

More Decks by Marton Braun

Other Decks in Programming

Transcript

  1. Say “hi” to zsmb.co zsmb13 Márton Braun Moshi

  2. @zsmb13

  3. Serialization @zsmb13

  4. JSON Serialization @zsmb13

  5. JSON Serialization on the JVM @zsmb13

  6. @zsmb13

  7. @zsmb13

  8. @zsmb13

  9. @zsmb13

  10. Gson github.com/google/gson @zsmb13

  11. Gson 101 @zsmb13

  12. Gson 101 val burger: Burger = Gson().fromJson("{...}", Burger::class.java) @zsmb13

  13. Gson 101 val burger: Burger = Gson().fromJson("{...}", Burger::class.java) val json:

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

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

    String = Gson().toJson(Burger()) @zsmb13
  16. Gson 101 val gson = Gson() val burger: Burger =

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

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

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

  20. Gson val gson = Gson() gson.toJson(Date()) : built-in types @zsmb13

    "Nov 13, 2021 : : AM" 10 55 22
  21. Gson val gson = Gson() gson.toJson(Date()) : built-in types @zsmb13

    "Nov 13, 2021 : : AM" 10 56 11
  22. 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
  23. 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
  24. 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
  25. 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
  26. Gson: field naming val gson = Gson() @zsmb13

  27. Gson: field naming val gson = GsonBuilder() .create() @zsmb13

  28. val gson = GsonBuilder() .setFieldNamingPolicy( FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .create() Gson: field naming

    @zsmb13
  29. 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
  30. 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
  31. 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
  32. 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
  33. @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
  34. Gson: missing data Gson() val gson = @zsmb13

  35. data class Burger( val name: String, val price: Int )

    Gson: missing data Gson() val gson = @zsmb13
  36. 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
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. val gson = Gson() Gson: malformed data @zsmb13

  43. val gson = Gson() Gson: malformed data data class Burger(val

    name: String, val price: Int) data class Restaurant(val menu: List<Burger>) @zsmb13
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. None
  51. Say “hi” to zsmb.co zsmb13 Márton Braun Moshi

  52. Moshi github.com/square/moshi @zsmb13

  53. Okio github.com/square/okio Retrofit github.com/square/retrofit Moshi github.com/square/moshi OkHttp github.com/square/okhttp @zsmb13

  54. Moshi github.com/square/moshi @zsmb13

  55. Moshi 101 @zsmb13

  56. Moshi 101 def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" @zsmb13

  57. 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
  58. // ... 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
  59. // ... 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
  60. // ... 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
  61. // ... Moshi 101 val moshi = Moshi.Builder().build() val json

    = moshi.toJson(JavaBurger("Classic", 10)) } public class JavaBurger { private String name; private int price; @zsmb13
  62. // ... 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
  63. // ... 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
  64. // ... 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
  65. // ... 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
  66. 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
  67. 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
  68. 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
  69. 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
  70. 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
  71. 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
  72. 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
  73. 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
  74. 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
  75. Moshi: reflection .add(KotlinJsonAdapterFactory()) val burgerAdapter: JsonAdapter<Burger> = moshi.adapter(Burger::class.java) class Burger(val

    name: String, val price: Int) .build() val moshi = Moshi.Builder() @zsmb13
  76. 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
  77. 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}
  78. 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)
  79. Moshi: reflection def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"

    @zsmb13
  80. 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
  81. 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
  82. def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" kapt moshi-kotlin-codegen:$moshi_version" "com.squareup.moshi: @zsmb13

    Moshi: codegen
  83. Moshi: codegen def moshi_version = '1.12.0' implementation "com.squareup.moshi:moshi:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"

    @zsmb13
  84. 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
  85. Moshi: codegen class Burger(val name: String, val price: Int) @zsmb13

  86. @JsonClass(generateAdapter = true) class Burger(val name: String, val price: Int)

    @zsmb13 Moshi: codegen
  87. @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
  88. 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
  89. 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
  90. 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
  91. 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
  92. @FromJson fun fromJson(reader: JsonReader): Burger? { } @zsmb13 Moshi: custom

    adapters
  93. Moshi: custom adapters @FromJson fun fromJson(reader: JsonReader): Burger? { var

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

    name: String? = null var price: Int? = null reader.beginObject() reader.endObject() } @zsmb13
  95. 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
  96. 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
  97. @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
  98. 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
  99. 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
  100. 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
  101. 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
  102. 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
  103. 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
  104. 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
  105. 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
  106. 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
  107. 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
  108. 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
  109. 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
  110. 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
  111. val moshi = Moshi.Builder().build() val adapter: JsonAdapter<Date> = moshi.adapter(Date::class.java) @zsmb13

    : built-in types Moshi
  112. val moshi = Moshi.Builder().build() val adapter: JsonAdapter<Date> = moshi.adapter(Date::class.java) @zsmb13

    Moshi: built-in types
  113. 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
  114. 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
  115. 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
  116. 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"

    val adapter: JsonAdapter<Date> = moshi.adapter(Date::class.java) .build() val moshi = Moshi.Builder() @zsmb13 Moshi: built-in types
  117. 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
  118. 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
  119. @zsmb13 Moshi: field naming

  120. @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
  121. @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
  122. @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
  123. @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
  124. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    price: Int ) @zsmb13 Moshi: missing data
  125. @JsonClass(generateAdapter = true) data class Burger( val name: String, val

    price: Int ) Moshi: missing data @zsmb13
  126. @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
  127. @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
  128. @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
  129. @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
  130. @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
  131. @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
  132. @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
  133. 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
  134. 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
  135. 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
  136. 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
  137. 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
  138. 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
  139. 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
  140. 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
  141. 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
  142. 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
  143. 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
  144. 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>
  145. 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
  146. 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
  147. 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
  148. 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
  149. 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
  150. 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
  151. 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
  152. 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
  153. 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
  154. 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
  155. 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
  156. @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
  157. @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
  158. Moshi: malformed data @zsmb13

  159. @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
  160. @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
  161. @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
  162. 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
  163. 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
  164. """{ "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
  165. 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
  166. 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
  167. 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
  168. 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
  169. 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
  170. 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
  171. 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
  172. @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
  173. def retrofit_version = '2.9.0' implementation "com.squareup.retrofit2:retrofit:$retrofit_version" @zsmb13 Moshi: Retrofit integration

  174. def retrofit_version = '2.9.0' implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" @zsmb13 Moshi:

    Retrofit integration
  175. 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" @zsmb13 Moshi: Retrofit integration
  176. 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
  177. 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()
  178. 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
  179. 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
  180. 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
  181. 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
  182. zsmb.co/talks zsmb13 Márton Braun › Reflection or codegen › Kotlin-friendly

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