Goで構造体スライスをXMLにして ( ;゚皿゚)ノシΣバンバン!! ってなった話
結論から書くと、ちゃんと構造体でラップしましょう、ということです。
出ないと書き出したXMLがちゃんと読み込まれなくて ( ;゚皿゚)ノシΣバンバン!! ってすることになります。
やらかしたこと
次のように、構造体とそのスライスを用意して、XMLに変換したわけです。
package main import "encoding/xml" import "fmt" type Item struct { XMLName xml.Name `xml:"item"` ID string `xml:"id,attr"` Name string `xml:"name"` Value string `xml:"value"` } type Items []Item func main() { items := Items{ Item{ ID: "001", Name: "AA1", Value: "|ω・`)ノ ヤァ", }, Item{ ID: "002", Name: "AA2", Value: "|д゚)チラッ", }, } if buf, err := xml.MarshalIndent(items, "", " "); err != nil { panic(err) } else { fmt.Print(xml.Header) fmt.Println(string(buf)) } }
実行すると次のようなXMLを得られます。
<?xml version="1.0" encoding="UTF-8"?> <item id="001"> <name>AA1</name> <value>|ω・`)ノ ヤァ</value> </item> <item id="002"> <name>AA2</name> <value>|д゚)チラッ</value> </item>
この時点ですでになんか怪しいことにお気づきでしょうか。そう、ルート要素が複数あるということに。
さて、これを以下のコードで読み込むと( ゚д゚)します。
package main import "encoding/xml" import "fmt" type Item struct { XMLName xml.Name `xml:"item"` ID string `xml:"id,attr"` Name string `xml:"name"` Value string `xml:"value"` } type Items []Item func main() { items := make(Items, 0) doc := `<?xml version="1.0" encoding="UTF-8"?> <item id="001"> <name>AA1</name> <value>|ω・`)ノ ヤァ</value> </item> <item id="002"> <name>AA2</name> <value>|д゚)チラッ</value> </item> ` if err := xml.Unmarshal([]byte(doc), &items); err != nil { panic(err) } else { for _, item := range items { fmt.Println(item) } } }
以下出力結果。
{{ item} 001 AA1 |ω・`)ノ ヤァ}
二要素目が闇に消えましたね ( ・´ー・`)
原因
たぶんルート要素がいっぱいあるせい。つまり正しいXMLになってない。
正しい解決法
なので、ちゃんと構造体でラップしましょう、そうしましょう。
エンコードする方のコード
package main import "encoding/xml" import "fmt" type Item struct { XMLName xml.Name `xml:"item"` ID string `xml:"id,attr"` Name string `xml:"name"` Value string `xml:"value"` } type Items struct { XMLName xml.Name `xml:"items"` Items []Item `xml:"item"` } func main() { items := &Items{ Items: []Item{ Item{ ID: "001", Name: "AA1", Value: "|ω・`)ノ ヤァ", }, Item{ ID: "002", Name: "AA2", Value: "|д゚)チラッ", }, }, } if buf, err := xml.MarshalIndent(items, "", " "); err != nil { panic(err) } else { fmt.Print(xml.Header) fmt.Println(string(buf)) } }
エンコード結果
<?xml version="1.0" encoding="UTF-8"?> <items> <item id="001"> <name>AA1</name> <value>|ω・`)ノ ヤァ</value> </item> <item id="002"> <name>AA2</name> <value>|д゚)チラッ</value> </item> </items>
デコードする方のコード
package main import "encoding/xml" import "fmt" type Item struct { XMLName xml.Name `xml:"item"` ID string `xml:"id,attr"` Name string `xml:"name"` Value string `xml:"value"` } type Items struct { XMLName xml.Name `xml:"items"` Items []Item `xml:"item"` } func main() { items := Items{} doc := `<?xml version="1.0" encoding="UTF-8"?> <items> <item id="001"> <name>AA1</name> <value>|ω・`)ノ ヤァ</value> </item> <item id="002"> <name>AA2</name> <value>|д゚)チラッ</value> </item> </items> ` if err := xml.Unmarshal([]byte(doc), &items); err != nil { panic(err) } else { for _, item := range items.Items { fmt.Println(item) } } }
デコード結果
{{ item} 001 AA1 |ω・`)ノ ヤァ} {{ item} 002 AA2 |д゚)チラッ}
ダメな解決方法
構造体作りたくないでござる、スライスはスライスのままにしたいでござる、みたいな人 *1 のためにたぶん間違った解決方法を。
その方法は xml.Marshaler / xml.Unmarshalerを実装して、ルート要素を無理やり作る、というのもですが。
エンコード側
package main import "encoding/xml" import "fmt" type Item struct { XMLName xml.Name `xml:"item"` ID string `xml:"id,attr"` Name string `xml:"name"` Value string `xml:"value"` } type Items []Item func (self Items) MarshalXML(e *xml.Encoder, start xml.StartElement) error { root := struct { Items []Item `xml:"item"` }{ Items: self, } start.Name.Local = "items" return e.EncodeElement(&root, start) } func main() { items := Items{ Item{ ID: "001", Name: "AA1", Value: "|ω・`)ノ ヤァ", }, Item{ ID: "002", Name: "AA2", Value: "|д゚)チラッ", }, } if buf, err := xml.MarshalIndent(items, "", " "); err != nil { panic(err) } else { fmt.Print(xml.Header) fmt.Println(string(buf)) } }
デコード側
package main import "encoding/xml" import "fmt" type Item struct { XMLName xml.Name `xml:"item"` ID string `xml:"id,attr"` Name string `xml:"name"` Value string `xml:"value"` } type Items []Item func (self *Items) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { root := struct { Items []Item `xml:"item"` }{} if err := d.DecodeElement(&root, &start); err != nil { return err } else { *self = root.Items return nil } } func main() { items := Items{} doc := `<?xml version="1.0" encoding="UTF-8"?> <items> <item id="001"> <name>AA1</name> <value>|ω・`)ノ ヤァ</value> </item> <item id="002"> <name>AA2</name> <value>|д゚)チラッ</value> </item> </items> ` if err := xml.Unmarshal([]byte(doc), &items); err != nil { panic(err) } else { for _, item := range items { fmt.Println(item) } } }
自分自身のスライスの参照先を書き換えるとか ((((;゚Д゚))))ガクガクブルブル しますね!!
*1:私