我想用Go解析和序列化xml,但看起来Marshall / Unmarshall仅适用于结构化数据,而不适用于有序指令。我想做这样的事情:
type Play struct { loops uint16 // Body of element is file name } type Say struct { loops uint16 voice string } func (p *Play) Execute() (err error) { // Play the file } xml := `<Root> <Say>Playing file</Say> <Play loops="2">https://host/somefile.mp3</Play> <Say>Done playing</Say> </Root>`
我想接受这一点,并从本质上讲是可以在上面运行方法的一部分。
for _, instruction := range actions { instruction.Execute() }
我该如何使用Unmarshall呢?
Unmarshall
编辑:也许我可以Decoder根据标签名称使用循环遍历和解组?
Decoder
与encoding/json包不同,您没有Unmarshaller接口。在您的情况下,您将必须使用Decoder自己建议的。
encoding/json
Unmarshaller
下面是一个可行的解决方案:
package main import ( "bytes" "encoding/xml" "fmt" ) // An interface required by any instruction type Executer interface { Execute() error } var factoryMap map[string]func() Executer = make(map[string]func() Executer) type Play struct { Loops int `xml:"loops,attr"` File string `xml:",innerxml"` // Body of element is file name } func (p *Play) Execute() error { for i := 0; i < p.Loops; i++ { fmt.Println(`o/ ` + p.File) } return nil } type Say struct { Voice string `xml:",innerxml"` } func (s *Say) Execute() error { fmt.Println(s.Voice) return nil } // Let's register the different instructions // You can have each Instruction struct in separate files, letting each file having an init func init() { factoryMap["Play"] = func() Executer { return new(Play) } factoryMap["Say"] = func() Executer { return new(Say) } } func Unmarshal(b []byte) ([]Executer, error) { d := xml.NewDecoder(bytes.NewReader(b)) var actions []Executer // Finding the first Root tag for { v, err := d.Token() if err != nil { return nil, err } if _, ok := v.(xml.StartElement); ok { break } } // Looping through the rest of the tokens // finding the start of each. for { v, err := d.Token() if err != nil { return nil, err } switch t := v.(type) { case xml.StartElement: // We found a start of an instruction. // Let's check the name in our factoryMap // You should check that the Instruction name actually exists. Now it panics. f := factoryMap[t.Name.Local] instr := f() // We decode the rest of the tag into the instruction struct err := d.DecodeElement(instr, &t) if err != nil { return nil, err } // Appending the populated action actions = append(actions, instr) case xml.EndElement: // We found the end tag of the Root. We are done! return actions, nil } } return nil, nil } func main() { xml := []byte(`<Root> <Say>Playing file</Say> <Play loops="2">https://host/somefile.mp3</Play> <Say>Done playing</Say> </Root>`) actions, err := Unmarshal(xml) if err != nil { panic(err) } for _, instruction := range actions { err = instruction.Execute() if err != nil { fmt.Println(err) } } }
输出:
Playing file o/ https://host/somefile.mp3 o/ https://host/somefile.mp3 Done playing
操场
当然,此代码并不完整,但是足以让您清楚地了解如何解决问题。