我可以使用Go的xml.Unmarshall排序多态类型吗? [英] Can I use Go's xml.Unmarshall for Ordered Polymorphic Types?
问题描述
我想用Go解析和序列化xml,但它看起来像Marshall / Unmarshall只适用于结构化数据,而不适用于有序指令。我想要做这样的事情:
type Play结构{
loops uint16
//元素体是文件名
}
类型说struct {
loops uint16
语音字符串
}
func( p * Play)Execute()(err error){
//播放文件
}
xml:=`< Root>
< Say>播放档案< / Say>
< Play loops =2> https://host/somefile.mp3< / Play>
< Say>完成播放< / Say>
< / Root>`
我想要这样做,我可以运行方法的一部分。
for _,instruction:= range actions {
instruction.Execute ()
}
如何使用 Unmarshall
?
编辑:也许我可以使用解码器
循环和Unmarshall每一个基于标签名称?
与 encoding / json
包不同,您没有 Unmarshaller
界面。在你的情况下,你必须使用 Decoder
,正如你所建议的那样。
以下是一个工作解决方案:
$ b
package main
import(
bytes
编码/ xml
fmt
)
//任何指令所需的接口
类型执行接口{
Execute()错误
}
var factoryMap map [string] func()Executer = make(map [string] func()Executer)
类型播放结构{
Loops int`xml:loops,attr`
文件字符串`xml:,innerxml`
//元素体是文件名
}
func(p * Play)Execute()错误{
for i:= 0;我< p.Loops; i ++ {
fmt.Println(`o /`+ p.File)
}
返回零
}
类型说struct {
语音字符串`xml:,innerxml`
}
func(s * Say)Execute()错误{
fmt.Println(s.Voice)
返回nil
}
//让我们注册不同的指令
//你可以将每个指令结构放在不同的文件中,让每个文件都有一个init
func init(){
factoryMap [Play] = func()Executer {return new(Play)}
factoryMap [Say] = func()Executer {return new(Say)}
$ b $ func Unmarshal(b [] byte)([] Executer,error){
d:= xml.NewDecoder(bytes.NewReader(b))
var操作[]执行器
//找到第一个根标记
用于{
v,err:= d.Token()
如果err!= nil {
return nil,err
}
if _,ok:= v。(xml.StartElement); OK {
break
}
}
//循环读取其余的标记
//找到每个标记的开头。
for {
v,err:= d.Token()
if err!= nil {
return nil,err
}
switch t:= v。(type){
case xml.StartElement:
//我们找到了一条指令的开始。
//让我们在我们的factoryMap
中检查名称//您应该检查指令名称是否存在。现在它恐慌。
f:= factoryMap [t.Name.Local]
instr:= f()
//我们将标记的其余部分解码为指令struct
err := d.DecodeElement(instr,& t)
if err!= nil {
return nil,err
}
//追加填充动作
actions = append(actions,instr)
case xml.EndElement:
//我们找到Root的结束标记。我们完了!
return actions,nil
}
}
return nil,nil
}
func main(){
xml: = [] byte(`< Root>
< Say>播放文件< / Say>
< Play loops =2> https://host/somefile.mp3< / Play> ;
< Say>完成播放< / Say>
< / Root>`)
actions,err:= Unmarshal(xml)
if err! = nil {
panic(err)
}
for _,instruction:= range actions {
err = instruction.Execute()
if err != nil {
fmt.Println(err)
}
}
}
输出:
播放文件
o / //host/somefile.mp3
o / https://host/somefile.mp3
完成播放
当然,这个代码并不完整,但应该是足以让您清楚了解如何解决问题。
I want to parse and serialize xml with Go, but it's looking like Marshall/Unmarshall only works well for structured data and not so much for ordered instructions. I'd like to do something like this:
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>`
I want to take that and essentially end up with a slice of these that I can run methods on.
for _, instruction := range actions {
instruction.Execute()
}
How can I do that using Unmarshall
?
EDIT: Maybe I could use the Decoder
to loop through and Unmarshall each one based on the tag name?
Unlike the encoding/json
package, you have no Unmarshaller
interface. In your case, you will have to use the Decoder
as you have suggested yourself.
Below is a working solution:
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)
}
}
}
Output:
Playing file
o/ https://host/somefile.mp3
o/ https://host/somefile.mp3
Done playing
Of course, this code is not complete, but it should be enough to give you a clear picture on how you can solve your problem.
这篇关于我可以使用Go的xml.Unmarshall排序多态类型吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!