5

I have some JSON that looks like this:

{
    "name" : "Credit Card",
    "code" : "AUD",
    "value" : 1000
}

and am using Moshi to unmarshall this into a data structure like:

data class Account(
    @Json(name = "name")
    val name: String,

    @Json(name = "currency")
    val currency: String,

    @Json(name = "value")
    val value: Int
)

Everything works well. However, I really would like to extract the currency and value parameters into a separate Money object. So my model looks more like:

data class Money(
    @Json(name = "currency")
    val currency: String,

    @Json(name = "value")
    val value: Int
)

data class Account(
    @Json(name = "name")
    val name: String,

    @Json(name = "???")
    val money: Money
)

The challenge I'm struggling with is how to annotate things so that the Money object can be given two different fields (currency and value) that come from the same level as the parent account.

Do I need to create an intermediate "unmarshalling" object called, say, MoshiAccount and then use a custom adapter to convert that to my real Account object?

I saw How to deseralize an int array into a custom class with Moshi? which looks close (except that in that case, the adapted object (VideoSize) only needs a single field as input... in my case, I need both currency and value)

Any thoughts or suggestions would be much appreciated. Thanks

Craig Edwards
  • 2,118
  • 1
  • 18
  • 23

1 Answers1

6

Moshi's adapters can morph your JSON structure for you.

object ADAPTER {
  private class FlatAccount(
      val name: String,
      val currency: String,
      val value: Int
  )

  @FromJson private fun fromJson(json: FlatAccount): Account {
    return Account(json.name, Money(json.currency, json.value))
  }

  @ToJson private fun toJson(account: Account): FlatAccount {
    return FlatAccount(account.name, account.money.currency, account.money.value)
  }
}

Don't forget to add the adapter to your Moshi instance.

val moshi = Moshi.Builder().add(Account.ADAPTER).add(KotlinJsonAdapterFactory()).build()
val adapter = moshi.adapter(Account::class.java)
Eric Cochran
  • 8,414
  • 5
  • 50
  • 91
  • 1
    Thanks for the confirmation.Essentially, I need to create an intermediate unmarshalling object and then transform that into my real object. While this would work perfectly for a single instance (eg. Account), however, it also implies I need to create an unmarshalling clone for *every* type that contains a Money object. So much boilerplate :-( Appreciate the answer though... – Craig Edwards Oct 04 '17 at 22:18
  • @CraigEdwards JsonAdapter.Factory is where the real power is. You may be able to make a more generalalized solution with it. – Eric Cochran Oct 05 '17 at 08:30