I'm receiving some JSON from an OrientDB server that looks something like this:
{
...
"out": ...,
"in": ...,
...
}
Now these two fields out
and in
can be one of two types: String
and my own custom object (let's call it a Record
). For example, for one request I might receive this:
{
...
"out": "#17:0",
"in": {
...
},
...
}
For another I might get:
{
...
"out": {
...
},
"in": "#18:2",
...
}
And so on. Both might be String
s, both might be Records
, one might be a String
and the other a Record
, et cetera et cetera. Now when I'm deserializing this kind of JSON using Moshi, I'd have two parameters out
and in
to hold the values of their respective keys; however, because these values aren't a fixed data type, that's easier said than done.
Creating multiple POJOs (or "POKO"s, I guess, because I'm using Kotlin) wouldn't work, because these objects can be found inside other JSON objects and stuff like that. I'd need a single object for which these parameters can take on a variable data type. So how would I do that?
Would I have to write a custom adapter in Moshi for serializing/deserializing these values? If so, how would I go about writing one that can assign a certain data type depending on the value of the parameter? Or is there some sort of Kotlin class/function/extension function I can find/write that can hold two possible data types?
If it's relevant, I'm also using Retrofit 2 + RxJava 2 to make my HTTP calls asynchronously, so if there's any data types or functions in these libraries that facilitates something like this, I'm all ears.
Even if anyone can only answer in Java that's okay, because I can convert the code myself. And if I'm missing something obvious, I apologize in advance.
You can do something like what I did in this answer: https://stackoverflow.com/a/65106419/3543610
Basically you'd create a sealed class to be the type of both your properties in
and out
. You'd also need to wrap the primitive string one into a type holding a String, so you can make it extend the sealed class, something like this:
sealed class YourType {
data class StringData(val value: String) : YourType()
@JsonClass(generateAdapter = true)
data class Record(
val prop1: String,
val prop2: Int
) : YourType()
}
Then your model that has these properties would look smth like:
@JsonClass(generateAdapter = true)
data class Model(
...
val in: YourType,
val out: YourType,
...
)
then finally you write your custom adapter for the YourType
type:
class YourTypeCustomAdapter {
@FromJson
fun fromJson(jsonReader: JsonReader, delegate: JsonAdapter<Record>): YourType? {
return if (jsonReader.peek() == BEGIN_OBJECT) {
delegate.fromJson(jsonReader)
} else {
StringData(jsonReader.nextString())
}
}
@ToJson
fun toJson(jsonWriter: JsonWriter, yourType: YourType, delegate: JsonAdapter<Record>) {
when (yourType) {
is Record -> delegate.toJson(jsonWriter, yourType)
is StringData -> jsonWriter.value(yourType.value)
}
}
}
and register with Moshi:
private val moshi = Moshi.Builder()
.add(YourTypeCustomAdapter())
.build()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With