I’m having trouble creating an Adapter for an obje...
# apollo-kotlin
s
I’m having trouble creating an Adapter for an object which comes from the backend and is read from the JsonReader as a LinkedHashMap 🤔
There’s the
scalar PaymentMethodsResponse
which when going inside the PaymentMethodsApiResponseAdapter I am building, the reader is in a state where peekedToken is BEGIN_OBJECT and peekedData is a LinkedHashMap. As I understand that is because the object in the json starts with a
{
. This is a test with a json of how the backend would respond (from a real response). And this is the adapter where this line is hit and I do in fact get a LinkedHashMap as I am debugging this. Thing is, there is no such function on the JsonReader to read this normally, and the only approached I have found that works is calling the @ApolloInternal
readAny()
and then checking if its type is LinkedHashMap but I am afraid doing this will just break in future releases since it’s supposed to be internal. Does this seem like something that our backend is doing wrong instead of something that the library should be handling differently? Or any ideas?
m
Looks like you have a custom scalar that is represented as a Json object ?
To read this from an adapter, you can call
beginObject()/endObject()
The typical way to do it is with a
when
statement in a loop:
Copy code
class PaymentMethodsResponse(
      val success: Boolean,
      val code: String
  )

  val adapter = object: Adapter<PaymentMethodsResponse> {
    override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): PaymentMethodsResponse {
      reader.beginObject()
      var success: Boolean? = null
      var code: String? = null
      while (reader.hasNext()) {
        val name = reader.nextName()
        when (name) {
          "success" -> success = reader.nextBoolean()
          "code" -> code = reader.nextString()
        }
      }
      reader.endObject()
      
      return PaymentMethodsResponse(
          success ?: error("'success' is missing"), 
          code ?: error("'code' is missing")
      )
    }
This is the most efficient way to do it because it streams the Json
If you don't want to deal with this, you can delegate to
AnyAdapter
instead of using
readAny
Copy code
val map = AnyAdapter.fromJson(reader, customScalarAdapters)
AnyAdapter
is public API and isn't going anywhere any time soon
s
Wow yeah I wasn’t aware I could go that approach, of course! I can’t quite do the looping approach as the things I am receiving are not quite stable and I don’t have access to that object and its fields myself since it comes from an DK. All I want to do with that object is send it over to this
PaymentMethodsApiResponse.SERIALIZER
which comes from the Adyen SDK which should just know how to handle the rest. So the AnyAdapter is what I was looking for all along I think! That feels so much better, let me try and get it working real quick!
👍 1
I guess I still have to do the map checks for my use case, but this does make me avoid using the ApolloInternal call. This makes my tests pass:
Copy code
override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): PaymentMethodsApiResponse {
    val data = AnyAdapter.fromJson(reader, customScalarAdapters)
    return if (data is LinkedHashMap<*, *>) {
        PaymentMethodsApiResponse.SERIALIZER.deserialize(JSONObject(data.toMap()))
    } else {
        val jsonString = data.toString().replace("\\", "")
        PaymentMethodsApiResponse.SERIALIZER.deserialize(JSONObject(jsonString))
    }
}
m
Do you even need to check for
LinkedHashMap
? Could it be
Map<*, *>
instead?
s
Nope, don’t need to do that either. This is the “final” state of it after your input. Don’t need to do .toMap() inside the JSONObject constructor either since the type is smart-cast to a map at that point already so the correct JSONObject constructor is hit.
👌 1
m
Noice 👍
At the end of the day, I guess it's worth asking your backend for a stable format there
s
I would’ve never found the AnyAdapter myself. Sometimes I really need to think just a bit out of the box instead of hammering on the problem at hand 😅 Thanks one again 😄
m
You're flexible about what you receive but they should be strict about what they send
👌 1
🙂 Sure thing!
s
Yeah I’ll try and open that discussion as well. I just think that a lot of this confusion comes from us using the SDK object directly both on the front-end and the back-end and their built-in serializer. Or something like that, I could be wrong, I wasn’t here when we started using Adyen so I am a bit out of the loop 😄
👍 1