Commit 9242a90a authored by Gustavo Niemeyer's avatar Gustavo Niemeyer

encoding/xml: handle anonymous pointer fields

This CL makes

    type T struct { *U }

behave in a similar way to:

    type T struct { U }

Fixes #3108.

R=golang-dev, rsc, gustavo
CC=golang-dev
https://golang.org/cl/5694044
parent 283a3dda
...@@ -57,8 +57,8 @@ const ( ...@@ -57,8 +57,8 @@ const (
// if the field value is empty. The empty values are false, 0, any // if the field value is empty. The empty values are false, 0, any
// nil pointer or interface value, and any array, slice, map, or // nil pointer or interface value, and any array, slice, map, or
// string of length zero. // string of length zero.
// - a non-pointer anonymous struct field is handled as if the // - an anonymous struct field is handled as if the fields of its
// fields of its value were part of the outer struct. // value were part of the outer struct.
// //
// If a field uses a tag "a>b>c", then the element c will be nested inside // If a field uses a tag "a>b>c", then the element c will be nested inside
// parent elements a and b. Fields that appear next to each other that name // parent elements a and b. Fields that appear next to each other that name
...@@ -164,7 +164,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { ...@@ -164,7 +164,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
xmlname := tinfo.xmlname xmlname := tinfo.xmlname
if xmlname.name != "" { if xmlname.name != "" {
xmlns, name = xmlname.xmlns, xmlname.name xmlns, name = xmlname.xmlns, xmlname.name
} else if v, ok := val.FieldByIndex(xmlname.idx).Interface().(Name); ok && v.Local != "" { } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
xmlns, name = v.Space, v.Local xmlns, name = v.Space, v.Local
} }
} }
...@@ -195,7 +195,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { ...@@ -195,7 +195,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
if finfo.flags&fAttr == 0 { if finfo.flags&fAttr == 0 {
continue continue
} }
fv := val.FieldByIndex(finfo.idx) fv := finfo.value(val)
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) { if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
continue continue
} }
...@@ -276,7 +276,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { ...@@ -276,7 +276,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
if finfo.flags&(fAttr|fAny) != 0 { if finfo.flags&(fAttr|fAny) != 0 {
continue continue
} }
vf := val.FieldByIndex(finfo.idx) vf := finfo.value(val)
switch finfo.flags & fMode { switch finfo.flags & fMode {
case fCharData: case fCharData:
switch vf.Kind() { switch vf.Kind() {
......
...@@ -108,7 +108,7 @@ type EmbedA struct { ...@@ -108,7 +108,7 @@ type EmbedA struct {
type EmbedB struct { type EmbedB struct {
FieldB string FieldB string
EmbedC *EmbedC
} }
type EmbedC struct { type EmbedC struct {
...@@ -493,7 +493,7 @@ var marshalTests = []struct { ...@@ -493,7 +493,7 @@ var marshalTests = []struct {
}, },
EmbedB: EmbedB{ EmbedB: EmbedB{
FieldB: "A.B.B", FieldB: "A.B.B",
EmbedC: EmbedC{ EmbedC: &EmbedC{
FieldA1: "A.B.C.A1", FieldA1: "A.B.C.A1",
FieldA2: "A.B.C.A2", FieldA2: "A.B.C.A2",
FieldB: "", // Shadowed by A.B.B FieldB: "", // Shadowed by A.B.B
......
...@@ -81,8 +81,8 @@ import ( ...@@ -81,8 +81,8 @@ import (
// of the above rules and the struct has a field with tag ",any", // of the above rules and the struct has a field with tag ",any",
// unmarshal maps the sub-element to that struct field. // unmarshal maps the sub-element to that struct field.
// //
// * A non-pointer anonymous struct field is handled as if the // * An anonymous struct field is handled as if the fields of its
// fields of its value were part of the outer struct. // value were part of the outer struct.
// //
// * A struct field with tag "-" is never unmarshalled into. // * A struct field with tag "-" is never unmarshalled into.
// //
...@@ -248,7 +248,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { ...@@ -248,7 +248,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
} }
return UnmarshalError(e) return UnmarshalError(e)
} }
fv := sv.FieldByIndex(finfo.idx) fv := finfo.value(sv)
if _, ok := fv.Interface().(Name); ok { if _, ok := fv.Interface().(Name); ok {
fv.Set(reflect.ValueOf(start.Name)) fv.Set(reflect.ValueOf(start.Name))
} }
...@@ -260,7 +260,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { ...@@ -260,7 +260,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
finfo := &tinfo.fields[i] finfo := &tinfo.fields[i]
switch finfo.flags & fMode { switch finfo.flags & fMode {
case fAttr: case fAttr:
strv := sv.FieldByIndex(finfo.idx) strv := finfo.value(sv)
// Look for attribute. // Look for attribute.
for _, a := range start.Attr { for _, a := range start.Attr {
if a.Name.Local == finfo.name { if a.Name.Local == finfo.name {
...@@ -271,22 +271,22 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { ...@@ -271,22 +271,22 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
case fCharData: case fCharData:
if !saveData.IsValid() { if !saveData.IsValid() {
saveData = sv.FieldByIndex(finfo.idx) saveData = finfo.value(sv)
} }
case fComment: case fComment:
if !saveComment.IsValid() { if !saveComment.IsValid() {
saveComment = sv.FieldByIndex(finfo.idx) saveComment = finfo.value(sv)
} }
case fAny: case fAny:
if !saveAny.IsValid() { if !saveAny.IsValid() {
saveAny = sv.FieldByIndex(finfo.idx) saveAny = finfo.value(sv)
} }
case fInnerXml: case fInnerXml:
if !saveXML.IsValid() { if !saveXML.IsValid() {
saveXML = sv.FieldByIndex(finfo.idx) saveXML = finfo.value(sv)
if p.saved == nil { if p.saved == nil {
saveXMLIndex = 0 saveXMLIndex = 0
p.saved = new(bytes.Buffer) p.saved = new(bytes.Buffer)
...@@ -461,7 +461,7 @@ Loop: ...@@ -461,7 +461,7 @@ Loop:
} }
if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local { if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
// It's a perfect match, unmarshal the field. // It's a perfect match, unmarshal the field.
return true, p.unmarshal(sv.FieldByIndex(finfo.idx), start) return true, p.unmarshal(finfo.value(sv), start)
} }
if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local { if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local {
// It's a prefix for the field. Break and recurse // It's a prefix for the field. Break and recurse
......
...@@ -66,10 +66,14 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) { ...@@ -66,10 +66,14 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
// For embedded structs, embed its fields. // For embedded structs, embed its fields.
if f.Anonymous { if f.Anonymous {
if f.Type.Kind() != reflect.Struct { t := f.Type
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
continue continue
} }
inner, err := getTypeInfo(f.Type) inner, err := getTypeInfo(t)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -327,3 +331,22 @@ type TagPathError struct { ...@@ -327,3 +331,22 @@ type TagPathError struct {
func (e *TagPathError) Error() string { func (e *TagPathError) Error() string {
return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2) return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
} }
// value returns v's field value corresponding to finfo.
// It's equivalent to v.FieldByIndex(finfo.idx), but initializes
// and dereferences pointers as necessary.
func (finfo *fieldInfo) value(v reflect.Value) reflect.Value {
for i, x := range finfo.idx {
if i > 0 {
t := v.Type()
if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
}
}
v = v.Field(x)
}
return v
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment