What’s Moshi?

It’s rare for a programmer to get to build the same thing twice. Either the first one is good enough and you’re done, or it wasn’t and you’re foolish to try again. I’ve had two chances to build the same thing twice.

Guice to Dagger

When I started at Square in 2012, the Android team was using Google Guice for dependency injection. I’d previously contributed to Guice and so it was nice to use familiar code at my new job. But there was a problem: Guice was too slow on even the newest Gingerbread devices.

I proposed fixing the problem by switching from the reflection-based Guice to a new codegen-based dependency injector that we’d create. I started the prototype that would eventually become Dagger. It was a lot easier to build something the second time! I knew which features were important and which could be left out. Dagger 1.0 borrowed Guice’s core features and made them simpler and faster.

Google has since taken over Dagger development. They’ve expanded its API and improved its performance.

Gson

I’ve also contributed to Google Gson, which Square uses for JSON encoding and databinding. As with Guice, it was nice to use a library I contributed to and was proud of. Gson is fast, secure, and easy to get started with. It has a flexible databinding model that scales up to sophisticated use cases.

But the more I used Gson, the more a few old mistakes annoyed me. For example, Gson’s default date format is broken:

Date epoch = new Date(0);
String epochJson = new Gson().toJson(epoch);
// "Dec 31, 1969 7:00:00 PM"

The encoded date doesn’t have a time zone so your data is corrupted if the encoder and decoder don’t share a time zone. This frustrating part of this bug is that it’s also dangerous to fix: we don’t know what applications are expecting the current format and so we don’t know what will break should we change it.

Another wrinkle is how the library responds to bad input. When you use Gson.fromJson() to convert a string to JSON it is always lenient. It considers {a=TRUE} to be equivalent to the JSON document {"a":true}. The method also returns normally for invalid inputs like the empty string and null. This behavior hides bugs, but fixing it is risky for existing users.

Moshi

18 months ago Jake, Scott, and I created Moshi in an attempt to borrow some of Gson’s core ideas and make them simpler and faster. We wanted a library that was small, efficient, and safe by default.

Moshi tries to prevent errors when encoding and can detect errors when decoding. For example, if you add a java.util.UUID field to one of your model objects Moshi will complain until you explicitly register an adapter for the binding. That way you won’t inadvertently leak JDK implementation details into your JSON data.

IllegalArgumentException: Platform class java.util.UUID requires explicit JsonAdapter to be registered
at com.squareup.moshi.ClassJsonAdapter$1.create()
at com.squareup.moshi.Moshi.adapter()

Similarly, Moshi has a strict mode that fails on unused fields. This is a great option to enable for integration testing!

JsonDataException: Cannot skip unexpected STRING at $.fullNane
at com.squareup.moshi.JsonUtf8Reader.skipValue()
at com.squareup.moshi.ClassJsonAdapter.fromJson()
at com.squareup.moshi.JsonAdapter.fromJson()

We’ve been quietly completing Moshi’s feature set and improving its performance. With the recent 1.4 release we added support for JSON values which convert your objects into either a byte stream or a memory model.

If you’re building a new application and need JSON please consider Moshi. It’s ready.