diff --git a/decode.go b/decode.go index 7fb9d84..a365a2e 100644 --- a/decode.go +++ b/decode.go @@ -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 } diff --git a/example_unmarshaler_test.go b/example_unmarshaler_test.go new file mode 100644 index 0000000..229ab83 --- /dev/null +++ b/example_unmarshaler_test.go @@ -0,0 +1,74 @@ +package plist_test + +import ( + "fmt" + + "github.com/groob/plist" +) + +const data = ` + + + + typekey + A + typeAkey + VALUE-A + +` + +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 +}