根元素的 MarshalXML
MarshalXML for root element
我似乎无法让编组器为根节点调用 MarshalXML
。我有一个用于存储 XML 的结构,如下所示:
type XmlNode struct {
Name string `xml:"-"`
Attrs map[string]string `xml:"-"`
Children []XmlNode `xml:",any"`
}
我有一个 MarshalXML
函数,如下所示:
func (n *XmlNode) MarshalXML(encoder *xml.Encoder, start xml.StartElement) error {
fmt.Println("Inside MarshalXML")
// Name
start.Name = xml.Name{Local: n.Name}
// Attrs
for k, v := range n.Attrs {
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: k}, Value: v})
}
type node XmlNode
return encoder.EncodeElement((*node)(n), start)
}
我用类似于以下的代码调用它:
func main() {
root := XmlNode{Name: "root", Attrs: map[string]string{}}
root.Attrs["rootAttr"] = "rootValue"
child := XmlNode{Name: "child", Attrs: map[string]string{}}
child.Attrs["childAttr"] = "childValue"
root.Children = append(root.Children, child)
out, _ := xml.Marshal(root)
fmt.Println(string(out))
}
运行时,我看到以下输出:
Inside MarshalXML
<XmlNode><child childAttr="childValue"></child></XmlNode>
我期待:
Inside MarshalXML
Inside MarshalXML
<root rootAttr="rootValue"><child childAttr="childValue"></child></root>
我知道我可以将名称编码到特定属性中,但我更关心如何处理地图中的属性。我也可能不应该在那里修改开始元素,但它似乎适用于此。我的问题是 MarshalXML 不会为根元素调用,但它会为任何子元素调用。所以,即使我在那里做了一些正确的事情,我也不确定我是否可以拦截那个根元素来适当地修改它。
我简单地尝试过直接使用xml.Encode
,但它似乎遵循相同的路径构建启动模板和根目录,然后为Children
节点调用MarshalXML
.有没有一种干净的方法可以将 XmlNode
转换为 xml.StartElement
以便我可以将其传递?我可以直接在 XmlNode 上编写一个函数,该函数将通过创建 StartElement 并调用 EncodeElement
或 Encode
来开始编组,但我试图避免这种情况并坚持使用 xml.Marshal
调用,以防止对其他开发者造成混淆。
有什么想法吗?
这里是上述代码的可运行形式 Go Playground link。
要么传入一个指针,即xml.Marshal(&root)
(playground), or change the receiver type from pointer to non-pointer, i.e. func (n *XmlNode) MarshalXML
=> func (n XmlNode) MarshalXML
(playground).
https://go.dev/ref/spec#Method_sets
The method set of type T
consists of all methods declared
with receiver type T
. The method set of the corresponding pointer type
*T
is the set of all methods declared with receiver *T
or T
(that is, it also contains the method set of T
).
The method set of a type determines the interfaces that the type
implements and the methods that can be called using a receiver of that
type.
上面的意思是 XmlNode
类型的变量 root
没有实现 xml.Marshaler
因为 MarshalXML
方法不是 XmlNode
方法集的一部分。但是 &root
类型 *XmlNode
确实实现了 xml.Marshaler
因为 MarshalXML
方法是 *XmlNode
的一部分的方法集。
当您将方法的接收者更改为非指针时,即 func (n XmlNode) MarshalXML
那么,XmlNode
类型的 root
将实现 xml.Marshaler
因为 MarshalXML
方法将成为XmlNode
方法集的一部分。引用的规范还说,&root
类型 *XmlNode
也将实现 xml.Marshaler
因为 MarshalXML
方法,即使在 XmlNode
上声明,也将成为 *XmlNode
方法集的一部分。
请注意,当您想直接在具体类型上而不是通过接口调用方法时,考虑此指针与非指针方法集的内容并不那么重要。例如,如果您有变量 root
,如果类型为 XmlNode
,并且您想要调用方法 DoXyz
,该方法使用指针接收器声明为 func (n *XmlNode) DoXyz()
,那么你不需要 &root
,表达式 root.DoXyz
会编译得很好。
A method call x.m()
is valid if the method set of (the type of) x
contains m
and the argument list can be assigned to the parameter list of m
. If x
is addressable and &x
's method set contains m
, x.m()
is shorthand for (&x).m()
.
我似乎无法让编组器为根节点调用 MarshalXML
。我有一个用于存储 XML 的结构,如下所示:
type XmlNode struct {
Name string `xml:"-"`
Attrs map[string]string `xml:"-"`
Children []XmlNode `xml:",any"`
}
我有一个 MarshalXML
函数,如下所示:
func (n *XmlNode) MarshalXML(encoder *xml.Encoder, start xml.StartElement) error {
fmt.Println("Inside MarshalXML")
// Name
start.Name = xml.Name{Local: n.Name}
// Attrs
for k, v := range n.Attrs {
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: k}, Value: v})
}
type node XmlNode
return encoder.EncodeElement((*node)(n), start)
}
我用类似于以下的代码调用它:
func main() {
root := XmlNode{Name: "root", Attrs: map[string]string{}}
root.Attrs["rootAttr"] = "rootValue"
child := XmlNode{Name: "child", Attrs: map[string]string{}}
child.Attrs["childAttr"] = "childValue"
root.Children = append(root.Children, child)
out, _ := xml.Marshal(root)
fmt.Println(string(out))
}
运行时,我看到以下输出:
Inside MarshalXML
<XmlNode><child childAttr="childValue"></child></XmlNode>
我期待:
Inside MarshalXML
Inside MarshalXML
<root rootAttr="rootValue"><child childAttr="childValue"></child></root>
我知道我可以将名称编码到特定属性中,但我更关心如何处理地图中的属性。我也可能不应该在那里修改开始元素,但它似乎适用于此。我的问题是 MarshalXML 不会为根元素调用,但它会为任何子元素调用。所以,即使我在那里做了一些正确的事情,我也不确定我是否可以拦截那个根元素来适当地修改它。
我简单地尝试过直接使用xml.Encode
,但它似乎遵循相同的路径构建启动模板和根目录,然后为Children
节点调用MarshalXML
.有没有一种干净的方法可以将 XmlNode
转换为 xml.StartElement
以便我可以将其传递?我可以直接在 XmlNode 上编写一个函数,该函数将通过创建 StartElement 并调用 EncodeElement
或 Encode
来开始编组,但我试图避免这种情况并坚持使用 xml.Marshal
调用,以防止对其他开发者造成混淆。
有什么想法吗?
这里是上述代码的可运行形式 Go Playground link。
要么传入一个指针,即xml.Marshal(&root)
(playground), or change the receiver type from pointer to non-pointer, i.e. func (n *XmlNode) MarshalXML
=> func (n XmlNode) MarshalXML
(playground).
https://go.dev/ref/spec#Method_sets
The method set of type
T
consists of all methods declared with receiver typeT
. The method set of the corresponding pointer type*T
is the set of all methods declared with receiver*T
orT
(that is, it also contains the method set ofT
).The method set of a type determines the interfaces that the type implements and the methods that can be called using a receiver of that type.
上面的意思是 XmlNode
类型的变量 root
没有实现 xml.Marshaler
因为 MarshalXML
方法不是 XmlNode
方法集的一部分。但是 &root
类型 *XmlNode
确实实现了 xml.Marshaler
因为 MarshalXML
方法是 *XmlNode
的一部分的方法集。
当您将方法的接收者更改为非指针时,即 func (n XmlNode) MarshalXML
那么,XmlNode
类型的 root
将实现 xml.Marshaler
因为 MarshalXML
方法将成为XmlNode
方法集的一部分。引用的规范还说,&root
类型 *XmlNode
也将实现 xml.Marshaler
因为 MarshalXML
方法,即使在 XmlNode
上声明,也将成为 *XmlNode
方法集的一部分。
请注意,当您想直接在具体类型上而不是通过接口调用方法时,考虑此指针与非指针方法集的内容并不那么重要。例如,如果您有变量 root
,如果类型为 XmlNode
,并且您想要调用方法 DoXyz
,该方法使用指针接收器声明为 func (n *XmlNode) DoXyz()
,那么你不需要 &root
,表达式 root.DoXyz
会编译得很好。
A method call
x.m()
is valid if the method set of (the type of)x
containsm
and the argument list can be assigned to the parameter list ofm
. Ifx
is addressable and&x
's method set containsm
,x.m()
is shorthand for(&x).m()
.