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

Upgrading to Moshi

Eric Cochran
September 05, 2017

Upgrading to Moshi

Moshi is the successor to Gson. I will briefly contrast Moshi to Gson and explain the advantages of Moshi's API and implementation. I will cover the power of the streaming API for complex use cases, like polymorphic deserialization, and how Okio's types make building off of Moshi fun and easy. Finally, we will go through how to use Moshi effectively with Retrofit and Auto-Value-Moshi and some tricks to employ when upgrading.

Eric Cochran

September 05, 2017
Tweet

More Decks by Eric Cochran

Other Decks in Programming

Transcript

  1. What is Moshi? JSON serialization library for Java with a

    streaming and object-mapping API. Gson 2
  2. What is Moshi? JSON serialization library for Java with a

    streaming and object-mapping API. Gson 2 => “Gson Lite”
  3. Why update from Gson? Gson: - Inactive - Too lenient

    - Large API - Inconsistent exceptions (Moshi: IOExceptions and JsonDataExceptions)
  4. Why update from Gson? Gson: - Inactive - Too lenient

    - Large API - Inconsistent exceptions (Moshi: IOExceptions and JsonDataExceptions) - ~188KB, 1345 methods (Moshi: 112KB, 759 methods)
  5. Why update from Gson? Gson: - Inactive - Too lenient

    - Large API - Inconsistent exceptions (Moshi: IOExceptions and JsonDataExceptions) - ~188KB, 1345 methods (Moshi: 112KB, 759 methods) Moshi optimizations and API niceties (continued…)
  6. Streaming API It’s the same! com.google.gson.stream.JsonReader => com.squareup.moshi.JsonReader com.google.gson.stream.JsonWriter =>

    com.squareup.moshi.JsonWriter Moshi Bonus: JsonReader.Options Moshi Bonus: JsonReader.setFailOnUnknown
  7. JsonReader.Options Prepare strings ahead of time: Options.of(“key1”, “key2”) Read out

    directly from the input source: JsonReader.selectName(options), JsonReader.selectString(options) returns index of string in Options.
  8. Object Mapping TypeAdapter => JsonAdapter No document-level API like Gson.fromJson

    Gson.getAdapter(Type) => Moshi.adapter(Type) Cache your adapters! https://publicobject.com/2016/03/24/reflection-machines/
  9. Object Mapping without bad leniency Platform types require explicitly registered

    JsonAdapters. moshi.adapter(java.util.Date.class) moshi.adapter(java.util.ArrayList.class) moshi.adapter(android.graphics.Point.class)
  10. Object Mapping TypeToken => com.squareup.moshi.Types factory methods Moshi prefers plain

    Java’s java.lang.reflect.Type. TypeToken.getParameterized(List.class, String.class) => Types.newParameterizedType(List.class, String.class)
  11. Object Mapping: Unknown Enums enum Exercise { RUN, JUMP, WALK

    } Gson: exerciseTypeAdapter.fromJson(“jog”) == null Moshi: exerciseJsonAdapter.fromJson(“jog”) => throws JsonDataException
  12. Object Mapping: Unknown Enums enum Exercise { RUN, JUMP, WALK

    } Gson: exerciseTypeAdapter.fromJson(“jog”) == null Moshi: exerciseJsonAdapter.fromJson(“jog”) => throws JsonDataException EnumWithDefaultValueJsonAdapter: https://goo.gl/85U7Pu API in Moshi to have fallback enums?
  13. Object Mapping: JsonQualifier Special-case type qualifiers: class Data { @JsonAdapter(WrappedStringTypeAdapter.class)

    String string; } => @Retention(RUNTIME) @JsonQualifier @interface WrappedString {} class Data { @WrappedString String string }
  14. Object Mapping: JsonQualifier class WrappedStringTypeAdapter extends TypeAdapter<String> { String read(JsonReader

    reader) throws IOException { reader.beginObject(); String string = reader.nextString(); reader.endObject(); return string; } }
  15. Object Mapping: JsonQualifiers class WrappedStringAdapter { @FromJson @WrappedString String fromJson(JsonReader

    reader) { reader.beginObject(); String string = reader.nextString(); reader.endObject(); return string; } }
  16. Object Mapping: Easier JsonAdapters class PointJsonAdaperFactory implements JsonAdapter.Factory { JsonAdapter<?>

    create(Type type, Set<? extends Annotation> annotations, Moshi moshi) { if (Types.getRawType(types) != Point.class) return null; return new JsonAdapter<Point> { Point fromJson(JsonReader reader) {...} void toJson(JsonWriter writer) {...} } } }
  17. Object Mapping: Easier JsonAdapters class PointJsonAdaperFactory implements JsonAdapter.Factory { JsonAdapter<?>

    create(Type type, Set<? extends Annotation> annotations, Moshi moshi) { if (Types.getRawType(types) != Point.class) return null; return new JsonAdapter<Point> { Point fromJson(JsonReader reader) {...} void toJson(JsonWriter writer) {...} } } }
  18. Object Mapping: Easier JsonAdapters class PointJsonAdaperFactory implements JsonAdapter.Factory { JsonAdapter<?>

    create(Type type, Set<? extends Annotation> annotations, Moshi moshi) { if (Types.getRawType(types) != Point.class) return null; return new JsonAdapter<Point> { Point fromJson(JsonReader reader) {...} void toJson(JsonWriter writer, Point value) {...} } } }
  19. Object Mapping: Easier JsonAdapters class PointJsonAdaperFactory implements JsonAdapter.Factory { JsonAdapter<?>

    create(Type type, Set<? extends Annotation> annotations, Moshi moshi) { if (Types.getRawType(types) != Point.class) return null; return new JsonAdapter<Point> { Point fromJson(JsonReader reader) {...} void toJson(JsonWriter writer, Point value) {...} } } } => class PointJsonAdapter { @FromJson Point fromJson(JsonReader reader) {...} @ToJson void toJson(JsonWriter writer, Point value) {...} }
  20. Even Easier JsonAdapters @FromJson Foo fromJson(JsonReader reader) @FromJson Foo fromJson(JsonReader

    jsonReader, JsonAdapter<any> delegate, <any more delegates>) @FromJson Foo fromJson(Bar value) // Bar is already a type that can be deserialized.
  21. Even Easier JsonAdapters @FromJson Foo fromJson(JsonReader reader) @FromJson Foo fromJson(JsonReader

    jsonReader, JsonAdapter<any> delegate, <any more delegates>) @FromJson Foo fromJson(Bar value) // Bar is already a type that can be deserialized. @ToJson void toJson(JsonWriter writer, Foo value) @ToJson void toJson(JsonWriter writer, JsonAdapter<any> delegate, <any more delegates>) @ToJson void toJson(JsonWriter writer, T value, JsonAdapter<any> delegate, <any more delegates>) @ToJson Bar toJson(Foo value) // Bar is already a type that can be serialized.
  22. Polymorphic types class Animal { String type; } List<Animal> animals

    = animalAdapter.fromJson(source) class Dog extends Animal { String bark; } class Cat extends Animal { int meals; }
  23. Polymorphic types Gson: class AnimalTypeAdapter extends TypeAdapter<Animal> { TypeAdapter<JsonElement> elementAdapter;

    TypeAdapter<Cat> catAdapter; TypeAdapter<Dog> dogAdapter; Animal read(JsonReader in) { JsonObject value = elementAdapter.read(in).getAsJsonObject(); // Inspect the value. Decide what TypeAdapter to delegate to. if (value.get(“type”).getAsString().equals(“cat”)) return catAdapter.fromJsonTree(value); // ... } }
  24. Polymorphic types Gson: class AnimalTypeAdapter extends TypeAdapter<Animal> { TypeAdapter<JsonElement> elementAdapter;

    TypeAdapter<Cat> catAdapter; TypeAdapter<Dog> dogAdapter; Animal read(JsonReader in) { JsonObject value = elementAdapter.read(in).getAsJsonObject(); // Inspect the value. Decide what TypeAdapter to delegate to. if (value.get(“type”).getAsString().equals(“cat”)) return catAdapter.fromJsonTree(value); // ... } }
  25. Polymorphic types Moshi: class AnimalJsonAdapter extends JsonAdapter<Animal> { JsonAdapter<Cat> catAdapter;

    JsonAdapter<Dog> dogAdapter; Animal fromJson(JsonReader in) { Map<String, Object> value = (Map<String, Object>) in.readJsonValue(); // Inspect the value. Decide what JsonAdapter to delegate to. if (value.get(“type”).equals(“cat”)) return catAdapter.fromJsonValue(value); // ... } }
  26. Polymorphic types Moshi: class AnimalJsonAdapter extends JsonAdapter<Animal> { JsonAdapter<Cat> catAdapter;

    JsonAdapter<Dog> dogAdapter; Animal fromJson(JsonReader in) { Map<String, Object> value = (Map<String, Object>) in.readJsonValue(); // Inspect the value. Decide what JsonAdapter to delegate to. if (value.get(“type”).equals(“cat”)) return catAdapter.fromJsonValue(value); // ... } }
  27. Retrofit AnnotatedConverterFactory https://goo.gl/nhLt8z @Moshi for new code Retrofit retrofit =

    new Retrofit.Builder().baseUrl(server.url("/")) .addConverterFactory(new AnnotatedConverterFactory.Builder() .add(com.example.Moshi.class, moshiConverterFactory) .add(com.example.Gson.class, gsonConverterFactory) .build()) .addConverterFactory(gsonConverterFactory) // Fallback. .build(); interface Service { @GET("/new_endpoint") @com.example.Moshi Call<Foo> newEndpoint(); @GET("/old_endpoint") @Gson Call<Foo> oldEndpoint(); @GET("/old_endpoint") Call<Foo> oldEndpointDefault(); // Will use fallback. }
  28. Auto-Value-Moshi https://github.com/rharter/auto-value-moshi @AutoValue abstract class Data { abstract String string();

    static JsonAdapter<Data> jsonAdapter(Moshi moshi) { return new AutoValue_Data.MoshiJsonAdapter(moshi); } }