c_idle
c_idle

Reputation: 1574

How to use PolymorphicJsonAdapterFactory with an interface in Moshi?

I have an interface with two concrete types as part of my model that I'd like to serialize/deserialize with Moshi. My issue is that I don't fully understand if PolymorphicJsonAdapterFactory is actually meant for my use case. I've looked at the samples and a few blog posts and (if I'm understanding them correctly) all of them seem to point to the fact that your interface is supposed to have a field in the interface that allows you to determine the type. I'm working in an existing codebase and so I can't easily add a field that would allow me to figure out what type it is by some string literal.

This is where I'm at with my Moshi code and I'm seeking validation on whether or not I'm using PolymorphicJsonAdapterFactory correctly. Note: I'm using java for the moshi portion of the code and for my model. My interface and it's concrete types are in kotlin

String json = ...;

Moshi moshi = new Moshi.Builder()
.add(PolymorphicJsonAdapterFactory.of(PersonInterface.java, "")
            .withSubtype(BusinessPerson.java, "occupation")
            .withSubtype(PolicePerson.java, "rank")
)
.build();
JsonAdapter<MyModel> jsonAdapter = moshi.adapter(MyModel.class);

MyModel myModel = jsonAdapter.fromJson(json);

Note: I'm using java for the moshi portion of the code and for my model. My interface and it's concrete types are in kotlin

MyModel is defined as the following

class MyModel {
    String month;
    PersonInterface person;
}

My interface and concrete classes in kotlin:

Interface PersonInterface {
    val personsName: String?
}

data class BusinessPerson(
        override val personsName: String,
        val occupation: String?
) : PersonInterface

data class PolicePerson(
    override val personsName: String,
    val rank: String?
) : PersonInterface

The goal is to hopefully have Moshi be able to create a

class MyModel {
    String month;
    BusinessPerson person;
}

or a

class MyModel {
    String month;
    PolicePerson person;
}

depending on whether or not the person field contains an occupation (which means it's of type BusinessPerson) or if it contains the field rank (which means it's of type PolicePerson).

Upvotes: 0

Views: 5493

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170815

PolymorphicJsonAdapterFactory.of(PersonInterface.java, "")
    .withSubtype(BusinessPerson.java, "occupation")
    .withSubtype(PolicePerson.java, "rank")

means that JSON for every person is supposed to contain a key "" and "occupation" or "rank" is the value for this key, not a key name itself as you want. So e.g. it would encode a

BusinessPerson("John", "CEO")

as

{"": "occupation", "personsName": "John", "occupation": "CEO"}

I think you'll have to write your own adapter factory for this use-case if you want to avoid a discriminator field.

Upvotes: 2

Related Questions