Hi! We’re using Moshi in our project and we’re qu...
# squarelibraries
l
Hi! We’re using Moshi in our project and we’re quite heavily vested and we have a number of fairly advanced custom adapters. It’s working quite well. However, there’s a new problem that I’d like to solve -- if at all possible, in Moshi. Given json like such:
Copy code
{
  "title": "Some title",
  "description": "Description of something",
  "amount": 1000,
  "currency": "NOK",
  "metadata": {
    "more": "stuff"
  }
}
I’d like to unflatten
amount
and
currency
into a its own object when deserialising:
Copy code
data class Item(val title: String, val description: String, val money: Money, val metadata: MetaData)

data class Money(val amount: Int, val currency: String)
Is this doable in a generic-ish way in Moshi? I’d like for that to extend to any Retrofit response object we deem worthy. If no, I guess the alternative would be the less neat:
Copy code
data class Item(val title: String, val description: String, val amount: Int, val currency String, val metadata: MetaData) {
    val money: Money = Money(amount, currency)
}
which I’d like to avoid.
b
Isn’t it just another custom adapter?
toJson extract money, fromJson doesn’t the reverse
l
This scheme have to work across all our different response objects. So the custom adapter needs to be custom in such a way that you don’t have to wire it up for each and every one of these responses.
b
The adapter is hooked up to Moshi so that shouldn’t be a problem?
If you do something like
Copy code
val moshi = Moshi.Builder()
    .add(CardAdapter())
    .build()
and have a unique Moshi instance across your whole app, that’s all you need
l
What does the
CardAdapter
look like? It needs to handle generic-ish objects and it needs to somehow extract the two fields it needs and put into the respective val in the data class. And the data class could be 1 of many different ones.
data class SomeOtherResponse(val index: Int, val money: Money)
f.ex. It needs to be generic, it needs to delegate the rest of the fields to other adapters.
b
This comes from the readme page of Moshi
If many different types will have those two fields you wanna merge, that's gonna be tough. You need a way to know when to merge or not
l
Probably a custom annotation yes.
I was thinking that if I had an annotation
@Wrapped
or something that could signal that this field should get its data from its “parent” (really it’ll be siblings), but I’m struggling to find a neat way to do that.
b
I would look into composing adapters https://github.com/square/moshi#composing-adapters Use something like
@AlwaysSerializeNulls
on all parents, and then hook my logic somewhere
@Zac Sweers I summon you pokemon
z
you’d need a custom adapter for
Item
and need to manually manage those keys yourself to create the intermediate
Money
type
easiest way to get it up and running is the
readJsonValue()
API, which will just give you back a Map<String, Any?> of everything in the current blob
l
I couldn’t get
beginFlatten
to work, but I managed to come up with something. Feel free to use it however you like. Public domain.