Home > Software engineering >  Golang encoding xml doesn't map empty value to nil
Golang encoding xml doesn't map empty value to nil

Time:05-23

I would like to map empty value from tag to nill value in my object e.g. I have this xml

<Address>
   <city>city</city>
   <streetNumber></streetNumber>
</Address>

a my type is

type Address struct{
   city string `xml:"city"`
   streetNumber *int `xml:"streetNumber"`
}

and in this case streetNumber is 0 but when I delete tag streetNumber from xml value is nil Is it possible to map streetNumber tag to nil when it is empty?

CodePudding user response:

You may want to look at making streetNumber its own type with custom marshaling like what they're doing here with time format: https://www.programming-books.io/essential/go/custom-xml-marshaling-aa8105fe264b4b198647cbc718480ba1

I'd lean towards the int Valid bool combo but you could do an int pointer.

Edit: updated to include a potential implementation of marshal/unmarshal.

type Address struct {
    City         string       `xml:"city"`
    StreetNumber StreetNumber `xml:"streetNumber"`
}

type StreetNumber struct {
    Value int
    Valid bool
}

func (s StreetNumber) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    // If the address is null/unset/invalid, we serialize an empty string as the value.
    v := ""
    if s.Valid {
        // The address has a value so we convert the integer to string.
        v = strconv.Itoa(s.Value)
    }

    // Encode the string `v` to the XML element.
    return e.EncodeElement(v, start)
}

func (s *StreetNumber) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    // Assume the value is invalid so we can just shortcut out.
    s.Valid = false
    s.Value = 0;
    
    var v string
    if err := d.DecodeElement(&v, &start); err != nil {
        // Forward the error if it failed to decode the raw XML
        return err
    }

    if v == "" {
        // The value was empty, but this is just the 3rd null state, not an error.
        return nil
    }

    i, err := strconv.Atoi(v)
    if err != nil {
        // The element value was not an integer so this is an invalid state -- forward the error.
        return err
    }

    // The street number was a valid integer.
    s.Valid = true
    s.Value = i

    return nil
}
  • Related