2

How can I use XML attributes as struct field?

This is my test: Each row correspond to a Person

package main

import (
    "encoding/xml"
    "fmt"
)

var xmlstr = `<data>
    <row>
        <col name='firstname'>John</col>
        <col name='age'>2</col>
    </row>
    <row>
        <col name='firstname'>3</col>
        <col name='age'>4</col>
    </row>
</data>`

type Data struct {
    XMLName xml.Name `xml:"data"`
    Person  []Person `xml:"row"`
}

type Person struct {
    PersonField []PersonField `xml:"col"`
}

type PersonField struct {
    Name  string `xml:"name,attr"`
    Value string `xml:",chardata"`
}

func main() {
    b := []byte(xmlstr)

    var d Data
    xml.Unmarshal(b, &d)

    for _, person := range d.Person {
        fmt.Println(person)
    }
}

I go a slice of 2 struct:

{[{firstname John} {age 2}]}
{[{firstname 3} {age 4}]}

How can I get this struct instead ? Where xml attributes is use as a struct field name?

type Person struct {
    Firstname string
    Age       int
}
1
  • What on earth do you mean by "How can I get this struct instead?" Commented Jul 13, 2016 at 20:21

1 Answer 1

3

You can define a custom unmarshaler for the <row> elements that does this "unpacking" for you:

package main

import (
    "encoding/xml"
    "fmt"
    "strconv"
)

var xmlstr = `<data>
    <row>
        <col name='firstname'>John</col>
        <col name='age'>2</col>
    </row>
    <row>
        <col name='firstname'>3</col>
        <col name='age'>4</col>
    </row>
</data>`

type Data struct {
    XMLName xml.Name `xml:"data"`
    Person  []Person `xml:"row"`
}

type Person struct {
    Firstname string
    Age       int
}

func (p *Person) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    x := struct {
        Col []struct {
            Name  string `xml:"name,attr"`
            Value string `xml:",chardata"`
        } `xml:"col"`
    }{}
    err := d.DecodeElement(&x, &start)
    if err != nil {
        return err
    }
    for _, col := range x.Col {
        switch col.Name {
        case "firstname":
            p.Firstname = col.Value
        case "age":
            p.Age, err = strconv.Atoi(col.Value)
            if err != nil {
                return err
            }
        }
    }
    return nil
}

func main() {
    b := []byte(xmlstr)

    var d Data
    if err := xml.Unmarshal(b, &d); err != nil {
        panic(err)
    }

    for _, person := range d.Person {
        fmt.Println(person)
    }
}

https://play.golang.org/p/DRF5axeBc0

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.