thoughtbot/Argo

Decoding the same object with different keys

VillSkog opened this issue · 6 comments

I am currently working with an API that gives me exactly the same JSON on several endpoints (either as part of a bigger object, an array, ...) but they don't always have the same keys ...
I asked the backend developer to make those uniform but he told me that the Android app already uses the API so it won't happen anytime soon.

Thus, I was wondering if there was any way to decode the exact same json (+/- some optionals) but with different keys, without making a whole new class ..

Just to make sure I'm clear, here's an exemple:

json A:

{
    "name" : "Awesome shop",
    "id" : "42"
}

json B

{
    "shop_name" : "Awesome shop",
    "shop_id" : "42"
}

As of now, I use a bridge class ShopB (subclass of Shop) to decode jsonB into a Shop model. Also note that the keys are not always in the form "var" / "shop_var" like in my exemple, they are just completely unrelated.

Yep, it's actually pretty easy with Argo. We supply an Alternative operator (<|>) for exactly this use case:

struct Shop {
  let id: String
  let name: String
}

extension Shop: Argo.Decodable {
  static func decode(_ j: JSON) -> Decoded<Shop> {
    return curry(Shop.init)
      <^> j <| "id"
      <*> (j <| "name") <|> (j <| "shop_name")
  }
}

Oh crap, I didn't see the notification before. That's awesome, thank you.

Actually, it doesn't seem to work so well for optionals. Is there a workaround this ?

struct Shop {
  let id: String
  let name: String
  let images: [String]
}

extension Shop: Argo.Decodable {
  static func decode(_ j: JSON) -> Decoded<Shop> {
    return curry(Shop.init)
      <^> j <| "id"
      <*> j <| "name"
      <*> (json <||? "shop_images" <|> json <||? "images")
  }
}     

The Alternative operator doesn't try to parse the second key since the first one is allowed to be nil (I guess ?).

yep, the trick is to not use the failable operators, and to instead wrap the whole thing in .optional:

<*> .optional(json <|| "shop_images" <|> json <|| "images")

That did the trick indeed. Thank you very much, my code will get much lighter.

Spectacular! Glad I could help. I'm going to go ahead and close this now, but feel free to re-open if you have any additional questions.