Android: Apollo 3 Migration Issue (Custom Scalars)

Hi! I’m trying to upgrade our project to Apollo3 (3.6.0), but I am running into issues with a Custom Scalar. In some instances we have a complex JSON structure and instead of parsing it into a Map I’d like it kept as a JSON-string. This is what I was doing before:

    private val jsonTypeAdapter = object : CustomTypeAdapter<String> {
        override fun encode(value: String): CustomTypeValue<*> {
            return CustomTypeValue.GraphQLString(value)
        }

        override fun decode(value: CustomTypeValue<*>): String {
            (value.value as? LinkedHashMap<*, *>)?.let {
                return JSON(it).toString()
            }
            return value.value as String
        }
    }
    val client = ApolloClient.builder()
            .serverUrl(apiConfiguration.getGraphQlHost())
            .okHttpClient(okHttpClient)
            .addCustomTypeAdapter(CustomType.JSON, jsonTypeAdapter)
            .build()
        return client

However I’ve tried this a few different ways using apollo3 all of which aren’t working. Here’s what I’ve tried thus far.

  1. Configure JsonString adapter in gradle configuration
apollo {
    generateApolloMetadata.set(true)
    mapScalar("Date", "java.util.Date", "com.apollographql.apollo3.adapter.DateAdapter")
    mapScalar("JSON", "kotlin.String")
}
  1. Provide my own custom adapter
apollo {
   // other configuration
   mapScalar("JSON", "kotlin.String", "com.android.schema.ApolloJsonAdapter")
}
// ApolloJsonAdapter Implementation
  val ApolloJsonAdapter = object: Adapter<String> {
    override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): String {
        return reader.toString()
    }

    override fun toJson(
        writer: JsonWriter,
        customScalarAdapters: CustomScalarAdapters,
        value: String
    ) {
        writer.writeAny(value)
    }
}

Any help on how I can leave the complex “JSON” types as json formatted strings? Do I need to do more parsing in the fromJson method?

Hi! :wave:

You were not far from a working solution but reader.toString() won’t return a JSON string as you want. Instead, you can use readyAny to parse the JSON as an object first and then AnyAdapter. toJsonString() to make it a String, like so:

val ApolloJsonAdapter = object : Adapter<String> {
  private val nullableAnyAdapter = AnyAdapter.nullable()

  override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): String {
    val jsonAsAny: Any? = reader.readAny()
    return nullableAnyAdapter.toJsonString(jsonAsAny, customScalarAdapters)
  }

  override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: String) {
    val stringAsBuffer = Buffer().writeUtf8(value)
    val stringAsAny: Any? = BufferedSourceJsonReader(stringAsBuffer).readAny()
    nullableAnyAdapter.toJson(writer, customScalarAdapters, stringAsAny)
  }
}