Alex B
Alex B

Reputation: 84852

Go XML parsing: set missing attributes to "true"

The snippet below,

package main
import (
    "encoding/xml"
    "fmt"
)

func main() {
    var r struct {
        Item []struct {
            Value string `xml:"value,attr"`
            Flag bool `xml:"flag,attr"`
        } `xml:"item"`
    }
    xml.Unmarshal([]byte(`
        <result>
            <item value="1" flag="false" />
            <item value="2" flag="true" />
            <item value="3" />
        </result>`,
    ), &r)
    fmt.Printf("%+v\n", r)
}

Will print the following result:

{Item:[{Value:1 Flag:false} {Value:2 Flag:true} {Value:3 Flag:false}]}

In some elements, flag attribute will be missing (e.g. item 3 above), but I want it to take the default value of true, instead than false.

  1. I cannot assign it in the constructor, because I don't know the number of elements in the array upfront.
  2. I cannot use a custom type that implements UnmarshalerAttr and assign during unmarshaling because if the attribute is missing, UnmarshalXMLAttr is not run.
  3. I could make it a pointer, and then check if nil, then true, but that's just gross and error-prone.

How would I go about doing this?

Upvotes: 3

Views: 2907

Answers (1)

user142162
user142162

Reputation:

You're correct in that you cannot use UnmarshalerAttr for this. Instead, ResultItem can implement Unmarshaler which will allow you to set default attribute values:

package main
import (
    "encoding/xml"
    "fmt"
)

type ResultItem struct {
  Value string `xml:"value,attr"`
  Flag bool `xml:"flag,attr"`
}

func (ri *ResultItem) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  type resultItem ResultItem // new type to prevent recursion
  item := resultItem{
    Flag: true,
  }
  if err := d.DecodeElement(&item, &start); err != nil {
    return err
  }
  *ri = (ResultItem)(item)
  return nil
}

func main() {
    var r struct {
      Item[] ResultItem `xml:"item"`
    }
    xml.Unmarshal([]byte(`
        <result x="ASDASD">
            <item value="1" flag="false" />
            <item value="2" flag="true" />
            <item value="3" />
        </result>`,
    ), &r)
    fmt.Printf("%+v\n", r)
}

Upvotes: 5

Related Questions