4

Goでxmlを解析してシリアライズしたいのですが、マーシャル/アンマーシャルは構造化データに対してのみうまく機能し、順序付けられた命令に対してはあまり機能しないようです。私はこのようなことをしたいと思います:

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て、タグ名に基づいてそれぞれをループスルーし、アンマーシャリングできますか?

4

1 に答える 1

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

遊び場

もちろん、このコードは完全ではありませんが、問題を解決する方法を明確に示すには十分なはずです。

于 2013-10-02T13:41:43.860 に答える