LearnGo
LearnGo

Reputation: 105

Unable to parse xml in GO with : in tags

I find that if tags in XML file have : in them the unmarshal code in Go does not seem to work. Any insights ?

For example, in the XML file below, Summary works but not Cevent.

<summary>...AIR QUALITY ALERT </summary>
<cap:event>Air Quality Alert</cap:event>
type Entry struct{
    Summary    string   `xml:"summary"`
    Cevent     string   `xml:"cap:event"`
}

Upvotes: 6

Views: 1834

Answers (3)

Mr_Pink
Mr_Pink

Reputation: 109388

cap is the namespace identifier, not part of the tag name. Here it is shorthand for urn:oasis:names:tc:emergency:cap:1.1

(This answer looks like it may have a good condensed explanation of namespaces: What does "xmlns" in XML mean?)

The Go "encoding/xml" package does not handle namespaces well, but if there are no conflicting tags, you can elide the namespace altogether

type Entry struct {
    Summary string `xml:"summary"`
    Event   string `xml:"event"`
}

The proper way to specify event, especially in the case of identical tags in different namespaces, would be with the full namespace like:

type Entry struct {
    Summary string `xml:"summary"`
    Event   string `xml:"urn:oasis:names:tc:emergency:cap:1.1 event"`
}

Here's a working example: https://play.golang.org/p/ry55F2pWKY

Upvotes: 8

helmbert
helmbert

Reputation: 38004

cap is not part of the tag name, but a namespace identifier (short for urn:oasis:names:tc:emergency:cap:1.1, as you've provided in comments). This is the correct notation:

type Entry struct{
    Summary    string   `xml:"summary"`
    Cevent     string   `xml:"urn:oasis:names:tc:emergency:cap:1.1:cap event"`
}

Mind the space instead of : to denote the namespace. Also note that just using the namespace identifier (like xml:"cap event") does not work.

Working example (https://play.golang.org/p/rjkb2esGgv):

package main

import "fmt"
import "encoding/xml"

type Entry struct{
    Summary    string   `xml:"summary"`
    Cevent     string   `xml:"urn:oasis:names:tc:emergency:cap:1.1:cap event"`
}

func main() {
    xmlString := []byte(`
        <doc xmlns:cap = 'urn:oasis:names:tc:emergency:cap:1.1'>
            <summary>...AIR QUALITY ALERT </summary>
            <cap:event>Air Quality Alert</cap:event>
        </doc>
    `)
    entry := new(Entry)

    if err := xml.Unmarshal(xmlString, entry); err == nil {
        fmt.Println(entry)
    } 
}

Upvotes: 4

evanmcdonnal
evanmcdonnal

Reputation: 48096

You just need to escape the colon. So change your xml tag to xml:"cap\:event" and it will work as you expect.

type Entry struct{
    Summary    string   `xml:"summary"`
    Cevent     string   `xml:"cap\:event"`
}

Tested this on the xml page using the unmarshal example with minor modification;

package main

import (
    "encoding/xml"
    "fmt"
)

func main() {
    type Email struct {
        Where string `xml:"where,attr"`
        Addr  string
    }
    type Address struct {
        City, State string
    }
    type Result struct {
        XMLName xml.Name `xml:"Person"`
        Name    string   `xml:"Full\:Name"`
        Phone   string
        Email   []Email
        Groups  []string `xml:"Group>Value"`
        Address
    }
    v := Result{Name: "none", Phone: "none"}

    data := `
        <Person>
            <Full:Name>Grace R. Emlin</Full:Name>
            <Company>Example Inc.</Company>
            <Email where="home">
                <Addr>[email protected]</Addr>
            </Email>
            <Email where='work'>
                <Addr>[email protected]</Addr>
            </Email>
            <Group>
                <Value>Friends</Value>
                <Value>Squash</Value>
            </Group>
            <City>Hanga Roa</City>
            <State>Easter Island</State>
        </Person>
    `
    err := xml.Unmarshal([]byte(data), &v)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }
    fmt.Printf("XMLName: %#v\n", v.XMLName)
    fmt.Printf("Name: %q\n", v.Name)
    fmt.Printf("Phone: %q\n", v.Phone)
    fmt.Printf("Email: %v\n", v.Email)
    fmt.Printf("Groups: %v\n", v.Groups)
    fmt.Printf("Address: %v\n", v.Address)
}

Remove the escape and it will print "none" for Name. Use a space instead of : or \: and it will also work. A space in the xml will cause a parse error because it's invalid apparently.

Upvotes: 0

Related Questions