aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/xml/read.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2016-10-12 22:58:47 -0400
committerRuss Cox <rsc@golang.org>2016-10-18 12:59:41 +0000
commitc1a1328c5f004c62b8c08faf0d0d2845e0be5d37 (patch)
tree3ee7682bbe45110d0040e81231e704c713796ec0 /src/encoding/xml/read.go
parent0794dce07239fad5845b9c77b50d084f19f7278f (diff)
downloadgo-c1a1328c5f004c62b8c08faf0d0d2845e0be5d37.tar.gz
go-c1a1328c5f004c62b8c08faf0d0d2845e0be5d37.zip
encoding/xml: add wildcard support for collecting all attributes
- Like ",any" for elements, add ",any,attr" for attributes to allow a mop-up field that gets any otherwise unmapped attributes. - Map attributes to fields of type slice by extending the slice, just like for elements. - Allow storing an attribute into an xml.Attr directly, to provide a way to record the name. Combined, these three independent features allow AllAttrs []Attr `xml:",any,attr"` to collect all attributes not otherwise spoken for in a particular struct. Tests based on CL 16292 by Charles Weill. Fixes #3633. Change-Id: I2d75817f17ca8752d7df188080a407836af92611 Reviewed-on: https://go-review.googlesource.com/30946 Reviewed-by: Quentin Smith <quentin@golang.org>
Diffstat (limited to 'src/encoding/xml/read.go')
-rw-r--r--src/encoding/xml/read.go81
1 files changed, 60 insertions, 21 deletions
diff --git a/src/encoding/xml/read.go b/src/encoding/xml/read.go
index 53c15a2840..ba62366560 100644
--- a/src/encoding/xml/read.go
+++ b/src/encoding/xml/read.go
@@ -52,6 +52,11 @@ import (
// the explicit name in a struct field tag of the form "name,attr",
// Unmarshal records the attribute value in that field.
//
+// * If the XML element has an attribute not handled by the previous
+// rule and the struct has a field with an associated tag containing
+// ",any,attr", Unmarshal records the attribute value in the first
+// such field.
+//
// * If the XML element contains character data, that data is
// accumulated in the first struct field that has tag ",chardata".
// The struct field may have type []byte or string.
@@ -94,8 +99,12 @@ import (
// Unmarshal maps an attribute value to a string or []byte by saving
// the value in the string or slice.
//
-// Unmarshal maps an XML element to a slice by extending the length of
-// the slice and mapping the element to the newly created value.
+// Unmarshal maps an attribute value to an Attr by saving the attribute,
+// including its name, in the Attr.
+//
+// Unmarshal maps an XML element or attribute value to a slice by
+// extending the length of the slice and mapping the element or attribute
+// to the newly created value.
//
// Unmarshal maps an XML element or attribute value to a bool by
// setting it to the boolean value represented by the string.
@@ -256,10 +265,31 @@ func (p *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error {
return pv.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value))
}
}
+
+ if val.Type().Kind() == reflect.Slice && val.Type().Elem().Kind() != reflect.Uint8 {
+ // Slice of element values.
+ // Grow slice.
+ n := val.Len()
+ val.Set(reflect.Append(val, reflect.Zero(val.Type().Elem())))
+
+ // Recur to read element into slice.
+ if err := p.unmarshalAttr(val.Index(n), attr); err != nil {
+ val.SetLen(n)
+ return err
+ }
+ return nil
+ }
+
+ if val.Type() == attrType {
+ val.Set(reflect.ValueOf(attr))
+ return nil
+ }
+
return copyValue(val, []byte(attr.Value))
}
var (
+ attrType = reflect.TypeOf(Attr{})
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
unmarshalerAttrType = reflect.TypeOf((*UnmarshalerAttr)(nil)).Elem()
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
@@ -356,16 +386,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
// Slice of element values.
// Grow slice.
n := v.Len()
- if n >= v.Cap() {
- ncap := 2 * n
- if ncap < 4 {
- ncap = 4
- }
- new := reflect.MakeSlice(typ, n, ncap)
- reflect.Copy(new, v)
- v.Set(new)
- }
- v.SetLen(n + 1)
+ v.Set(reflect.Append(val, reflect.Zero(v.Type().Elem())))
// Recur to read element into slice.
if err := p.unmarshal(v.Index(n), start); err != nil {
@@ -412,22 +433,40 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
}
// Assign attributes.
- // Also, determine whether we need to save character data or comments.
- for i := range tinfo.fields {
- finfo := &tinfo.fields[i]
- switch finfo.flags & fMode {
- case fAttr:
- strv := finfo.value(sv)
- // Look for attribute.
- for _, a := range start.Attr {
+ for _, a := range start.Attr {
+ handled := false
+ any := -1
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ switch finfo.flags & fMode {
+ case fAttr:
+ strv := finfo.value(sv)
if a.Name.Local == finfo.name && (finfo.xmlns == "" || finfo.xmlns == a.Name.Space) {
if err := p.unmarshalAttr(strv, a); err != nil {
return err
}
- break
+ handled = true
+ }
+
+ case fAny | fAttr:
+ if any == -1 {
+ any = i
}
}
+ }
+ if !handled && any >= 0 {
+ finfo := &tinfo.fields[any]
+ strv := finfo.value(sv)
+ if err := p.unmarshalAttr(strv, a); err != nil {
+ return err
+ }
+ }
+ }
+ // Determine whether we need to save character data or comments.
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ switch finfo.flags & fMode {
case fCDATA, fCharData:
if !saveData.IsValid() {
saveData = finfo.value(sv)