Josh Bleecher Snyder
Josh Bleecher Snyder

Reputation: 8442

DRY struct tags in Go

I'm parsing XML, and at most every level of the document, there's a description.

Here's an toy example:

<obj>
    <description>outer object</description>
    <subobjA>
        <description>first kind of subobject</description>
        <foo>some goop</foo>
    </subobjA>
    <subobjB>
        <description>second kind of subobject</description>
        <bar>some other goop</bar>
    </subobjB>
</obj>

This means that every struct involved has an identical Description member, with an identical tag `xml:"description,omitempty"`.

Here's functioning code: http://play.golang.org/p/1-co6Qcm8d

I'd rather the Description tags be DRY. The obvious thing to want to do is something like:

type Description string `xml:"description,omitempty"`

and then use the type Description throughout. However, only struct members can have tags. See http://play.golang.org/p/p83UrhrN4u for what I want to write; it doesn't compile.

One could create a Description struct and embed it repeatedly, but that adds a layer of indirection when accessing.

Is there another way to go about this?

Upvotes: 5

Views: 246

Answers (1)

nemo
nemo

Reputation: 57757

Embedding a re-factored Description struct (as you already suggested) is the way to go:

(Playground)

type describable struct{
    Description string `xml:"description"`
}

type subobjA struct {
    describable
    XMLName     xml.Name `xml:"subobjA"`
}

type subobjB struct {
    describable
    XMLName     xml.Name `xml:"subobjB"`
}

type obj struct {
    XMLName     xml.Name `xml:"obj"`
    A           subobjA
    B           subobjB
}

The mentioned layer of indirection does not exist. To cite the spec on that topic:

A field or method f of an anonymous field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.

So you can do this:

err := xml.Unmarshal([]byte(sampleXml), &sampleObj)
fmt.Println(sampleObj.Description)
fmt.Println(sampleObj.A.Description)

sampleObj.describable.Description is promoted to be sampleObj.Description, so no further layer of indirection here.

Upvotes: 4

Related Questions