2016-05-31 8 views
2

Ich dekodiere einige XML, die nur String-Werte und Attribute enthält. Es enthält auch ein paar Instanzen von "&", was unglücklich ist, und ich möchte das nur zu "&" eher als "&" dekodieren. Ich werde auch etwas mehr mit diesen Zeichenfolgenwerten arbeiten, in denen ich das Zeichen "|" nie erscheinen lassen möchte, und deshalb möchte ich jede "|" Instanz durch "%7C" ersetzen.Benutzerdefinierte String-Übersetzung beim Dekodieren von XML in Golang

Ich konnte diese Änderungen tun strings.Replace nach der Decodierung verwendet wird, aber da die Decodierung bereits eine ähnliche Arbeit tun (schließlich ist es "&"-"&" übersetzen) Ich kann es zur gleichen Zeit tun möchten.

Die Dateien ich sind sehr groß werden, Parsen, also werde ich hier zu http://blog.davidsingleton.org/parsing-huge-xml-files-with-go/

ähnlich, etwas zu tun ist, ist ein kurzes Beispiel XML-Datei:

<?xml version="1.0" encoding="utf-8"?> 
<tests> 
    <test_content>X&amp;amp;Y is a dumb way to write XnY | also here's a pipe.</test_content> 
    <test_attr> 
     <test name="Normal" value="still normal" /> 
     <test name="X&amp;amp;Y" value="should be the same as X&amp;Y | XnY would have been easier." /> 
    </test_attr> 
</tests> 

Und einige Code Go, die Standard-Decodierung tut und druckt die Ergebnisse aus:

package main 

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

type XMLTests struct { 
    Content string  `xml:"test_content"` 
    Tests []*XMLTest `xml:"test_attr>test"` 
} 

type XMLTest struct { 
    Name string `xml:"name,attr"` 
    Value string `xml:"value,attr"` 
} 

func main() { 
    xmlFile, err := os.Open("test.xml") 
    if err != nil { 
     fmt.Println("Error opening file:", err) 
     return 
    } 
    defer xmlFile.Close() 

    var q XMLTests 

    decoder := xml.NewDecoder(xmlFile) 

    // I tried this to no avail: 
    // decoder.Entity = make(map[string]string) 
    // decoder.Entity["|"] = "%7C" 
    // decoder.Entity["&amp;amp;"] = "&" 

    var inElement string 
    for { 
     t, _ := decoder.Token() 
     if t == nil { 
      break 
     } 
     switch se := t.(type) { 
     case xml.StartElement: 
      inElement = se.Name.Local 
      if inElement == "tests" { 
       decoder.DecodeElement(&q, &se) 
      } 
     default: 
     } 
    } 

    fmt.Println(q.Content) 
    for _, t := range q.Tests { 
     fmt.Printf("\t%s\t\t%s\n", t.Name, t.Value) 
    } 
} 

Wie ändere ich diesen Code, um zu bekommen, was ich will? zB: Wie passt man den Decoder an?

Ich schaute auf die Dokumente, speziell https://golang.org/pkg/encoding/xml/#Decoder und versuchte, mit der Entity Map zu spielen, aber ich konnte keine Fortschritte machen.

Edit:

Basierend auf den Kommentaren, habe ich das Beispiel von Multiple-types decoder in golang gefolgt und hinzugefügt/geändert Folgendes obigen Code:

type string2 string 

type XMLTests struct { 
    Content string2 `xml:"test_content"` 
    Tests []*XMLTest `xml:"test_attr>test"` 
} 

type XMLTest struct { 
    Name string2 `xml:"name,attr"` 
    Value string2 `xml:"value,attr"` 
} 

func (s *string2) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 
    var content string 
    if err := d.DecodeElement(&content, &start); err != nil { 
     return err 
    } 
    content = strings.Replace(content, "|", "%7C", -1) 
    content = strings.Replace(content, "&amp;", "&", -1) 
    *s = string2(content) 
    return nil 
} 

, die für die test_content funktioniert aber nicht für die Attribute?

X&Y is a dumb way to write XnY %7C also here's a pipe. 
    Normal  still normal 
    X&amp;Y  should be the same as X&Y | XnY would have been easier. 
+2

Sie wollen tatsächlich etwas tun http://stackoverflow.com/questions/21164455/multiple-types- decoder-in-golang, wo Sie eine Implementierung von 'UnmarshalXML' bereitstellen, obwohl ich persönlich nicht denke, dass es besser ist, als nur eine Funktion wie' type.Sanatize() 'nach der Tat aufzurufen. Ich persönlich würde letzteres nehmen, weil es weniger verschleiert ist. Ich sehe benutzerdefinierte 'Unmarshal'-Implementierungen sehr ähnlich wie Operatorüberladung, mehr Verwirrung und Arbeit, als sie wert sind. – evanmcdonnal

+0

@evanmcdonnal Beide Optionen sind ziemlich unbefriedigend. Ich meine, der existierende Decoder ändert bereits "&" zu "&" zusammen mit anderen Standard-XML-Escapes, ist es wirklich so hart-codiert, dass ich nicht einfach dort mitkommen kann? Ich versuche nicht, die XML-Regeln wie in der anderen Frage zu brechen. –

+0

Ich meine, das ist es, was die Implementierung von 'UnmarshalXML' bewirkt ... Sie können einfach alles decodieren, String ersetzen und dann das normale' Unmarshal' aufrufen, es ist nicht so, als müssten Sie die harte Arbeit machen. Ich bin nicht besonders in die XML-Spezifikation eingeweiht, aber afaik '|' hat keine spezielle Bezeichnung, also warum würdest du erwarten, dass es so behandelt werden kann wie ein Escape-Zeichen? Ja, ich würde erwarten, dass diese Liste von Sonderzeichen hart codiert und nicht exportiert wird, warum sollte es nicht sein? – evanmcdonnal

Antwort

1

mit Attributen umgehen, können Sie die UnmarshalerAttr Schnittstelle mit der UnmarshalXMLAttr Methode verwenden können. Ihr Beispiel wird dann:

package main 

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

type string2 string 

type XMLTests struct { 
    Content string2 `xml:"test_content"` 
    Tests []*XMLTest `xml:"test_attr>test"` 
} 

type XMLTest struct { 
    Name string2 `xml:"name,attr"` 
    Value string2 `xml:"value,attr"` 
} 

func decode(s string) string2 { 
    s = strings.Replace(s, "|", "%7C", -1) 
    s = strings.Replace(s, "&amp;", "&", -1) 
    return string2(s) 
} 

func (s *string2) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 
    var content string 
    if err := d.DecodeElement(&content, &start); err != nil { 
     return err 
    } 
    *s = decode(content) 
    return nil 
} 

func (s *string2) UnmarshalXMLAttr(attr xml.Attr) error { 
    *s = decode(attr.Value) 
    return nil 
} 

func main() { 
    xmlData := `<?xml version="1.0" encoding="utf-8"?> 
<tests> 
    <test_content>X&amp;amp;Y is a dumb way to write XnY | also here's a pipe.</test_content> 
    <test_attr> 
     <test name="Normal" value="still normal" /> 
     <test name="X&amp;amp;Y" value="should be the same as X&amp;Y | XnY would have been easier." /> 
    </test_attr> 
</tests>` 
    xmlFile := strings.NewReader(xmlData) 

    var q XMLTests 

    decoder := xml.NewDecoder(xmlFile) 
    decoder.Decode(&q) 

    fmt.Println(q.Content) 
    for _, t := range q.Tests { 
     fmt.Printf("\t%s\t\t%s\n", t.Name, t.Value) 
    } 
} 

Ausgang:

X&Y is a dumb way to write XnY %7C also here's a pipe. 
    Normal  still normal 
    X&Y  should be the same as X&Y %7C XnY would have been easier. 

(du in der Go playground testen können.)

Also überall, wenn mit string2 für Sie geeignet ist, sollte dies den Trick .

(bearbeiten: einfacher Code, ohne DecodeElement und eine Art Schalter ...)