Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Directly vs indirectly nested structs

In the Go documentation for xml:Unmarshal there is an example that unmarshals this xml

    <Person>
        <FullName>Grace R. Emlin</FullName>
        <Company>Example Inc.</Company>
        <Email where="home">
            <Addr>[email protected]</Addr>
        </Email>
        <Email where='work'>
            <Addr>[email protected]</Addr>
        </Email>
        <Group>
            <Value>Friends</Value>
            <Value>Squash</Value>
        </Group>
        <City>Hanga Roa</City>
        <State>Easter Island</State>
    </Person>

using these structs

    type Address struct {
        City, State string
    }
    type Result struct {
        XMLName xml.Name `xml:"Person"`
        Name    string   `xml:"FullName"`
        Phone   string
        Email   []Email
        Groups  []string `xml:"Group>Value"`
        Address
    }

Note that Result contains a reference to the separately defined Address. Obviously, this code works.


When I try to unmarshal this xml

<C>
  <D>
    <E>Fred</E>
    <F>42</F>
  </D>
</C>

using these structs

type D struct { 
  E string
  F int
}
type C struct {   // Compiles OK but result empty.
  D 
} 

I get empty results {{ 0}}. However the struct below works OK producing {{Fred 42}}

type C struct {   // This works.
  D struct { 
    E string
    F int
  }
}

See Playground example.

Am I missing some subtle point about structs?

like image 879
RedGrittyBrick Avatar asked Feb 27 '17 15:02

RedGrittyBrick


1 Answers

When you do this:

type C struct {
    D
}

This is called embedding (D is an anonymous field or embedded field). You may think of this as if the fields (and methods) of the embedded type become part of the embedder type (they get promoted). So in this case it's "legal" to write C.E and C.F.

When you do:

type C struct {
    D struct {
      E string
      F int
    }
}

This is not embedding (or "nesting"). Here D is a "regular", named field of the C type. D is the name of the field, which is followed by an anonymous type literal, the type of the field. Here it is not legal to write C.E nor C.F, only C.D.E and C.D.F. And this is the proper mapping of the XML structure you try to unmarshal, hence this works (try it on the Go Playground).

Note that if you change the embedding to a regular field, it will also work (try it on the Go Playground):

type C struct {
    D D
}

Also note that you can skip the whole D wrapper struct if you specify the XML element paths in the field tags:

type C struct {
    E string `xml:"D>E"`
    F int    `xml:"D>F"`
}

Try it on the Go Playground.

like image 55
icza Avatar answered Oct 01 '22 23:10

icza