2013-09-29 88 views
4

我想用Go解析和序列化xml,但它看起來像Marshall/Unmarshall只適用於結構化數據,而不適用於有序指令。我想要做這樣的事情:我可以使用Go的xml.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

編輯:也許我可以使用Decoder循環和Unmarshall每一個基於標籤名稱?

回答

3

encoding/json包不同,您沒有Unmarshaller接口。在你的情況下,你必須按照你自己的建議使用Decoder

下面是一個有效的解決方案:

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 

Playground

當然,這個代碼是不完整的,但它應該足夠給你一個關於如何解決問題的清晰畫面。

+0

絕對完美。這裏有50個大你:) –

+0

整潔!哦,我注意到一個錯誤,我通過測試'v == nil'來檢查EOF是否已經達到。這是錯誤的。我應該測試'err == io.EOF'。我刪除了代碼,以免混淆。 – ANisus

+0

我應該不檢查'EOF'嗎?還是應該檢查'err = io.EOF'是否有強健的代碼? –

相關問題