Search code examples
javaxml-parsingjaxbrss

JAXB retrieve inner element with namespace (returns blank)


I want to unmarshal

<item>
    <a10:author>
        <a10:name>AuthorName</a10:name>
    </a10:author>
</item>

to

data class Item(val author:String)

if possible, by reading author/name into author field directly. If not possible in clean/simple way, i want to create Author class with name field, as below. In below code, author.name is empty, but it should not be. Why is it empty?

Sample XML


<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
    <channel xmlns:os="http://a9.com/-/spec/opensearch/1.1/">
        <title>SomeTitle</title>
        <link>https://google.com</link>
        <description>SomeDescription</description>
        <os:totalResults>49</os:totalResults>
        <item>
            <link>https://google.com</link>
            <a10:author>
                <a10:name>AuthorName</a10:name>
            </a10:author>
            <title>SomeTitle</title>
            <description>SomeDesc</description>
            <pubDate>Fri, 06 Nov 2020 07:59:27 Z</pubDate>
        </item>
    </channel>
</rss>

All model classes:

@XmlRootElement(name = "rss", namespace = "http://www.w3.org/2005/Atom")
@XmlAccessorType(XmlAccessType.FIELD)
data class Rss(val channel: RssChannel?) {

    constructor() : this(channel = null)
}

@XmlRootElement(name = "channel", namespace = "http://a9.com/-/spec/opensearch/1.1/")
@XmlAccessorType(XmlAccessType.FIELD)
data class RssChannel  (val link: String,
                        val title: String,
                        val description: String,
                        @field:XmlElement(namespace = "http://a9.com/-/spec/opensearch/1.1/")
                        val totalResults: Int,
                        @field:XmlElement(name = "item")
                        val items: List<Item>?) {

    constructor() : this(link = "", title = "", description = "", totalResults = 0, items = null)
}

@XmlAccessorType(XmlAccessType.FIELD)
data class Item( val title: String,
                 val description: String,
                 val link: String,
                 @field:XmlElement(namespace = "http://www.w3.org/2005/Atom")
                 val author: Author,
                 val pubDate: String) {

    constructor() : this(title = "", description = "", link = "", author = Author(), pubDate = "")
}

@XmlAccessorType(XmlAccessType.FIELD)
data class Author(@XmlElement(namespace = "http://www.w3.org/2005/Atom")
                  val name: String) {

    constructor() : this(name = "")
}


Solution

  • 1) Error was @field:XmlElement instead of @XmlElement

    This is kotlin property/field distinction error.

    From

    @XmlAccessorType(XmlAccessType.FIELD)
    data class Author(@XmlElement(namespace = "http://www.w3.org/2005/Atom")
                      val name: String)
    

    To

    @XmlAccessorType(XmlAccessType.FIELD)
    data class Author(@field:XmlElement(namespace = "http://www.w3.org/2005/Atom")
                      val name: String)
    

    2) Another solution using @XmlAnyElement (extra complexity)

    data class Author(@field:XmlAnyElement(lax = true)
                      val elements: MutableList<JAXBElement<String>>)
    ...
    
    @XmlRegistry
    class NameStringFactory {
        @XmlElementDecl(name = "name", namespace = "http://www.w3.org/2005/Atom")
        fun createName(name: String): JAXBElement<String> {
            return JAXBElement<String>(QName("name"), String::class.java, name)
        }
    }
    
    ...
    
    val jaxbContext = JAXBContext.newInstance(NameStringFactory::class.java,...)
    

    But i still don't know if removing Author class and using data class Item(val author:String) instead is possible