DHowett / go-plist

A pure Go Apple Property List transcoder
Other
416 stars 97 forks source link

XMLFormat: any(*time.Time) incorrectly encoded as string within structure #82

Open grahammiln opened 1 month ago

grahammiln commented 1 month ago

A *time.Time placed in an any typed field is incorrectly encoded as a string in an XML formatted plist

type Structure struct {
     UntypedPtr any
}
now := time.Date(2003, 2, 3, 9, 15, 30, 0, time.UTC)
s := &Structure{ UntypedPtr: &now }

Below is a test showing the current behaviour and expected output. The test shows that directly encoding the values works as expected. Only when the same values are embedded within a structure does the encoding bug appear:

func TestPlistDates(t *testing.T) {
    var (
        date       time.Time = time.Date(2003, 2, 3, 9, 15, 30, 0, time.UTC)
        datePtr              = &date
        untyped    any       = date
        untypedPtr any       = &date
    )

    expectDateEncoding := `<?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">
  <date>2003-02-03T09:15:30Z</date>
</plist>`

    if b, err := plist.MarshalIndent(date, plist.XMLFormat, "  "); err != nil {
        t.Error(err)
    } else if string(b) != expectDateEncoding {
        t.Errorf("expect:\n%s\ngot:\n%s", expectDateEncoding, string(b))
    }

    if b, err := plist.MarshalIndent(datePtr, plist.XMLFormat, "  "); err != nil {
        t.Error(err)
    } else if string(b) != expectDateEncoding {
        t.Errorf("expect:\n%s\ngot:\n%s", expectDateEncoding, string(b))
    }

    if b, err := plist.MarshalIndent(untyped, plist.XMLFormat, "  "); err != nil {
        t.Error(err)
    } else if string(b) != expectDateEncoding {
        t.Errorf("expect:\n%s\ngot:\n%s", expectDateEncoding, string(b))
    }

    if b, err := plist.MarshalIndent(untypedPtr, plist.XMLFormat, "  "); err != nil {
        t.Error(err)
    } else if string(b) != expectDateEncoding {
        t.Errorf("expect:\n%s\ngot:\n%s", expectDateEncoding, string(b))
    }

    type Structure struct {
        Date       time.Time
        DatePtr    *time.Time
        Untyped    any
        UntypedPtr any
    }
    s := &Structure{
        Date:       date,
        DatePtr:    datePtr,
        Untyped:    untyped,
        UntypedPtr: untypedPtr,
    }

    expectStructureEncoding := `<?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>Date</key>
    <date>2003-02-03T09:15:30Z</date>
    <key>DatePtr</key>
    <date>2003-02-03T09:15:30Z</date>
    <key>Untyped</key>
    <date>2003-02-03T09:15:30Z</date>
    <key>UntypedPtr</key>
    <date>2003-02-03T09:15:30Z</date>
  </dict>
</plist>`

    if b, err := plist.MarshalIndent(s, plist.XMLFormat, "  "); err != nil {
        t.Error(err)
    } else if string(b) != expectStructureEncoding {
        t.Errorf("expect:\n%s\ngot:\n%s", expectStructureEncoding, string(b))
    }
}

The current incorrect output is below. The final key/value pair should be a date, not a string:

<?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>Date</key>
    <date>2003-02-03T09:15:30Z</date>
    <key>DatePtr</key>
    <date>2003-02-03T09:15:30Z</date>
    <key>Untyped</key>
    <date>2003-02-03T09:15:30Z</date>
    <key>UntypedPtr</key>
    <string>2003-02-03T09:15:30Z</string>
  </dict>
</plist>

This is an esoteric bug that is unlikely to have a significant impact. I am documenting the problem here in case others stumble upon it.

DHowett commented 1 month ago

This is an excellent catch, thank you!