Golang中如何实现不带xmlns的XML结构体序列化

Golang中如何实现不带xmlns的XML结构体序列化 我希望在序列化结构体时不添加xmlns;据我所知,一旦上层标签添加了xmlns,即使不存在,也总是会添加一个空的xmlns。

Go Playground - Go编程语言

也就是说,我希望‘bar2’输出为普通的‘’,而不是‘’。

我已经追踪到这里,但查看那个条件语句,我没有看到任何抑制添加空xmlns的方法。

有没有办法做到这一点,还是我只需要在每个层级都添加一个显式的xmlns?

谢谢, Jon


更多关于Golang中如何实现不带xmlns的XML结构体序列化的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

编组器代码中的注释没有明确说明这一点,但这种行为是合理的。请从不具有 xmlns 属性的 XML 读取器的角度来思考这意味着什么:“此元素从其父元素继承命名空间。”但你使用空命名空间初始化了插入的元素,这与父元素的命名空间不同。因此,编组器现在表示:“注意,我们正在此处更改命名空间。”

建议查看这篇关于空命名空间的讨论

更多关于Golang中如何实现不带xmlns的XML结构体序列化的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢您的回复;是的,这里的问题不在于封送器(marshaler)本身有误——调用者可能确实需要 xmlns=""。但正如您所说,这里存在两种情况:无命名空间+继承,以及全局命名空间,而封送器代码无法区分这两者。我想这大概是类型定义的方式导致的——如果 Space 是一个指向字符串的指针,那么我们本可以用 nil 表示无命名空间,用空字符串表示全局命名空间。

无论如何,如果我理解正确的话,使用通用的封送器是无法实现完全无命名空间的行为的。因此,我认为我需要采用为我的结构体添加自定义封送方法(marshal method)的方案,这样我就可以自由地按我的意愿来构造它了。

在Go的encoding/xml包中,确实存在一旦父元素设置了xmlns属性,子元素会自动继承并可能生成空xmlns的问题。要避免生成空的xmlns属性,可以通过自定义XML命名空间或使用原始XML处理来实现。

以下是两种解决方案:

方案1:使用自定义类型和MarshalXML方法

通过实现xml.Marshaler接口,可以完全控制序列化过程:

package main

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

type Foo struct {
    Bar Bar `xml:"bar"`
    Baz Baz `xml:"baz"`
}

type Bar struct {
    Value string `xml:",chardata"`
}

type Baz struct {
    Bar2 Bar2 `xml:"bar2"`
}

// Bar2 自定义序列化
type Bar2 struct {
    Value string
}

func (b Bar2) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    // 创建不带xmlns的起始标签
    start.Name = xml.Name{Local: "bar2"}
    start.Attr = nil // 清空所有属性
    
    if err := e.EncodeToken(start); err != nil {
        return err
    }
    
    if err := e.EncodeToken(xml.CharData(b.Value)); err != nil {
        return err
    }
    
    return e.EncodeToken(xml.EndElement{Name: start.Name})
}

func main() {
    foo := Foo{
        Bar: Bar{Value: "hello"},
        Baz: Baz{
            Bar2: Bar2{Value: "world"},
        },
    }

    output, err := xml.MarshalIndent(foo, "", "  ")
    if err != nil {
        panic(err)
    }
    
    fmt.Println(string(output))
}

方案2:使用xml.Name控制命名空间

通过为结构体字段指定明确的XML名称,可以避免自动添加xmlns:

package main

import (
    "encoding/xml"
    "fmt"
)

type Foo struct {
    XMLName xml.Name `xml:"foo"`
    Bar     Bar      `xml:"bar"`
    Baz     Baz      `xml:"baz"`
}

type Bar struct {
    XMLName xml.Name `xml:"bar"`
    Value   string   `xml:",chardata"`
}

type Baz struct {
    XMLName xml.Name `xml:"baz"`
    Bar2    Bar2     `xml:"bar2"`
}

type Bar2 struct {
    XMLName xml.Name `xml:""` // 空命名空间
    Value   string   `xml:",chardata"`
}

func main() {
    foo := Foo{
        Bar: Bar{Value: "hello"},
        Baz: Baz{
            Bar2: Bar2{Value: "world"},
        },
    }

    output, err := xml.MarshalIndent(foo, "", "  ")
    if err != nil {
        panic(err)
    }
    
    fmt.Println(string(output))
}

方案3:后处理XML字符串

如果上述方法不适用,可以在序列化后移除空的xmlns属性:

package main

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

type Foo struct {
    XMLName xml.Name `xml:"foo"`
    Bar     string   `xml:"bar"`
    Baz     Baz      `xml:"baz"`
}

type Baz struct {
    Bar2 string `xml:"bar2"`
}

func main() {
    foo := Foo{
        Bar: "hello",
        Baz: Baz{Bar2: "world"},
    }

    output, err := xml.MarshalIndent(foo, "", "  ")
    if err != nil {
        panic(err)
    }
    
    // 移除空的xmlns属性
    result := strings.ReplaceAll(string(output), ` xmlns=""`, "")
    fmt.Println(result)
}

方案4:使用原始XML处理

对于复杂场景,可以直接生成XML:

package main

import (
    "encoding/xml"
    "fmt"
)

func main() {
    type Bar2 struct {
        Value string
    }
    
    bar2 := Bar2{Value: "world"}
    
    // 手动构建XML
    sb := &strings.Builder{}
    encoder := xml.NewEncoder(sb)
    encoder.Indent("", "  ")
    
    encoder.EncodeToken(xml.StartElement{Name: xml.Name{Local: "foo"}})
    encoder.EncodeToken(xml.StartElement{Name: xml.Name{Local: "bar"}})
    encoder.EncodeToken(xml.CharData("hello"))
    encoder.EncodeToken(xml.EndElement{Name: xml.Name{Local: "bar"}})
    
    encoder.EncodeToken(xml.StartElement{Name: xml.Name{Local: "baz"}})
    // bar2没有xmlns属性
    encoder.EncodeToken(xml.StartElement{
        Name: xml.Name{Local: "bar2"},
        Attr: []xml.Attr{},
    })
    encoder.EncodeToken(xml.CharData(bar2.Value))
    encoder.EncodeToken(xml.EndElement{Name: xml.Name{Local: "bar2"}})
    
    encoder.EncodeToken(xml.EndElement{Name: xml.Name{Local: "baz"}})
    encoder.EncodeToken(xml.EndElement{Name: xml.Name{Local: "foo"}})
    encoder.Flush()
    
    fmt.Println(sb.String())
}

第一种方案(实现MarshalXML方法)通常是最可靠的方法,因为它提供了对序列化过程的完全控制。根据具体的XML结构复杂度,可以选择最适合的方案。

回到顶部