Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use a sealed class hierarchy with differing root elements? #98

Closed
dduits opened this issue Sep 10, 2022 · 3 comments
Closed

How to use a sealed class hierarchy with differing root elements? #98

dduits opened this issue Sep 10, 2022 · 3 comments
Labels
bug Something isn't working enhancement New feature or request indev The issue is fixed/implemented in the dev branch

Comments

@dduits
Copy link

dduits commented Sep 10, 2022

How to use a sealed class hierarchy with differing root elements?

I'm writing a HTTP client for a web service that returns a different XML based
on whether records are found or not. I've described a simplified version of my
use-case below.

When records are found the API returns a cars root element with car
elements inside it:

<?xml version="1.0" encoding="UTF-8"?>
<cars context="main">
    <car color="red" />
    <car color="blue" />
    <car color="green" />
</cars>

When no records are found it returns a single car root element instead:

<?xml version="1.0" encoding="UTF-8"?>
<car context="main" />

I've tried the following approach using xmlutil:

@Serializable
sealed class Base {

    abstract val context: String

    @Serializable
    @SerialName("car")
    data class NoResultsFound(override val context: String) : Base()

    @Serializable
    @SerialName("cars")
    data class CarsFound(
        override val context: String,
        @XmlElement(true) val cars: List<Car>
    ) : Base() {

        @Serializable
        @SerialName("car")
        data class Car(val color: String)

    }

}

val xml = XML()

val carsFoundXml = """
    <cars context="main">
        <car color="red" />
        <car color="blue" />
        <car color="green" />
    </cars>""".trimIndent()

val noCarsFoundXml = """<car context="main" />"""

// Works
val carsFound = xml.decodeFromString(Base.CarsFound.serializer(), carsFoundXml)
println(carsFound)

// Works
val noCarsFound = xml.decodeFromString(Base.NoResultsFound.serializer(), noCarsFoundXml)
println(noCarsFound)

// Fails with UnknownXmlFieldException
val carsFoundFromBase = xml.decodeFromString(Base.serializer(), carsFoundXml)
println(carsFoundFromBase)

// Fails with UnknownXmlFieldException
val noCarsFoundFromBase = xml.decodeFromString(Base.serializer(), noCarsFoundXml)
println(noCarsFoundFromBase)

Program output:

CarsFound(context=main, cars=[Car(color=red), Car(color=blue), Car(color=green)])
NoResultsFound(context=main)
Exception in thread "main" nl.adaptivity.xmlutil.serialization.UnknownXmlFieldException: Could not find a field for name Base/context
  candidates: type, value at position Line number = 1
Column number = 22
System Id = null
Public Id = null
Location Uri= null
CharacterOffset = 21

When working with JSON I would use JsonContentPolymorphicSerializer to select
the serializer based on what keys are found in the object. Though for obvious
reasons not an option here. I've went through at the documentation, examples
and issues but I'm unable to figure out how to approach this issue.

Any suggestions how I could approach serializing above XML into a sealed class
using xmlutil?

@pdvrieze pdvrieze added bug Something isn't working enhancement New feature or request labels Sep 14, 2022
@pdvrieze
Copy link
Owner

This is a limitation of the implementation at the moment. Basically it doesn't handle transparently polymorphic root elements. I'll look at a proper solution, but a workaround would be to have a wrapper element that "holds" the actual content. At that point it should work correctly (perhaps with XML { autoPolymorphic = true })

@pdvrieze pdvrieze added the indev The issue is fixed/implemented in the dev branch label Sep 14, 2022
@pdvrieze
Copy link
Owner

I have added the fix/feature to the dev branch (so wrappers wouldn't be needed anymore).

@pdvrieze
Copy link
Owner

The new release should have fixed this (with the limitation that the root type's annotations are not accessible - this is a limitation of the plugin)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request indev The issue is fixed/implemented in the dev branch
Projects
None yet
Development

No branches or pull requests

2 participants