Golang Decode MusicXML

381 views Asked by At

I am writing a program that can read in a complete MusicXML file, edit it, and write a new file out. I am using xml.Decode to read the data into a struct for the MusicXML file, but when I run it nothing seems to happen. I tried printing the Decode object to the screen, but it printed a struct full of bytes.

I've looked over the xml package page and I can't seem to find any threads covering the Decode function. I tried using UnMarshall according to some of the pointers I found, but that didn't work (most of those threads were older, so maybe UnMarshall works a bit differently since Decode was implemented?).

Here's the input function:

func ImportXML(infile string) *xml.Decoder {
    // Reads music xml file to memory
    f, err := os.Open(infile)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error opening music xml file: %v\n", err)
        os.Exit(1)
    }
    defer f.Close()
    fmt.Println("\n\tReading musicXML file...")
    song := xml.NewDecoder(io.Reader(f))
    // must pass an interface pointer to Decode
    err = song.Decode(&Score{})
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error assigning musicXML file to struct: %v\n", err)
        os.Exit(1)
    }
    return song
}

Here's the first few structs (the rest follow the same format):

type Score struct {
    Work           Work           `xml:"work"`
    Identification Identification `xml:"identification"`
    Defaults       Defaults       `xml:"defaults"`
    Credit         Credit         `xml:"credit"`
    Partlist       []Scorepart    `xml:"score-part"`
    Part           []Part         `xml:"part"`
}

// Name and other idenfication
type Work struct {
    Number string `xml:"work-number"`
    Title  string `xml:"work-title"`
}

type Identification struct {
    Type     string     `xml:"type,attr"`
    Creator  string     `xml:"creator"`
    Software string     `xml:"software"`
    Date     string     `xml:"encoding-date"`
    Supports []Supports `xml:"supports"`
    Source   string     `xml:"source"`
}

I greatly appreciate any insight.

1

There are 1 answers

1
Adrian On

I think you've misunderstood the behavior of the decoder: it decodes XML into the object you pass to Decode:

song := xml.NewDecoder(io.Reader(f))
score := Score{}
err = song.Decode(&score)
// Decoded document is in score, *NOT* in song
return score

You're treating the decoder as if it will contain your document, but it's just a decoder. It decodes. To make it clearer in code, it should not be named song - it should be named, say, decoder or scoreDecoder or what have you. You almost certainly don't want to return an *xml.Decoder* from your function, but rather the decoded Score.