HomeAndroidHow Trello Android transformed from Gson to Moshi

How Trello Android transformed from Gson to Moshi


Trello Android just lately transformed from utilizing Gson to Moshi for dealing with JSON. It was a bit tough so I wished to doc the method.

(For context, Trello Android primarily parses JSON. We hardly ever serialize JSON, and thus a lot of the focus right here is on deserializing.)

There have been three important causes for the change from Gson to Moshi: security, pace, and dangerous life selections.

Security – Gson doesn’t perceive Kotlin’s null security and can fortunately place null values into non-null properties. Additionally, default values solely typically work (relying on the constructor setup).

Velocity – Loads of benchmarks (1, 2, 3) have demonstrated that Moshi is often sooner than Gson. After we transformed, we arrange some benchmarks to see how real-world parsing in contrast in our app, and we noticed a 2x-3.5x speedup:

Dangerous life selections – As an alternative of utilizing Gson to parse JSON into easy fashions, we might write elaborate, complicated, brittle customized deserializers that had fully an excessive amount of logic in them. Refactoring gave us a possibility to appropriate this architectural snafu.


As for why we picked Moshi over rivals (e.g. Kotlin serialization), we usually belief Sq.’s libraries, we have used Moshi prior to now for tasks (each at work and at dwelling) and felt it labored properly. We didn’t do an in-depth examine of options.

Step one was to make sure that we might use characteristic flags to modify between utilizing our previous Gson implementation and the brand new Moshi one. I wrote a JsonInterop class which, primarily based on the flag, would both parse all JSON responses utilizing Gson or Moshi.

(I opted to keep away from utilizing instruments like moshi-gson-interop as a result of I wished to check whether or not Moshi parsing labored in its entirety. In case you’d quite have a mixture of Gson and Moshi on the identical time, that library could be helpful.)

Gson offers you alternatives to override the default naming of a key utilizing @SerializedName. Moshi allows you to do the identical factor with @Json. That is all properly and good, nevertheless it appeared very easy to me to make a mistake right here, the place a property is parsed underneath completely different names in Gson vs. Moshi.

Thus, I wrote some unit exams that might confirm that our generated Moshi adapters would have the identical consequence as Gson’s parsing. Particularly, I examined…

  • …that Moshi might generate an adapter (not essentially an accurate one!) for every class we wished to deserialize. (If it could not, Moshi would throw an exception.)
  • …that every subject annotated with @SerializedName was additionally annotated with @Json (utilizing the identical key).

Between these two checks, it was simple to seek out after I’d made a mistake updating our courses in later steps.

(I can’t embody the supply right here, however mainly we used Guava’s ClassPath to collect all our courses, then scan by means of them for issues.)

Gson means that you can parse generic JSON bushes utilizing JsonElement (and associates). We discovered this handy in some contexts like parsing socket updates (the place we wouldn’t understand how, precisely, to parse the response mannequin till after some preliminary processing).

Clearly, Moshi shouldn’t be going to be completely happy about utilizing Gson’s courses, so we switched to utilizing Map<String, Any?> (and typically Checklist<Map<String, Any?>>) for generic bushes of knowledge. Each Gson and Moshi can parse these:

enjoyable <T> fromJson(map: Map<String, Any?>?, clz: Class<T>): T? {
  return if (USE_MOSHI) {
    moshi.adapter(clz).fromJsonValue(map)
  }
  else {
    gson.fromJson(gson.toJsonTree(map), clz)
  }
}

As well as, Gson is pleasant in the direction of parsing through Readers, however Moshi shouldn’t be. I discovered that utilizing BufferedSource was a very good various, as it may be transformed to a Reader for previous Gson code.

The best adapters for Moshi are those the place you simply slap @JsonClass on them and name it a day. Sadly, as I discussed earlier, we had loads of unlucky customized deserialization logic in our Gson parser.

It’s fairly simple to write a customized Moshi adapter, however as a result of there was a lot customized logic in our deserializers, simply writing a single adapter wouldn’t reduce it. We ended up having to create interstitial fashions to parse the uncooked JSON, then adapt from that to the fashions we’re used to utilizing.

To provide a concrete instance, think about now we have a knowledge class Foo(val depend: Int), however the precise JSON we get again is of the shape:

{
  "knowledge": { 
    "depend": 5
  }
}

With Gson, we might simply manually take a look at the tree and seize the depend out of the knowledge object, however now we have found that method lies insanity. We might quite simply parse utilizing easy POJOs, however we nonetheless need to output a Foo in the long run (so we do not have to vary our entire codebase).

To unravel that drawback, we’d create new fashions and use these in customized adapter, like so:

@JsonClass(generateAdapter = true) knowledge class JsonFoo(val knowledge: JsonData)

@JsonClass(generateAdapter = true) knowledge class JsonData(val depend: Int)

object FooAdapter {
  @FromJson
  enjoyable fromJson(json: JsonFoo): Foo {
    return Foo(depend = json.knowledge.depend)
  }
}

Voila! Now the parser can nonetheless output Foo, however we’re utilizing easy POJOs to mannequin our knowledge. It’s each simpler to interpret and straightforward to check.

Keep in mind how I stated that Gson will fortunately parse null values into non-null fashions? It seems that we had been (sadly) counting on this conduct in all kinds of locations. Particularly, Trello’s sockets usually return partial fashions – so whereas we’d usually count on, say, a card to come back again with a reputation, in some circumstances it gained’t.

That meant having to observe our crashes for circumstances the place the Moshi would blow up (as a result of a null worth) when Gson could be completely happy as a clam. That is the place characteristic flags actually shine, because you don’t need to need to push a buggy parser on unsuspecting manufacturing customers!

After fixing a dozen of those bugs, I really feel like I’ve gained a hearty appreciation for non-JSON applied sciences with well-defined schemas like protocol buffers. There are loads of bugs I bumped into that merely wouldn’t have occurred if we had a contract between the server and the consumer.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments