在Golang中解码XML时的自定义字符串翻译
问题描述:
我正在解码一些只包含字符串值和属性的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;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;Y" value="should be the same as X&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;"] = "&"
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, "&", "&", -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&Y should be the same as X&Y | XnY would have been easier.
答
为了应对属性,可以使用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, "&", "&", -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;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;Y" value="should be the same as X&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
和类型开关...)
你真的想这样做http://stackoverflow.com/questions/21164455/multiple-types-在golang中提供了一个'UnmarshalXML'的实现,尽管我个人认为它比在事实之后调用类似'type.Sanatize()'的函数更好。我个人会采取后者,因为它没有混淆。我看到自定义的'Unmarshal'实现很像操作符重载,更多的混淆和工作,而不是他们的价值。 – evanmcdonnal
@evanmcdonnal两个选项都相当不满意。我的意思是现有的解码器已经将“&”与其他标准xml转义一起更改为“&”,是否真的如此硬编码以至于我不能在此处进行标记?我没有试图像其他问题那样真正地破坏XML规则。 –
我的意思就是实现'UnmarshalXML'的功能......你可以解码所有东西,运行字符串替换,然后调用常规的'Unmarshal',这不像你必须做任何艰苦的工作。我对xml的规范不是很熟悉,但afaik'|'没有特殊的名称,那么为什么你希望能够像转义字符那样对待它呢?是的,我希望特殊字符的列表能够被硬编码并且不被导出,为什么它不是? – evanmcdonnal