首页 > 解决方案 > 在两个命名空间中解组具有相同属性名称的 XML 属性

问题描述

我有一个 XML 结构,其中有一个未命名空间的“类型”属性和一个命名空间属性。我无法让 GO 的解组器读取两个“类型”属性。

XML 是:

data := `<response xmlns="urn:debugger_protocol_v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<map name="bool" type="bool_T" xsi:type="xsd:boolean"></map>
<map name="int" type="int_T" xsi:type="xsd:decimal"></map>
</response>`

我的 Go/XML 定义是:


type Typemap struct {
    XMLName xml.Name `xml:"map"`
    Name    string   `xml:"name,attr"`
    Type    string   `xml:"urn:debugger_protocol_v1 type,attr"`
    XsiType string   `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`
}

type Response struct {
    XMLName xml.Name `xml:"response"`
    Typemap []Typemap `xml:"map,omitempty"`
}

运行以下代码时:

package main

import (
    "encoding/xml"
    "fmt"
    "strings"
)

type Typemap struct {
    XMLName xml.Name `xml:"map"`
    Name    string   `xml:"name,attr"`
    Type    string   `xml:"urn:debugger_protocol_v1 type,attr"`
    XsiType string   `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`
}

type Response struct {
    XMLName xml.Name  `xml:"response"`
    Typemap []Typemap `xml:"map,omitempty"`
}

func main() {
    rq := new(Response)
    data := `<response xmlns="urn:debugger_protocol_v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<map name="bool" type="bool_T" xsi:type="xsd:boolean"></map>
<map name="int" type="int_T" xsi:type="xsd:decimal"></map>
</response>`

    reader := strings.NewReader(data)

    decoder := xml.NewDecoder(reader)

    err := decoder.Decode(&rq)

    if err != nil {
        fmt.Println(err)
    }

    fmt.Printf("Unmarshalled Content:\n%v", rq)
}

输出漏掉了bool_T,如下:

&{{urn:debugger_protocol_v1 response} [
    {{urn:debugger_protocol_v1 map} bool  xsd:boolean}
    {{urn:debugger_protocol_v1 map} int  xsd:decimal}
]}

如果我urn:debugger_protocol_v1从定义中删除 ,我会收到以下错误:

main.Typemap field "Type" with tag "type,attr" conflicts with field "XsiType" with tag "http://www.w3.org/2001/XMLSchema-instance type,attr"

我无法更改原始 XML 数据格式。有没有办法解组这两个type属性?

我也设置了一个围棋游乐场

标签: xmlgo

解决方案


事实证明,目前没有办法在 Go 中原生地做到这一点。有一个与命名空间相关的问题列表,以及描述它的特定问题,但尚未解决。

但是,我也找到了一种解决方法。可以为属性元素编写自己的解组代码。

对于属性,您可以实现以下内容:

type NonMarshalled string

func (s *NonMarshalled) UnmarshalXMLAttr(attr xml.Attr) error {
    fmt.Printf("Parsing attribute '%s', with value '%s'", attr.Name.Local, attr.Value)
    s.Title = strings.ToUpper(attr.Value)
    return nil
}

type Typemap struct {
    XMLName xml.Name `xml:"map"`
    Name    string   `xml:"name,attr"`
    Type    NonNamespaced `xml:"type,attr"`
    XsiType string   `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`
}

但是,这并不能同时具有命名空间属性名称和具有相同名称的非命名空间属性。这仍然会导致:

main.Typemap field "Type" with tag "type,attr" conflicts with field "XsiType" with tag "http://www.w3.org/2001/XMLSchema-instance type,attr"

这里的解决方法是为整个Typemap类型编写一个解组器。就我而言,此代码如下所示:

func (s *Typemap) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {                                         
    nameAttr := ""                                                                                                     
    typeAttr := ""                                                                                                     
    xsiTypeAttr := ""                                                                                                  

    for _, attr := range start.Attr {                                                                                  
        if attr.Name.Space == "" && attr.Name.Local == "name" {                                                        
            nameAttr = attr.Value                                                                                      
        }                                                                                                              
        if attr.Name.Space == "" && attr.Name.Local == "type" {                                                        
            typeAttr = attr.Value                                                                                      
        }                                                                                                              
        if attr.Name.Space == "http://www.w3.org/2001/XMLSchema-instance" && attr.Name.Local == "type" {               
            xsiTypeAttr = attr.Value                                                                                   
        }                                                                                                              
    }                                                                                                                  

    d.Skip()                                                                                                           
    *s = Typemap{Name: nameAttr, Type: typeAttr, XsiType: xsiTypeAttr}                                                 

    return nil                                                                                                         
}                                                                                                                      

推荐阅读