2016-03-29 6 views
0

Beim Parsen von XML mit Go, wie kann ich das Attribut eines verschachtelten Elements direkt in meine Struktur lesen?Das Attribut des Unterelements direkt in Go struct analysieren

Meine XML sieht wie folgt aus. Es ist Teil des OpenStreetMap-Format:

<way id="123" > 
     <nd ref="101"/> 
     <!-- Lots of nd elements repeated --> 
     <nd ref="109"/> 
</way> 

Ich habe

type Way struct { 
    Nodes []NodeRef `xml:"nd"` 
} 

mit

type NodeRef struct { 
    Ref int `xml:"ref,attr"` 
} 

aber ich möchte in der Lage sein

type Way struct { 
    Nodes []int `???` 
} 

direkt zu tun.

Die Dokumentation auf Unmarshalling hat mir nicht geholfen. Ich habe versucht, mit xml:"nd>ref,attr", aber das scheitert mit "Kette nicht gültig mit Attr-Flag".

Bitte beachten Sie den folgenden Beispielcode. Run the code in Go Playground

package main 

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

func main() { 
    data := ` 
     <way id="5090250" > 
     <nd ref="822403"/> 
     <nd ref="21533912"/> 
     <nd ref="821601"/> 
     <nd ref="21533910"/> 
     <nd ref="135791608"/> 
     <nd ref="333725784"/> 
     <nd ref="333725781"/> 
     <nd ref="333725774"/> 
     <nd ref="333725776"/> 
     <nd ref="823771"/> 
     </way> 
    ` 

    r := strings.NewReader(data) 
    way, err := ReadWay(r) 
    if err != nil { 
     fmt.Println("Could not read", err) 
     os.Exit(1) 
    } 

    fmt.Println(way) 
} 

// I'd like to get rid of this nested struct. 
type NodeRef struct { 
    Ref int `xml:"ref,attr"` 
} 

type Way struct { 
    ID int `xml:"id,attr"` 
    // How can I write all <nd ref="123"/> directly into Nodes []int? 
    Nodes []NodeRef `xml:"nd"` 
} 

func ReadWay(reader io.Reader) (Way, error) { 
    var way Way 
    if err := xml.NewDecoder(reader).Decode(&way); err != nil { 
     return way, err // Why can't I return nil, err? 
    } 
    return way, nil 
} 

Antwort

3

Kurz gesagt, Sie können das nicht direkt tun. Das übliche Muster zur Umgehung der XML-Struktur besteht darin, die Schnittstelle xml.Unmarshaler zu implementieren. Hier ein Beispiel:

type Way struct { 
    ID int 
    Nodes []int 
} 

func (w *Way) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 
    var payload struct { 
     ID int `xml:"id,attr"` 
     Nodes []struct { 
      Ref int `xml:"ref,attr"` 
     } `xml:"nd"` 
    } 

    err := d.DecodeElement(&payload, &start) 
    if err != nil { 
     return err 
    } 

    w.ID = payload.ID 
    w.Nodes = make([]int, 0, len(payload.Nodes)) 

    for _, node := range payload.Nodes { 
     w.Nodes = append(w.Nodes, node.Ref) 
    } 

    return nil 
} 

Hier wird das payload Variable wird nach dem Vorbild der XML-Daten, während die Way Struktur modelliert wird, wie Sie es wollen. Sobald die Nutzlast decodiert wird, können Sie es verwenden, um die Way Variable zu initialisieren, wie Sie wollen ...

Randbemerkung:// Why can't I return nil, err?

Sie nicht nil zurückkehren können, weil Way nicht entweder ein Zeiger oder eine Schnittstelle , also nil ist kein gültiger Wert dafür.

+0

Für zukünftige Referenz die Dokumentation zu diesem ist hier: https://golang.org/pkg/encoding/xml/#Unmarshaler – Unapiedra