You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言xml.Unmarshal解析空字符实体报错,如何处理?

问题描述

我需要验证系统是否会被空字符(\0,URL编码为%00)干扰,这是集成测试的一部分。系统返回了包含�实体的XML错误,但我的Go测试代码解析该XML时失败。

我认为以下XML是有效的:

<Error>
    <Code>1234</Code>
    <Message>Test&#0;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&#0;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包严格遵循这一规范,因此遇到&#0;实体时会直接抛出错误。

方法1:预处理XML内容(简单直接)

在解析前将XML中的&#0;替换为合法占位符,解析完成后再替换回空字符:

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&#0;afterzero</Message>
          </Error>
       `)
    // 替换&#0;为临时占位符
    processedData := strings.ReplaceAll(string(rawData), "&#0;", "__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开启非严格模式,并自定义实体映射来支持&#0;:

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&#0;afterzero</Message>
          </Error>
       `)

    dec := xml.NewDecoder(bytes.NewReader(rawData))
    // 关闭严格模式,允许部分非规范XML内容
    dec.Strict = false
    // 自定义实体映射,将&#0;映射为空字符
    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流,手动解析&#0;实体:

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&#0;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, "&#0;", "\x00")
            content = strings.ReplaceAll(content, "&#x0;", "\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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 04:25:59