Skip to content

Commit bb7e665

Browse files
committed
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 <[email protected]>
1 parent ab9c25f commit bb7e665

File tree

3 files changed

+269
-78
lines changed

3 files changed

+269
-78
lines changed

src/encoding/xml/marshal.go

+86-76
Original file line numberDiff line numberDiff line change
@@ -578,12 +578,14 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
578578
// 3. type name
579579
var start StartElement
580580

581-
// Historic behaviour: elements use the default name space
582-
// they are contained in by default.
583-
start.Name.Space = p.defaultNS
581+
// explicitNS records whether the element's name
582+
// space has been explicitly set (for example an
583+
// and XMLName field).
584+
explicitNS := false
584585

585586
if startTemplate != nil {
586587
start.Name = startTemplate.Name
588+
explicitNS = true
587589
start.Attr = append(start.Attr, startTemplate.Attr...)
588590
} else if tinfo.xmlname != nil {
589591
xmlname := tinfo.xmlname
@@ -592,11 +594,13 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
592594
} else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
593595
start.Name = v
594596
}
597+
explicitNS = true
595598
}
596599
if start.Name.Local == "" && finfo != nil {
597600
start.Name.Local = finfo.name
598601
if finfo.xmlns != "" {
599602
start.Name.Space = finfo.xmlns
603+
explicitNS = true
600604
}
601605
}
602606
if start.Name.Local == "" {
@@ -606,91 +610,39 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
606610
}
607611
start.Name.Local = name
608612
}
609-
// Historic behaviour: an element that's in a namespace sets
610-
// the default namespace for all elements contained within it.
611-
start.setDefaultNamespace()
613+
614+
// defaultNS records the default name space as set by a xmlns="..."
615+
// attribute. We don't set p.defaultNS because we want to let
616+
// the attribute writing code (in p.defineNS) be solely responsible
617+
// for maintaining that.
618+
defaultNS := p.defaultNS
612619

613620
// Attributes
614621
for i := range tinfo.fields {
615622
finfo := &tinfo.fields[i]
616623
if finfo.flags&fAttr == 0 {
617624
continue
618625
}
619-
fv := finfo.value(val)
620-
name := Name{Space: finfo.xmlns, Local: finfo.name}
621-
622-
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
623-
continue
624-
}
625-
626-
if fv.Kind() == reflect.Interface && fv.IsNil() {
627-
continue
628-
}
629-
630-
if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
631-
attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
632-
if err != nil {
633-
return err
634-
}
635-
if attr.Name.Local != "" {
636-
start.Attr = append(start.Attr, attr)
637-
}
638-
continue
639-
}
640-
641-
if fv.CanAddr() {
642-
pv := fv.Addr()
643-
if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
644-
attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
645-
if err != nil {
646-
return err
647-
}
648-
if attr.Name.Local != "" {
649-
start.Attr = append(start.Attr, attr)
650-
}
651-
continue
652-
}
653-
}
654-
655-
if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
656-
text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
657-
if err != nil {
658-
return err
659-
}
660-
start.Attr = append(start.Attr, Attr{name, string(text)})
661-
continue
662-
}
663-
664-
if fv.CanAddr() {
665-
pv := fv.Addr()
666-
if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
667-
text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
668-
if err != nil {
669-
return err
670-
}
671-
start.Attr = append(start.Attr, Attr{name, string(text)})
672-
continue
673-
}
674-
}
675-
676-
// Dereference or skip nil pointer, interface values.
677-
switch fv.Kind() {
678-
case reflect.Ptr, reflect.Interface:
679-
if fv.IsNil() {
680-
continue
681-
}
682-
fv = fv.Elem()
683-
}
684-
685-
s, b, err := p.marshalSimple(fv.Type(), fv)
626+
attr, add, err := p.fieldAttr(finfo, val)
686627
if err != nil {
687628
return err
688629
}
689-
if b != nil {
690-
s = string(b)
630+
if !add {
631+
continue
632+
}
633+
start.Attr = append(start.Attr, attr)
634+
if attr.Name.Space == "" && attr.Name.Local == "xmlns" {
635+
defaultNS = attr.Value
691636
}
692-
start.Attr = append(start.Attr, Attr{name, s})
693637
}
638+
if !explicitNS {
639+
// Historic behavior: elements use the default name space
640+
// they are contained in by default.
641+
start.Name.Space = defaultNS
642+
}
643+
// Historic behaviour: an element that's in a namespace sets
644+
// the default namespace for all elements contained within it.
645+
start.setDefaultNamespace()
694646

695647
if err := p.writeStart(&start); err != nil {
696648
return err
@@ -719,6 +671,64 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
719671
return p.cachedWriteError()
720672
}
721673

674+
// fieldAttr returns the attribute of the given field and
675+
// whether it should actually be added as an attribute;
676+
// val holds the value containing the field.
677+
func (p *printer) fieldAttr(finfo *fieldInfo, val reflect.Value) (Attr, bool, error) {
678+
fv := finfo.value(val)
679+
name := Name{Space: finfo.xmlns, Local: finfo.name}
680+
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
681+
return Attr{}, false, nil
682+
}
683+
if fv.Kind() == reflect.Interface && fv.IsNil() {
684+
return Attr{}, false, nil
685+
}
686+
if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
687+
attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
688+
return attr, attr.Name.Local != "", err
689+
}
690+
if fv.CanAddr() {
691+
pv := fv.Addr()
692+
if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
693+
attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
694+
return attr, attr.Name.Local != "", err
695+
}
696+
}
697+
if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
698+
text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
699+
if err != nil {
700+
return Attr{}, false, err
701+
}
702+
return Attr{name, string(text)}, true, nil
703+
}
704+
if fv.CanAddr() {
705+
pv := fv.Addr()
706+
if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
707+
text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
708+
if err != nil {
709+
return Attr{}, false, err
710+
}
711+
return Attr{name, string(text)}, true, nil
712+
}
713+
}
714+
// Dereference or skip nil pointer, interface values.
715+
switch fv.Kind() {
716+
case reflect.Ptr, reflect.Interface:
717+
if fv.IsNil() {
718+
return Attr{}, false, nil
719+
}
720+
fv = fv.Elem()
721+
}
722+
s, b, err := p.marshalSimple(fv.Type(), fv)
723+
if err != nil {
724+
return Attr{}, false, err
725+
}
726+
if b != nil {
727+
s = string(b)
728+
}
729+
return Attr{name, s}, true, nil
730+
}
731+
722732
// defaultStart returns the default start element to use,
723733
// given the reflect type, field info, and start template.
724734
func (p *printer) defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement {

0 commit comments

Comments
 (0)