Skip to content

Commit

Permalink
document and add example using unmarshaler interface (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
jessepeterson authored Nov 27, 2024
1 parent 16fcafd commit 0993743
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 0 deletions.
6 changes: 6 additions & 0 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import (
// MarshalFunc is a function used to Unmarshal custom plist types.
type MarshalFunc func(interface{}) error

// Unmarshaler is the interface implemented by types that can unmarshal
// themselves from property list objects. The UnmarshalPlist method
// receives a function that may be called to unmarshal the original
// property list value into a field or variable.
//
// It is safe to call the unmarshal function more than once.
type Unmarshaler interface {
UnmarshalPlist(f func(interface{}) error) error
}
Expand Down
74 changes: 74 additions & 0 deletions example_unmarshaler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package plist_test

import (
"fmt"

"github.com/groob/plist"
)

const data = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>typekey</key>
<string>A</string>
<key>typeAkey</key>
<string>VALUE-A</string>
</dict>
</plist>`

type TypeDecider struct {
ActualType interface{} `plist:"-"`
}

type TypeA struct {
TypeAKey string `plist:"typeAkey"`
}

type TypeB struct {
TypeBKey string `plist:"typeBkey"`
}

func (t *TypeDecider) UnmarshalPlist(f func(interface{}) error) error {
// stub struct for decoding a single key to tell which
// specific type we should umarshal into
typeKey := &struct {
TypeKey string `plist:"typekey"`
}{}
if err := f(typeKey); err != nil {
return err
}

// switch using the decoded value to determine the correct type
switch typeKey.TypeKey {
case "A":
t.ActualType = new(TypeA)
case "B":
t.ActualType = new(TypeB)
case "":
return fmt.Errorf("empty typekey (or wrong input data)")
default:
return fmt.Errorf("unknown typekey: %s", typeKey.TypeKey)
}

// decode into the actual type
return f(t.ActualType)
}

// ExampleUnmarshaler demonstrates using structs that use the Unmarshaler interface.
func ExampleUnmarshaler() {
decider := new(TypeDecider)
if err := plist.Unmarshal([]byte(data), decider); err != nil {
fmt.Println(err)
return
}

typeA, ok := decider.ActualType.(*TypeA)
if !ok {
fmt.Println("actual type is not TypeA")
return
}

fmt.Println(typeA.TypeAKey)
// Output: VALUE-A
}

0 comments on commit 0993743

Please sign in to comment.