"unable to create converter for class when using sealed class or an interface with moshi" Code Answer

5

you can make use of jsonadapter from moshi to parse different json models if you can differentiate them by foreseeing some value in the json.

for example, consider json response having two schemas,

{
  "root": {
      "subroot": {
         "prop" : "hello",
         "type" : "string"
       }
   }
}

(or)

{
  "root": {
      "subroot": {
         "prop" : 100,
         "type" : "integer"
       }
   }
}

here, subroot has different schemas (one containing string property and another containg a integer property) which can be identified by "type"

you can create a parent sealed class with common keys and derive few child classes with varying keys. write a adapter to select the type of class to be used while json serialization and add that adapter to moshi builder.

model classes:

class response {
    @json(name = "root")
    val root: root? = null
}

class root {
    @json(name = "subroot")
    val subroot: hybridmodel? = null
}

sealed class hybridmodel {
    @json(name = "type")
    val type: string? = null

    class stringmodel : hybridmodel() {
        @json(name = "prop")
        val prop: string? = null
    }

    class integermodel : hybridmodel() {
        @json(name = "prop")
        val prop: int? = null
    }
}

few extension methods to jsonreader,

inline fun jsonreader.readobject(process: () -> unit) {
    beginobject()
    while (hasnext()) {
        process()
    }
    endobject()
}

fun jsonreader.skipnameandvalue() {
    skipname()
    skipvalue()
}

hybridadapter to select type of class for "subroot" key

class hybridadapter : jsonadapter<hybridmodel>() {
    @fromjson
    override fun fromjson(reader: jsonreader): hybridmodel {
        var type: string = ""

        // copy reader and  foresee type
        val copy = reader.peekjson()
        copy.readobject {
            when (copy.selectname(jsonreader.options.of("type"))) {
                0 -> {
                    type = copy.nextstring()
                }
                else -> copy.skipnameandvalue()
            }
        }

        //handle exception if type cannot be identified
        if (type.isempty()) throw jsondataexception("missing type")

        // build model based on type
        val moshi = moshi.builder().build()
        return if (type == "string")
            moshi.adapter(hybridmodel.stringmodel::class.java).fromjson(reader)!!
        else
            moshi.adapter(hybridmodel.integermodel::class.java).fromjson(reader)!!
    }

    @tojson
    override fun tojson(p0: jsonwriter, p1: hybridmodel?) {
        // serialization logic
    }
}

finally build moshi with the hybridadapter to serialize hybridmodel,

fun printprop(response: response?) {
    val subroot = response?.root?.subroot
    when (subroot) {
        is hybridmodel.stringmodel -> println("string model: ${subroot.prop}")
        is hybridmodel.integermodel -> println("integer model: ${subroot.prop}")
    }
}

fun main() {
    val jsonwithstringsubroot =
    """
    {
        "root": {
            "subroot": {
                "prop" : "hello",
                 "type" : "string"
            }
        }
    }
    """
    val jsonwithintegersubroot =
    """
    {
        "root": {
            "subroot": {
                "prop" : 1,
                 "type" : "integer"
            }
        }
    }
    """

    val moshi = moshi.builder().add(hybridadapter()).build()

    val response1 = moshi.adapter(response::class.java).fromjson(jsonwithstringsubroot)
    printprop(response1)  // contains hybridmodel.stringmodel

    val response2 = moshi.adapter(response::class.java).fromjson(jsonwithintegersubroot)
    printprop(response2) // contains hybridmodel.integermodel
}
By Andrew Politylo on July 12 2022

Answers related to “unable to create converter for class when using sealed class or an interface with moshi”

Only authorized users can answer the Search term. Please sign in first, or register a free account.