Hector
Hector

Reputation: 5458

How to deserialize dynamic Json with pure Kotlin Serialization

I am investigating the use of Kotlin Serialization in my latest project

The Json I have to contend with is as follows:-

{
    "countryCodeMapping": {
        "ssd": [
            {
                "name": "South Sudan",
                "alpha2": "ss",
                "alpha3": "ssd"
            }
        ],
        "nic": [
            {
                "name": "Nicaragua",
                "alpha2": "ni",
                "alpha3": "nic"
            }
        ],
        "fin": [
            {
                "name": "Finland",
                "alpha2": "fi",
                "alpha3": "fin"
            }
        ],
        "cck": [
            {
                "name": "Cocos (Keeling) Islands",
                "alpha2": "cc",
                "alpha3": "cck"
            }
        ],
        "tun": [
            {
                "name": "Tunisia",
                "alpha2": "tn",
                "alpha3": "tun"
            }
        ],
        "uga": [
            {
                "name": "Uganda",
                "alpha2": "ug",
                "alpha3": "uga"
            }
        ],
....
        "myDescriptions": {
        "1": {
            "groupId": 1,
            "description": "cvbxcvbxcvbxbxb",
            "myTypes": [
                "cxcvbxb",
                "xcvxcbxxx",
                "cvbxcvbxcxvcb"
            ]
        },
        "2": {
            "groupId": 2,
            "description": "vxvbxbxbxbx",
            "myTypes": [
                "xcvb",
                "xcvbxcvb"
            ]
        },
        "3": {
            "groupId": 3,
            "description": "xcvbxcvbxcvbxcvb",
            "myTypes": [
                "xcvbxcvbxcvbx"
            ]
        },
        "4": {
            "groupId": 4,
            "description": "xcvbxcbxcvbxcb",
            "myTypes": [
                "xcvbxcxcvb"
            ]
        },
        "5": {
            "groupId": 5,
            "description": "xcvbxcvbxcvbxcvbx",
            "myTypes": [
                "xcvbxcvbxc",
                "xcvbxcvbxcvbxb"
            ]
        },
        "6": {
            "groupId": 6,
            "description": "dfgsdfgsdgsdfgsdgsg",
            "myTypes": [
        

I must decode the entire Json string, however I started with the countryCodeMapping

section as follows:-

@Serializable
data class Meta(

    @SerialName("countryCodeMapping")
    val mapping: CountryCodeMapping
)

@Serializable
data class CountryCodeMapping(val ssd: List<Country>)

@Serializable
data class Country(

    @SerialName("name")
    val name: String,

    @SerialName("alpha2")
    val alpha2: String,

    @SerialName("alpha3")
    val alpha3: String
)


val countryMap: Meta = Json {
    isLenient = true
    ignoreUnknownKeys = true
    allowStructuredMapKeys = true
}.decodeFromString<Meta>(metaJson)

This works, however I obviously only decode Sudan

countryMap = Meta(mapping=CountryCodeMapping(ssd=[Country(name=South Sudan, alpha2=ss, alpha3=ssd)]))

What data class models do I require to enable me to achieve my goal?

UPDATE

The changes I had to make (Based on answer comment) were

@Serializable
data class Meta(

    @SerialName("countryCodeMapping")
    val mapping: Map<String, List<Country>>
)

@Serializable
data class Country(

    @SerialName("name")
    val name: String? = null,

    @SerialName("alpha2")
    val alpha2: String? = null,

    @SerialName("alpha3")
    val alpha3: String? = null
)

Upvotes: 3

Views: 3258

Answers (2)

Joffrey
Joffrey

Reputation: 37879

When you have JSON objects like { "someKey": ..., "otherKey": ... }, you have mainly 2 options to represent them:

  1. use a regular class with properties
  2. use a generic Map<String, ...>

You current approach uses option 1 (with CountryCodeMapping class, and hardcoded ssd property). This approach is best when the keys are fixed and well known.

Approach 2 is better when the keys are dynamic and all the values have the same type, which is your case here.

To implement approach 2, use what @broot suggested: a Map<String, List<Country>> instead of the CountryCodeMapping class:

@Serializable
data class Meta(

    @SerialName("countryCodeMapping")
    val mapping: Map<String, List<Country>>
)

@Serializable
data class Country(

    @SerialName("name")
    val name: String,

    @SerialName("alpha2")
    val alpha2: String,

    @SerialName("alpha3")
    val alpha3: String
)

Upvotes: 2

broot
broot

Reputation: 28470

You can just use a regular map here:

val mapping: Map<String, List<Country>>

Upvotes: 4

Related Questions