Golang中如何实现不带xmlns的XML结构体序列化
Golang中如何实现不带xmlns的XML结构体序列化 我希望在序列化结构体时不添加xmlns;据我所知,一旦上层标签添加了xmlns,即使不存在,也总是会添加一个空的xmlns。
也就是说,我希望‘bar2’输出为普通的‘’,而不是‘’。
我已经追踪到这里,但查看那个条件语句,我没有看到任何抑制添加空xmlns的方法。
有没有办法做到这一点,还是我只需要在每个层级都添加一个显式的xmlns?
谢谢, Jon
更多关于Golang中如何实现不带xmlns的XML结构体序列化的实战教程也可以访问 https://www.itying.com/category-94-b0.html
编组器代码中的注释没有明确说明这一点,但这种行为是合理的。请从不具有 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结构复杂度,可以选择最适合的方案。

