首页 > 解决方案 > xml 编组和解组 xsi:type

问题描述

这是关于xsi:type属性的,但是如果您有一个名为 < soap:envelope ...> 的元素,则会出现类似的问题,似乎有必要实现自定义解组器函数。

这是有问题的代码(在游乐场: https: //play.golang.org/p/ORQsINYS-9o

type PayloadPublication struct {
    XMLName xml.Name `json:"xmlName"`
    XsiType xml.Attr `xml:"xsi type,attr" json:"xsiType"`
    Lang    xml.Attr `xml:"lang,attr" json:"lang"`
}

func main() {
    payload := PayloadPublication{}

    expectedXML := `<payloadPublication xmlns="http://datex2.eu/schema/2/2_0" xsi:type="MeasurementSiteTablePublication" lang="nl"></payloadPublication>`

    // 1. Unmarshal - 2. Marshal - 3. Compare
    err := xml.Unmarshal([]byte(expectedXML), &payload)
    if err != nil {
        fmt.Print(err.Error())
    }

    result, err := xml.Marshal(payload)
    if err != nil {
        fmt.Print(err.Error())
    }

    fmt.Printf("expected: %v\n", expectedXML)
    fmt.Printf("result:   %v\n", string(result))
}

结果

expected: <payloadPublication xmlns="http://datex2.eu/schema/2/2_0" xsi:type="MeasurementSiteTablePublication" lang="nl"></payloadPublication>
result:   <payloadPublication xmlns="http://datex2.eu/schema/2/2_0" xmlns:xsi="xsi" xsi:type="MeasurementSiteTablePublication" lang="nl"></payloadPublication>

如您所见,添加了属性xmlns:xsi="xsi"这不是我想要的

解决此问题的方法是使用自定义解组器,但这似乎不必要地复杂和冗长。(下面的代码)。有没有更简单的方法来做到这一点?处理这种 XML 的常规方法是什么?

解决方法代码(在操场上:https: //play.golang.org/p/d4OtYPtYBDg

type PayloadPublication struct {
    XMLName xml.Name `json:"xmlName"`
    XsiType xml.Attr `xml:"xsi type,attr" json:"xsiType"`
    Lang    xml.Attr `xml:"lang,attr" json:"lang"`
}

func (pp *PayloadPublication) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    // Attributes
    for _, attr := range start.Attr {
        if attr.Name.Local == "type" {
            pp.XsiType = xml.Attr{Name: xml.Name{Local: "xsi:type"}, Value: attr.Value}
        }
    }

    var dummy struct {
        // attributes
        XMLName xml.Name `json:"xmlName"`
        XsiType xml.Attr `xml:"xsi type,attr" json:"xsiType"`
        Lang    xml.Attr `xml:"lang,attr" json:"lang"`
    }

    err := d.DecodeElement(&dummy, &start)
    if err != nil {
        return err
    }

    pp.XMLName = dummy.XMLName
    pp.Lang = dummy.Lang

    return nil
}

func main() {
    payload := PayloadPublication{}

    expectedXML := `<payloadPublication xmlns="http://datex2.eu/schema/2/2_0" xsi:type="MeasurementSiteTablePublication" lang="nl"></payloadPublication>`

    // 1. Unmarshal - 2. Marshal - 3. Compare
    err := xml.Unmarshal([]byte(expectedXML), &payload)
    if err != nil {
        fmt.Print(err.Error())
    }

    result, err := xml.Marshal(payload)
    if err != nil {
        fmt.Print(err.Error())
    }

    fmt.Printf("expected: %v\n", expectedXML)
    fmt.Printf("result:   %v\n", string(result))
}

标签: xmlgomarshallingunmarshalling

解决方案


从技术上讲,示例中的黄金结果(我的意思是第一个)似乎不是格式良好的 XML。它具有属性的未绑定 xml 元素前缀xsixsi:type我认为,这个规范为此设定了权威规则。这个 XML 片段看起来没有竞争力,如果它是更大文档的一部分,那么xsi前缀可能会绑定到其他地方——这对于 SOAP 文档很常见。

请注意,Go Marshaller 生成格式良好的 XML。由于片段中缺少 DTD 等原因,该文档仍然不是有效的 XML,但它至少不会混淆任何 XML 解析器。

作为一个更广泛的建议,匹配用于测试或任何东西的 XML 文档的确切文本表示最有可能产生较差的结果,因为在 XML 中编码文档的语法正确(且格式正确)的方法不止一种。不同的编组器可以根据作者的喜好调整特定的编码方法,这不会使生成的 XML 损坏或与任何其他兼容的解码器不兼容。尝试比较未编组的结构,而不是比较文本片段。像github.com/google/go-cmp/cmp包这样的东西可以让它变得简单。


推荐阅读