mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
encoding/xml: fix xmlns= behavior
When an xmlns="..." attribute was explicitly generated, it was being ignored because the name space on the attribute was assumed to have been explicitly set (to the empty name space) and it's not possible to have an element in the empty name space when there is a non-empty name space set. We fix this by recording when a default name space has been explicitly set and setting the name space of the element to that so printer.defineNS can do its work correctly. We do not attempt to add our own xmlns="..." attribute when one is explicitly set. We also add tests for EncodeElement, as that's the only way to attain coverage of some of the changed behaviour. Some other test coverage is also increased, although more work remains to be done in this area. This change was jointly developed with Martin Hilton (mhilton on github). Fixes #11431. Change-Id: I7b85e06eea5b18b2c15ec16dcbd92a8e1d6a9a4e Reviewed-on: https://go-review.googlesource.com/11635 Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
ab9c25f2de
commit
bb7e665687
3 changed files with 269 additions and 78 deletions
|
|
@ -578,12 +578,14 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
|
|||
// 3. type name
|
||||
var start StartElement
|
||||
|
||||
// Historic behaviour: elements use the default name space
|
||||
// they are contained in by default.
|
||||
start.Name.Space = p.defaultNS
|
||||
// explicitNS records whether the element's name
|
||||
// space has been explicitly set (for example an
|
||||
// and XMLName field).
|
||||
explicitNS := false
|
||||
|
||||
if startTemplate != nil {
|
||||
start.Name = startTemplate.Name
|
||||
explicitNS = true
|
||||
start.Attr = append(start.Attr, startTemplate.Attr...)
|
||||
} else if tinfo.xmlname != nil {
|
||||
xmlname := tinfo.xmlname
|
||||
|
|
@ -592,11 +594,13 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
|
|||
} else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
|
||||
start.Name = v
|
||||
}
|
||||
explicitNS = true
|
||||
}
|
||||
if start.Name.Local == "" && finfo != nil {
|
||||
start.Name.Local = finfo.name
|
||||
if finfo.xmlns != "" {
|
||||
start.Name.Space = finfo.xmlns
|
||||
explicitNS = true
|
||||
}
|
||||
}
|
||||
if start.Name.Local == "" {
|
||||
|
|
@ -606,9 +610,12 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
|
|||
}
|
||||
start.Name.Local = name
|
||||
}
|
||||
// Historic behaviour: an element that's in a namespace sets
|
||||
// the default namespace for all elements contained within it.
|
||||
start.setDefaultNamespace()
|
||||
|
||||
// defaultNS records the default name space as set by a xmlns="..."
|
||||
// attribute. We don't set p.defaultNS because we want to let
|
||||
// the attribute writing code (in p.defineNS) be solely responsible
|
||||
// for maintaining that.
|
||||
defaultNS := p.defaultNS
|
||||
|
||||
// Attributes
|
||||
for i := range tinfo.fields {
|
||||
|
|
@ -616,81 +623,26 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
|
|||
if finfo.flags&fAttr == 0 {
|
||||
continue
|
||||
}
|
||||
fv := finfo.value(val)
|
||||
name := Name{Space: finfo.xmlns, Local: finfo.name}
|
||||
|
||||
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
|
||||
continue
|
||||
}
|
||||
|
||||
if fv.Kind() == reflect.Interface && fv.IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
|
||||
attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if attr.Name.Local != "" {
|
||||
start.Attr = append(start.Attr, attr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if fv.CanAddr() {
|
||||
pv := fv.Addr()
|
||||
if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
|
||||
attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if attr.Name.Local != "" {
|
||||
start.Attr = append(start.Attr, attr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
|
||||
text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
start.Attr = append(start.Attr, Attr{name, string(text)})
|
||||
continue
|
||||
}
|
||||
|
||||
if fv.CanAddr() {
|
||||
pv := fv.Addr()
|
||||
if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
|
||||
text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
start.Attr = append(start.Attr, Attr{name, string(text)})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Dereference or skip nil pointer, interface values.
|
||||
switch fv.Kind() {
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
if fv.IsNil() {
|
||||
continue
|
||||
}
|
||||
fv = fv.Elem()
|
||||
}
|
||||
|
||||
s, b, err := p.marshalSimple(fv.Type(), fv)
|
||||
attr, add, err := p.fieldAttr(finfo, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if b != nil {
|
||||
s = string(b)
|
||||
if !add {
|
||||
continue
|
||||
}
|
||||
start.Attr = append(start.Attr, attr)
|
||||
if attr.Name.Space == "" && attr.Name.Local == "xmlns" {
|
||||
defaultNS = attr.Value
|
||||
}
|
||||
start.Attr = append(start.Attr, Attr{name, s})
|
||||
}
|
||||
if !explicitNS {
|
||||
// Historic behavior: elements use the default name space
|
||||
// they are contained in by default.
|
||||
start.Name.Space = defaultNS
|
||||
}
|
||||
// Historic behaviour: an element that's in a namespace sets
|
||||
// the default namespace for all elements contained within it.
|
||||
start.setDefaultNamespace()
|
||||
|
||||
if err := p.writeStart(&start); err != nil {
|
||||
return err
|
||||
|
|
@ -719,6 +671,64 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
|
|||
return p.cachedWriteError()
|
||||
}
|
||||
|
||||
// fieldAttr returns the attribute of the given field and
|
||||
// whether it should actually be added as an attribute;
|
||||
// val holds the value containing the field.
|
||||
func (p *printer) fieldAttr(finfo *fieldInfo, val reflect.Value) (Attr, bool, error) {
|
||||
fv := finfo.value(val)
|
||||
name := Name{Space: finfo.xmlns, Local: finfo.name}
|
||||
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
|
||||
return Attr{}, false, nil
|
||||
}
|
||||
if fv.Kind() == reflect.Interface && fv.IsNil() {
|
||||
return Attr{}, false, nil
|
||||
}
|
||||
if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
|
||||
attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
|
||||
return attr, attr.Name.Local != "", err
|
||||
}
|
||||
if fv.CanAddr() {
|
||||
pv := fv.Addr()
|
||||
if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
|
||||
attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
|
||||
return attr, attr.Name.Local != "", err
|
||||
}
|
||||
}
|
||||
if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
|
||||
text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return Attr{}, false, err
|
||||
}
|
||||
return Attr{name, string(text)}, true, nil
|
||||
}
|
||||
if fv.CanAddr() {
|
||||
pv := fv.Addr()
|
||||
if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
|
||||
text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return Attr{}, false, err
|
||||
}
|
||||
return Attr{name, string(text)}, true, nil
|
||||
}
|
||||
}
|
||||
// Dereference or skip nil pointer, interface values.
|
||||
switch fv.Kind() {
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
if fv.IsNil() {
|
||||
return Attr{}, false, nil
|
||||
}
|
||||
fv = fv.Elem()
|
||||
}
|
||||
s, b, err := p.marshalSimple(fv.Type(), fv)
|
||||
if err != nil {
|
||||
return Attr{}, false, err
|
||||
}
|
||||
if b != nil {
|
||||
s = string(b)
|
||||
}
|
||||
return Attr{name, s}, true, nil
|
||||
}
|
||||
|
||||
// defaultStart returns the default start element to use,
|
||||
// given the reflect type, field info, and start template.
|
||||
func (p *printer) defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue