Custom XML marshaling
suggest changeWriting custom XML marshalling
Sometimes a type doesn’t have an obvious mapping to JSON.
How would you serialize time.Time
? There are so many possibilities.
Go provides a default XML mapping for time.Time
. We can implement custom marshaller for user-defined types like structs.
For existing types we can define a new (but compatible) type.
Here’s a custom marshalling and unmarshalling for time.Time
that only serializes year/month/date part:
type Event struct {
What string
When time.Time
}
e := Event{
What: "earthquake",
When: time.Now(),
}
d, err := xml.Marshal(&e)
if err != nil {
log.Fatalf("json.MarshalIndent failed with '%s'\n", err)
}
fmt.Printf("Standard time JSON: %s\n", string(d))
type customTime time.Time
const customTimeFormat = `2006-02-01`
func (ct customTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
t := time.Time(ct)
v := t.Format(customTimeFormat)
return e.EncodeElement(v, start)
}
func (ct *customTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var s string
err := d.DecodeElement(&s, &start)
if err != nil {
return err
}
t, err := time.Parse(customTimeFormat, s)
if err != nil {
return err
}
*ct = customTime(t)
return nil
}
type Event2 struct {
What string
When customTime
}
e := Event2{
What: "earthquake",
When: customTime(time.Now()),
}
d, err := xml.Marshal(&e)
if err != nil {
log.Fatalf("xml.Marshal failed with '%s'\n", err)
}
fmt.Printf("\nCustom time XML: %s\n", string(d))
var decoded Event2
err = xml.Unmarshal(d, &decoded)
if err != nil {
log.Fatalf("xml.Unmarshal failed with '%s'\n", err)
}
t := time.Time(decoded.When)
fmt.Printf("Decoded custom time: %s\n", t.Format(customTimeFormat))
notCustom()
custom()
Standard time JSON: <Event><What>earthquake</What><When>2020-07-12T07:34:51.565806306Z</When></Event>
Custom time XML: <Event2><What>earthquake</What><When>2020-12-07</When></Event2>
Decoded custom time: 2020-12-07
Notice that receiver type of UnmashalXML
is a pointer to the type.
This is necessary for changes to persist outside the function itself.
Custom marshaling behind the scenes
How does custom marshaling works?
Package XML defines 2 interfaces: Marshaler
and Unmarshaler
.
type Marshaler interface {
MarshalXML(e *Encoder, start StartElement) error
}
type Unmarshaler interface {
UnmarshalXML(d *Decoder, start StartElement) error
}
By implementing those functions we make our type conform to Marshaler
or Unmarshaler
interface.
XML encoder / decoder checks if the value being encoded conforms to those interfaces and will call those functions instead of executing default logic.