5

I have a Sticker class and its wrapper:

@JsonClass(generateAdapter = true)
class StickerDto(
        @Json (name = "totalAnimatedStickers") val total: Int,
        @Json(name = "pages") val pages: Int,
        @Json(name = "data") val stickers: List<Sticker>

)

@JsonClass(generateAdapter = true)
class Sticker(
        @Json(name = "name") val name: String,
        @Json(name = "id") val id: String,
        @Json(name = "stickerData") val stickerData: JsonObject,
        var isSelected:Boolean = false
)

The stickerData attribute comes from the api with a dynamic json object with unknown attributes

"stickerData": {}

How do I deserialize an object like that using Moshi?

My current retrofit client:

 private fun createNewFriendsClient(authRefreshClient: AuthRefreshClient,
                                       preferencesInteractor: PreferencesInteractor): FriendsApiClient {

        val logger = run {
            val httpLoggingInterceptor = HttpLoggingInterceptor()
            httpLoggingInterceptor.apply {
                httpLoggingInterceptor.level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
            }
        }

        val okHttp = OkHttpClient.Builder().addInterceptor(logger).authenticator(RefreshUserAuthenticator(authRefreshClient, preferencesInteractor,
                UnauthorizedNavigator(SDKInternal.appContext, Interactors.preferences))).build()


        return Retrofit.Builder()
                .client(okHttp)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(MoshiConverterFactory.create())
                .baseUrl(Interactors.apiEndpoint)
                .build()
                .create(FriendsApiClient::class.java)


    }

Gives me an

"Unable to create converter for class StickerDto"
Caused by NoJsonAdapter for java.util.Comparator<? super java.lang.String>

error. What converter do I need to use if not that Moshi one? Trying to pull it down as a string also gives an error as it is expecting and object. I just need that string.

Edit, the Json string is very long but it begins like this:

{"tileId":"1264373a-24d8-4c10-ae90-d6e8f671410c","friendId":"2c50f187-039a-4f85-b12b-0c802396a611","name":"David Carey","message":"Joined WeAre8","animatedSticker":{"v":"5.5.7","fr":24,"ip":0,"op":48,"w":1024,"h":1024,"nm":"party_popper","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"C | Position","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[176,892,0],"to":[-6.667,6.667,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":7,"s":[136,932,0],"to":[0,0,0],"ti":[-6.667,6.667,0]},{"t":11,"s":[176,892,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":7,"s":[115,75,100]},{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":11,"s":[95,105,100]},{"t":20,"s":[100,100,100]}],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Controller","np":13,"mn":"Pseudo/DUIK controller","ix":1,"en":1,"ef":[{"ty":6,"nm":"Icon","mn":"Pseudo/DUIK controller-0001","ix":1,"v":0},{"ty":2,"nm":"Color","mn":"Pseudo/DUIK controller-0002","ix":2,"v":{"a":0,"k":[0.92549020052,0.0941176489,0.0941176489,1],"ix":2}},{"ty":3,"nm":"Position","mn":"Pseudo/DUIK controller-0003","ix":3,"v":{"a":0,"k":[0,0],"ix":3}},{"ty":0,"nm":"Size","mn":"Pseudo/DUIK controller-0004","ix":4,"v":{"a":0,"k":100,"ix":4}},{"ty":0,"nm":"Orientation","mn":"Pseudo/DUIK controller-0005
andrewedgar
  • 797
  • 1
  • 12
  • 46

3 Answers3

8

Note that JsonObject is a class from the gson package, so if you want to use Moshi you will need to switch to JSONObject which is the default class supported by Android.

To do this you will need to write your own JSONObject adapter.

First, write your adapter class:

import com.squareup.moshi.FromJson
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.ToJson
import okio.Buffer
import org.json.JSONException
import org.json.JSONObject

class JSONObjectAdapter {
    
    @FromJson
    fun fromJson(reader: JsonReader): JSONObject? {
        // Here we're expecting the JSON object, it is processed as Map<String, Any> by Moshi
        return (reader.readJsonValue() as? Map<String, Any>)?.let { data ->
            try {
                JSONObject(data)
            } catch (e: JSONException) {
                // Handle exception
                return null
            }
        }
    }
    
    @ToJson
    fun toJson(writer: JsonWriter, value: JSONObject?) {
        if (value != null) {
            writer.value(Buffer().writeUtf8(value.toString()))
        } else {
            writer.value(null as String?)
        }             
    }
   
}

Adjust your retrofit build to provide custom Moshi object when creating the MoshiConverterFactory:

    .addConverterFactory(MoshiConverterFactory.create(Moshi.Builder().add(JSONObjectAdapter()).build()))

and then you are good to go and use JSONObject

@Json(name = "stickerData") val stickerData: JSONObject

Good luck and I hope this helps!

Egis
  • 5,081
  • 5
  • 39
  • 61
Viktor Petrovski
  • 796
  • 5
  • 14
2

Built-in Type Adapters for Moshi include Arrays, Collections, Lists, Sets, and Maps. A JsonObject type is not provided with Moshi itself, but it would be an enhanced Map<String, Any> anyhow, so just use the Map instead of an object.

@JsonClass(generateAdapter = true)
class Sticker(
        @Json(name = "name") val name: String,
        @Json(name = "id") val id: String,
        @Json(name = "stickerData") val stickerData: Map<String, Any>,
        var isSelected: Boolean = false
)

The values are automatically converted as well. Thus you'll find strings, lists or numbers there.

tynn
  • 38,113
  • 8
  • 108
  • 143
-2

I made stickerData into a Map and used a GsonConverterFactory instead of Moshi.

andrewedgar
  • 797
  • 1
  • 12
  • 46
  • 1
    Still this doesn't give the solution to the question. Its a workaround. What if we need to use the Moshi instead of Gson? – Jaswant Singh Jul 13 '20 at 17:31
  • 1
    yep I'd go with gson, I had to do something similar for a very simple object `[ time: '2019-10-11...' , time : 123456 ]` and just gave up and went gson. Moshi is very cumbersome for simple objects that need a simple adapter. – Fabio Jul 17 '20 at 06:59