2016-05-31 45 views
2

我正在解碼一些只包含字符串值和屬性的XML。它還包含一些"&"的實例,這是不幸的,我想將其解碼爲"&"而不是"&"。我還將對這些字符串值做一些更多的工作,其中我需要字符"|"永不出現,所以我想用"%7C"替換任何"|"實例。在Golang中解碼XML時的自定義字符串翻譯

我能做的解碼後用strings.Replace這些變化,但由於解碼已經在做類似的工作(畢竟它翻譯"&""&")我想在同一時間做這件事。

我會被解析的文件是巨大的,所以我會做類似http://blog.davidsingleton.org/parsing-huge-xml-files-with-go/

下面是一個簡單的例子xml文件的內容:

<?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> 

有些Go代碼,做標準的解碼並打印出結果:

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) 
    } 
} 

如何修改此代碼以獲得我想要的內容?即:如何定製解碼器?

我查看了文檔,特別是https://golang.org/pkg/encoding/xml/#Decoder,並嘗試使用Entity貼圖,但我無法取得任何進展。

編輯:

基礎上的評論,我已經按照從Multiple-types decoder in golang的例子,並添加/更改以下上面的代碼:

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 
} 

,對於test_content但不工作爲屬性?

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

你真的想這樣做http://stackoverflow.com/questions/21164455/multiple-types-在golang中提供了一個'UnmarshalXML'的實現,儘管我個人認爲它比在事實之後調用類似'type.Sanatize()'的函數更好。我個人會採取後者,因爲它沒有混淆。我看到自定義的'Unmarshal'實現很像操作符重載,更多的混淆和工作,而不是他們的價值。 – evanmcdonnal

+0

@evanmcdonnal兩個選項都相當不滿意。我的意思是現有的解碼器已經將「&」與其他標準xml轉義一起更改爲「&」,是否真的如此硬編碼以至於我不能在此處進行標記?我沒有試圖像其他問題那樣真正地破壞XML規則。 –

+0

我的意思就是實現'UnmarshalXML'的功能......你可以解碼所有東西,運行字符串替換,然後調用常規的'Unmarshal',這不像你必須做任何艱苦的工作。我對xml的規範不是很熟悉,但afaik'|'沒有特殊的名稱,那麼爲什麼你希望能夠像轉義字符那樣對待它呢?是的,我希望特殊字符的列表能夠被硬編碼並且不被導出,爲什麼它不是? – evanmcdonnal

回答

1

爲了應對屬性,可以使用UnmarshalerAttr接口與UnmarshalXMLAttr方法。你舉的例子就變成了:

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) 
    } 
} 

輸出:

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. 

(您可以在Go playground進行測試。)

因此,如果使用string2到處是適合你的,這應該做的伎倆。

編輯:簡單的代碼,而無需使用DecodeElement和類型開關...)