Go语言xml.Unmarshal解析空字符实体报错,如何处理?
问题描述
我需要验证系统是否会被空字符(\0,URL编码为%00)干扰,这是集成测试的一部分。系统返回了包含�实体的XML错误,但我的Go测试代码解析该XML时失败。
我认为以下XML是有效的:
<Error> <Code>1234</Code> <Message>Test�afterzero</Message> </Error>
但以下Go代码解析时会报错,无法得到包含\0的字符串:
package main import ( "encoding/xml" "fmt" ) type ErrorMsg struct { XMLName xml.Name `xml:"Error"` Code string `xml:"Code"` Message string `xml:"Message"` } func main() { var data = []byte(` <Error> <Code>1234</Code> <Message>Test�afterzero</Message> </Error> `) b := ErrorMsg{} o := xml.Unmarshal(data, &b) fmt.Println(o) fmt.Println(b) }
我想保留这个测试,有没有办法让XML解析器接受这个空字符实体?
解决方案
解析失败的原因
XML 1.0规范明确禁止U+0000(空字符)出现在文档中,Go标准库的encoding/xml包严格遵循这一规范,因此遇到�实体时会直接抛出错误。
方法1:预处理XML内容(简单直接)
在解析前将XML中的�替换为合法占位符,解析完成后再替换回空字符:
package main import ( "encoding/xml" "fmt" "strings" ) type ErrorMsg struct { XMLName xml.Name `xml:"Error"` Code string `xml:"Code"` Message string `xml:"Message"` } func main() { rawData := []byte(` <Error> <Code>1234</Code> <Message>Test�afterzero</Message> </Error> `) // 替换�为临时占位符 processedData := strings.ReplaceAll(string(rawData), "�", "__NULL__") b := ErrorMsg{} err := xml.Unmarshal([]byte(processedData), &b) if err != nil { fmt.Println(err) return } // 解析完成后替换回空字符 b.Message = strings.ReplaceAll(b.Message, "__NULL__", "\x00") fmt.Printf("解析结果:%+v\n", b) fmt.Printf("Message内容:%q\n", b.Message) }
方法2:自定义XML解码器(灵活适配)
使用xml.Decoder开启非严格模式,并自定义实体映射来支持�:
package main import ( "bytes" "encoding/xml" "fmt" ) type ErrorMsg struct { XMLName xml.Name `xml:"Error"` Code string `xml:"Code"` Message string `xml:"Message"` } func main() { rawData := []byte(` <Error> <Code>1234</Code> <Message>Test�afterzero</Message> </Error> `) dec := xml.NewDecoder(bytes.NewReader(rawData)) // 关闭严格模式,允许部分非规范XML内容 dec.Strict = false // 自定义实体映射,将�映射为空字符 dec.Entity = map[string]string{"0": "\x00"} var b ErrorMsg err := dec.Decode(&b) if err != nil { fmt.Println(err) return } fmt.Printf("解析结果:%+v\n", b) fmt.Printf("Message内容:%q\n", b.Message) }
注意:非严格模式会放宽XML规范检查,可能忽略其他潜在的格式错误,仅建议在测试场景使用。
方法3:手动处理Token流(底层控制)
如果需要更精细的控制,可以遍历XML Token流,手动解析�实体:
package main import ( "bytes" "encoding/xml" "fmt" "io" "strings" ) type ErrorMsg struct { XMLName xml.Name `xml:"Error"` Code string `xml:"Code"` Message string `xml:"Message"` } func main() { rawData := []byte(` <Error> <Code>1234</Code> <Message>Test�afterzero</Message> </Error> `) dec := xml.NewDecoder(bytes.NewReader(rawData)) var errMsg ErrorMsg var currentField string for { token, err := dec.Token() if err == io.EOF { break } if err != nil { fmt.Println(err) return } switch t := token.(type) { case xml.StartElement: currentField = t.Name.Local case xml.CharData: content := strings.TrimSpace(string(t)) // 手动替换空字符实体 content = strings.ReplaceAll(content, "�", "\x00") content = strings.ReplaceAll(content, "�", "\x00") switch currentField { case "Code": errMsg.Code = content case "Message": errMsg.Message = content } } } fmt.Printf("解析结果:%+v\n", errMsg) fmt.Printf("Message内容:%q\n", errMsg.Message) }
内容的提问来源于stack exchange,提问作者Tomáš Zato
相关产品推荐
相关产品推荐

