How to differentiate same name siblings in xml using golang?

Viewed 41

I have the following xml:

<?xml version='1.0' encoding='UTF-8'?>
<actions>
  <foo>text</foo>
  <foo1>text</foo1>
  <foo2>text</foo2>
  <bar>text</bar>
  <foo>text</foo>
  <foo1>text</foo1>
  <foo2>text</foo2>
</actions>

Here i want to parse the xml so i can have batches of foo.

Something like this:

[
{foo: text, foo1: text, foo2: text}
{foo: text, foo1: text, foo2: text}
]

But not sure how to make it like so.

If i us below code i get output value in a different way which i don't want.

package main

import (
    "encoding/xml"
    "fmt"
    "io/ioutil"
    "os"
)

type Foo struct {
    Foo string
    Foo1 string
    Foo2 string
}

type MyXml struct {
    XMLName xml.Name `xml:"actions"`
    Foo []string `xml:"foo"`
}

func main() {
    // Open our xmlFile
    xmlFile, err := os.Open("./z.xml")
    // if we os.Open returns an error then handle it
    if err != nil {
        fmt.Println("err", err)
    }

    fmt.Println("Successfully Opened z.xml")
    // defer the closing of our xmlFile so that we can parse it later on
    defer xmlFile.Close()

    // read our opened xmlFile as a byte array.
    byteValue, _ := ioutil.ReadAll(xmlFile)

    var data MyXml

    z := xml.Unmarshal(byteValue, &data)

    if z != nil {
        panic(z)
    }

    fmt.Println(data)
}

Output:

{{ actions} [text text]}

Is there any way by which its possible to change the above code a little so the output will be like this:

[
{foo: text, foo1: text, foo2: text}
{foo: text, foo1: text, foo2: text}
]
1 Answers

Given the relatively simple XML structure, you can use xml.Decoder to parse it in streaming mode:

func main() {
    // Open our xmlFile ...
    xmlFile := . . .
    decoder := xml.NewDecoder(xmlFile)

    var data MyXml
    inActions := false
    for {
        t, _ := decoder.Token()
        if t == nil {
            break
        }
        switch se := t.(type) {
        case xml.StartElement:
            if inActions {
                switch se.Name.Local {
                case "foo":
                    decoder.DecodeElement(&data.getFoo(true).Foo, &se)
                case "foo1":
                    decoder.DecodeElement(&data.getFoo(false).Foo1, &se)
                case "foo2":
                    decoder.DecodeElement(&data.getFoo(false).Foo2, &se)
                }
            } else if se.Name.Local == "actions" {
                inActions = true
            }
        case xml.EndElement:
            if se.Name.Local == "actions" {
                inActions = false
            }
        }
    }

    fmt.Printf("%+v\n", data.Actions)
}

type Foo struct {
    Foo  string
    Foo1 string
    Foo2 string
}

type MyXml struct {
    Actions []Foo
}

func (d *MyXml) getFoo(new bool) *Foo {
    if new || len(d.Actions) == 0 {
        d.Actions = append(d.Actions, Foo{})
    }
    return &d.Actions[len(d.Actions)-1]
}

Would print: (live demo)

[{Foo:text Foo1:text Foo2:text} {Foo:text Foo1:text Foo2:text}]
Related