Search code examples
xmlgounmarshalling

How do I unmarshal nested XML elements into an array?


My XML contains an array of predefined elements, but I can't pick up the array. Here is the XML structure:

var xml_data = `<Parent>
                   <Val>Hello</Val>
                   <Children>
                      <Child><Val>Hello</Val></Child>
                      <Child><Val>Hello</Val></Child>
                      <Child><Val>Hello</Val></Child>
                   </Children>
                </Parent>`

Here is the full code and here is the playground link. Running this will pick up Parent.Val, but not Parent.Children.

package main

import (
    "fmt"
    "encoding/xml"
)

func main() {

    container := Parent{}
    err := xml.Unmarshal([]byte(xml_data), &container)

    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(container)  
    }
}

var xml_data = `<Parent>
                   <Val>Hello</Val>
                   <Children>
                      <Child><Val>Hello</Val></Child>
                      <Child><Val>Hello</Val></Child>
                      <Child><Val>Hello</Val></Child>
                   </Children>
                </Parent>`

type Parent struct {
    Val string
    Children []Child
}

type Child struct {
    Val string
}

EDIT: I simplified the problem a bit here. Basically I can't unmarshal any array, not just of predefined structure. Below is the updated work code. In that example, only one item ends up in the container interface.

func main() {

    container := []Child{}
    err := xml.Unmarshal([]byte(xml_data), &container)

    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(container)  
    }

    /* 
        ONLY ONE CHILD ITEM GETS PICKED UP
    */
}

var xml_data = `
            <Child><Val>Hello</Val></Child>
            <Child><Val>Hello</Val></Child>
            <Child><Val>Hello</Val></Child>
        `

type Child struct {
    Val string
}

Solution

  • For this kind of nesting, you'll need also a struct for Children element:

    package main
    
    import (
        "fmt"
        "encoding/xml"
    )
    
    func main() {
    
        container := Parent{}
        err := xml.Unmarshal([]byte(xml_data), &container)
    
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println(container)  
        }
    }
    
    var xml_data = `<Parent>
                <Val>Hello</Val>
                <Children>
                    <Child><Val>Hello</Val></Child>
                    <Child><Val>Hello</Val></Child>
                    <Child><Val>Hello</Val></Child>
                </Children>
            </Parent>`
    
    type Parent struct {
        Val string
        Children Children
    }
    
    type Children struct {
        Child []Child
    }
    
    type Child struct {
        Val string
    }
    

    Also pasted here: Go Playground

    Note that your code would work (after changing variable name from Children to Child) with this kind of XML structure:

    <Parent>
        <Val>Hello</Val>
        <Child><Val>Hello</Val></Child>
        <Child><Val>Hello</Val></Child>
        <Child><Val>Hello</Val></Child>
    </Parent>