我似乎在讓編組器呼叫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>
我知道我可以將名稱編碼到特定的屬性中,但我更關心處理地圖中的屬性。我也可能不應該在那里修改 start 元素,但它似乎適用于此。我的問題是 MarshalXML 不會為根元素呼叫,但它會為任何子元素呼叫。因此,即使我在那里正確地做某事,我也不確定我是否可以攔截該根元素以對其進行適當的修改。
我簡單地嘗試過xml.Encode直接使用,但它似乎遵循相同的路徑來構建啟動模板和根,然后MarshalXML為Children節點呼叫。有沒有一種干凈的方法可以XmlNode轉換成xml.StartElement這樣我可以傳遞它?我可以直接在 XmlNode 上撰寫一個函式,通過創建 StartElement 并呼叫EncodeElementor 來開始編組Encode,但我試圖避免這種情況并堅持xml.Marshal呼叫,以防止其他開發人員混淆。
有什么想法嗎?
這是一個Go Playground鏈接,以可運行的形式指向上述代碼。
uj5u.com熱心網友回復:
要么傳入一個指標,即xml.Marshal(&root)(playground),要么將接收器型別從指標更改為非指標,即func (n *XmlNode) MarshalXML=> func (n XmlNode) MarshalXML(playground)。
https://go.dev/ref/spec#Method_sets
型別的方法集
T由所有用接收者型別宣告的方法組成T。對應指標型別*T的方法集是所有用receiver*Tor宣告T的方法的集合(也就是包含了的方法集T)。型別的方法集決定了該型別實作的介面以及可以使用該型別的接收器呼叫的方法。
以上意味著該變數root,它的型別的XmlNode,沒有實作xml.Marshaler ,因為該MarshalXML方法并不的一部分XmlNode的方法集。但是&root,它的型別*XmlNode確實會實作,xml.Marshaler 因為該MarshalXML方法是*XmlNode方法集的一部分。
當您將方法的接收器更改為非指標時,即func (n XmlNode) MarshalXML,root型別為 的XmlNode將實作,xml.Marshaler 因為該MarshalXML方法將成為XmlNode的方法集的一部分。并且參考的規范還說, ,&root型別*XmlNode也將實作,xml.Marshaler 因為該MarshalXML方法即使宣告在 上XmlNode,也將是*XmlNode的方法集的一部分。
請注意,當您想直接呼叫具體型別上的方法而不是通過介面時,考慮此指標與非指標方法集的內容并不那么重要。例如,如果您有一個rootif 型別的變數XmlNode,并且您想呼叫該方法DoXyz,該方法用指標接收器宣告為 as func (n *XmlNode) DoXyz(),那么您不需要&root,運算式root.DoXyz將編譯得很好。
https://go.dev/ref/spec#Calls
x.m()如果(的型別)的方法集x包含m并且引數串列可以分配給 的引數串列,則方法呼叫是有效的m。Ifx是可尋址的并且&x的方法集包含m,x.m()是 的簡寫(&x).m()。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/378877.html
