I could use some help on a tricky serialization is...
# serialization
j
I could use some help on a tricky serialization issue. For context, I am using the current version of
kotlinx.serialization
in a multplatform project. The basic question is this: what is the best way to handle deserialization of Json when one of the field names (i.e. keys) is variable? I have put together a simple example to illustrate the problem:
Copy code
@Serializable data class UserData(val name: String)

@Serializable
data class User(
    val user_data: UserData, 
    val user_contributions: List<Contribution>
) {}

interface Action {
    val id: String
}

@Polymorphic interface Content

@Polymorphic interface Contribution : Action {
    val content: Content
}

@Serializable data class Mentor(
    override val id: String,
    
    @SerialName("mentorContent")
    override val content: MentorContent
) : Contribution {
    @Serializable data class MentorContent(val mentor: String, val mentee: String) : Content
}

@Serializable data class Judge(
    override val id: String,
    
    @SerialName("judgeContent")
    override val content: JudgeContent
) : Contribution {
    @Serializable data class JudgeContent(val judge: String, val judged: String) : Content
}

// Given this JSON:

{
    "user_data": {
        "name": "Justin"
    },
    "user_contributions": [
        {
            "id": "1",
            "mentorContent": {
                "mentor": "Lisa",
                "mentee": "Phil"
            }
        },
        {
            "id": "2",
            "judgeContent": {
                "judge": "Robert",
                "judged": "Karen"
            }
        }
    ]
}

// This is the desired deserialized object:

User(
    user_data = UserData(name = "Justin"),
    user_contributions = listOf(
        Mentor(
            id = "1",
            content = MentorContent(mentor = "Lisa", mentee = "Phil")
        ),
        Judge(
            id = "2",
            content = JudgeContent(judge = "Robert", judged = "Karen")
        )
    )
)
f
1. interfaces don't need
@polymorphic
afaik 2. Can the
content
of
user_contributions
be anything? or is it just a finite set of keys
If the former, you use a map, if the latter, what you did is pretty good. However, if the finite set is pretty big (like 5+) or likely to change in the future, you should consider using a map instead.
j
It’s a finite set of keys (each of which maps to a corresponding data class)
In my codebase, there are ~80 possible models (and keys) for that
content
property
@Fudge When you say use a map, do you mean a custom serializer?
f
No. But you might need a custom serializer for that
id
. With no
id
you could do
Copy code
user_contributions: Map<String,Map<String,String>>
I can take a look at this later
👍 1
j
Oh I see what you’re saying, but that’s not actually solving my problem. Just to be clear, I don’t want to serialize
user_contributions
into a generic map; it must to be serialized into a list of type
Contribution
(hence all the types I wrote out).
m
I solved this issue with a custom serializer but I'm not sure whether or not this is the only solution. I wrote a serializer for the common interface (mine was an abstract/sealed class). (in your case that would be
Contribution
) Downside: You need to repeat all the possible types.
j
@molikuner When you say you need to repeat all the types, you mean you need to switch on the key string to find a matching type?
m
Yes, something like that. I mean you could probably do that via reflection but I prefer(red) the
when
statement.