DHowett / go-plist

A pure Go Apple Property List transcoder
Other
414 stars 96 forks source link

Can't decode plist file that contains <array> #68

Closed freekdegreef closed 3 years ago

freekdegreef commented 3 years ago

I'm trying to decode a plist file that has a structure that's like:

<plist version="1.0">
<array>
    <dict>
        <key> keyname1</key>
        <string>data</string>
        <key> keyname2</key>
        <string>data</string>
      </dict>
</array>
</plist

I keep getting the message "plist: type mismatch: tried to decode plist type array' into value of typemain.credentialsFile'" when running the code.

My struct looks like this:

type credentialsFile struct {
    Keyname1      string `plist:"keyname1"`
    Keyname2      string `plist:"keyname2"`
}

And the main code block:

func main() {
    var data credentialsFile
    buf, _ := os.Open("credentials_example.xml")
    defer buf.Close()
    decoder := plist.NewDecoder(buf)
    err := decoder.Decode(&data)
    if err != nil {
        fmt.Println(err)
    }
    data.Keyname1 = "test"
}

When I remove the <array> tags from the XML everything is fine. Can anyone tell me what I'm missing here?

DHowett commented 3 years ago

Sure!

So, a document with an array as the root element must be deserialized into an array or slice type -- for a couple reasons. Foremost is that there's no direct conversion between a []T and a T. Equally important is that there actually could be multiple elements¹ in an <array> that you'd need to handle.

If you change the type of data to []credentialsFile, it works fine:

type credentialsFile struct {
    Keyname1 string `plist:"keyname1"`
    Keyname2 string `plist:"keyname2"`
}

func main() {
    var data []credentialsFile
    buf, _ := os.Open("credentials_example.xml")
    defer buf.Close()
    decoder := plist.NewDecoder(buf)
    err := decoder.Decode(&data)
    if err != nil {
        fmt.Println(err)
    }
    data[0].Keyname1 = "test"

    fmt.Printf("%+v", data)
}
% go run test.go
[{Keyname1:test Keyname2:data}]

¹ Take this document as an example:

<array>
    <dict>
        <key>keyname1</key> <string>data1</string>
        <key>keyname2</key> <string>data1</string>
    </dict>
    <dict>
        <key>keyname1</key> <string>data2</string>
        <key>keyname2</key> <string>data2</string>
    </dict>
</array>
freekdegreef commented 3 years ago

Thanks so much! I was already looking in this direction but even looking at other implementations of your library on github I couldn't find this exact use case.

DHowett commented 3 years ago

Happy to help! I’ll close this out for now, but don’t hesitate to comment or file a new issue if you’ve got any other questions/thoughts/etc.