Skip to content

Commit b69ea01

Browse files
committed
encoding/xml: fix namespaces in a>b tags
Previously, if there was a namespace defined on a a>b tag, the namespace was ignored when printing the parent elements. This fixes that, and also fixes the racy behaviour of printerStack.trim as discussed in https://go-review.googlesource.com/#/c/4152/10 . Fixes #9796. Change-Id: I75f97f67c08bbee151d1e0970f8462dd0f4511ef Reviewed-on: https://go-review.googlesource.com/5910 Reviewed-by: Nigel Tao <[email protected]>
1 parent 25da594 commit b69ea01

File tree

2 files changed

+86
-48
lines changed

2 files changed

+86
-48
lines changed

src/encoding/xml/marshal.go

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,25 +1018,22 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
10181018
}
10191019

10201020
case fElement, fElement | fAny:
1021-
if err := s.trim(finfo.parents); err != nil {
1021+
if err := s.setParents(finfo, vf); err != nil {
10221022
return err
10231023
}
1024-
if len(finfo.parents) > len(s.stack) {
1025-
if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() {
1026-
if err := s.push(finfo.parents[len(s.stack):]); err != nil {
1027-
return err
1028-
}
1029-
}
1030-
}
10311024
}
10321025
if err := p.marshalValue(vf, finfo, nil); err != nil {
10331026
return err
10341027
}
10351028
}
1036-
s.trim(nil)
1029+
if err := s.setParents(&noField, reflect.Value{}); err != nil {
1030+
return err
1031+
}
10371032
return p.cachedWriteError()
10381033
}
10391034

1035+
var noField fieldInfo
1036+
10401037
// return the bufio Writer's cached write error
10411038
func (p *printer) cachedWriteError() error {
10421039
_, err := p.Write(nil)
@@ -1075,37 +1072,64 @@ func (p *printer) writeIndent(depthDelta int) {
10751072
}
10761073

10771074
type parentStack struct {
1078-
p *printer
1079-
stack []string
1075+
p *printer
1076+
xmlns string
1077+
parents []string
10801078
}
10811079

1082-
// trim updates the XML context to match the longest common prefix of the stack
1083-
// and the given parents. A closing tag will be written for every parent
1084-
// popped. Passing a zero slice or nil will close all the elements.
1085-
func (s *parentStack) trim(parents []string) error {
1086-
split := 0
1087-
for ; split < len(parents) && split < len(s.stack); split++ {
1088-
if parents[split] != s.stack[split] {
1089-
break
1080+
// setParents sets the stack of current parents to those found in finfo.
1081+
// It only writes the start elements if vf holds a non-nil value.
1082+
// If finfo is &noField, it pops all elements.
1083+
func (s *parentStack) setParents(finfo *fieldInfo, vf reflect.Value) error {
1084+
xmlns := s.p.defaultNS
1085+
if finfo.xmlns != "" {
1086+
xmlns = finfo.xmlns
1087+
}
1088+
commonParents := 0
1089+
if xmlns == s.xmlns {
1090+
for ; commonParents < len(finfo.parents) && commonParents < len(s.parents); commonParents++ {
1091+
if finfo.parents[commonParents] != s.parents[commonParents] {
1092+
break
1093+
}
10901094
}
10911095
}
1092-
for i := len(s.stack) - 1; i >= split; i-- {
1093-
if err := s.p.writeEnd(Name{Local: s.stack[i]}); err != nil {
1096+
// Pop off any parents that aren't in common with the previous field.
1097+
for i := len(s.parents) - 1; i >= commonParents; i-- {
1098+
if err := s.p.writeEnd(Name{
1099+
Space: s.xmlns,
1100+
Local: s.parents[i],
1101+
}); err != nil {
10941102
return err
10951103
}
10961104
}
1097-
s.stack = parents[:split]
1098-
return nil
1099-
}
1100-
1101-
// push adds parent elements to the stack and writes open tags.
1102-
func (s *parentStack) push(parents []string) error {
1103-
for i := 0; i < len(parents); i++ {
1104-
if err := s.p.writeStart(&StartElement{Name: Name{Local: parents[i]}}); err != nil {
1105+
s.parents = finfo.parents
1106+
s.xmlns = xmlns
1107+
if commonParents >= len(s.parents) {
1108+
// No new elements to push.
1109+
return nil
1110+
}
1111+
if (vf.Kind() == reflect.Ptr || vf.Kind() == reflect.Interface) && vf.IsNil() {
1112+
// The element is nil, so no need for the start elements.
1113+
s.parents = s.parents[:commonParents]
1114+
return nil
1115+
}
1116+
// Push any new parents required.
1117+
for _, name := range s.parents[commonParents:] {
1118+
start := &StartElement{
1119+
Name: Name{
1120+
Space: s.xmlns,
1121+
Local: name,
1122+
},
1123+
}
1124+
// Set the default name space for parent elements
1125+
// to match what we do with other elements.
1126+
if s.xmlns != s.p.defaultNS {
1127+
start.setDefaultNamespace()
1128+
}
1129+
if err := s.p.writeStart(start); err != nil {
11051130
return err
11061131
}
11071132
}
1108-
s.stack = append(s.stack, parents...)
11091133
return nil
11101134
}
11111135

src/encoding/xml/marshal_test.go

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"reflect"
1313
"strconv"
1414
"strings"
15+
"sync"
1516
"testing"
1617
"time"
1718
)
@@ -625,17 +626,21 @@ var marshalTests = []struct {
625626
C string `xml:"space x>c"`
626627
C1 string `xml:"space1 x>c"`
627628
D1 string `xml:"space1 x>d"`
629+
E1 string `xml:"x>e"`
628630
}{
629631
A: "a",
630632
B: "b",
631633
C: "c",
632634
C1: "c1",
633635
D1: "d1",
636+
E1: "e1",
634637
},
635638
ExpectXML: `<top xmlns="space">` +
636-
`<x xmlns=""><a>a</a><b>b</b><c xmlns="space">c</c>` +
637-
`<c xmlns="space1">c1</c>` +
638-
`<d xmlns="space1">d1</d>` +
639+
`<x><a>a</a><b>b</b><c>c</c></x>` +
640+
`<x xmlns="space1">` +
641+
`<c>c1</c>` +
642+
`<d>d1</d>` +
643+
`<e>e1</e>` +
639644
`</x>` +
640645
`</top>`,
641646
},
@@ -659,10 +664,11 @@ var marshalTests = []struct {
659664
D1: "d1",
660665
},
661666
ExpectXML: `<top xmlns="space0">` +
662-
`<x xmlns=""><a>a</a><b>b</b>` +
663-
`<c xmlns="space">c</c>` +
664-
`<c xmlns="space1">c1</c>` +
665-
`<d xmlns="space1">d1</d>` +
667+
`<x><a>a</a><b>b</b></x>` +
668+
`<x xmlns="space"><c>c</c></x>` +
669+
`<x xmlns="space1">` +
670+
`<c>c1</c>` +
671+
`<d>d1</d>` +
666672
`</x>` +
667673
`</top>`,
668674
},
@@ -676,8 +682,8 @@ var marshalTests = []struct {
676682
B1: "b1",
677683
},
678684
ExpectXML: `<top>` +
679-
`<x><b xmlns="space">b</b>` +
680-
`<b xmlns="space1">b1</b></x>` +
685+
`<x xmlns="space"><b>b</b></x>` +
686+
`<x xmlns="space1"><b>b1</b></x>` +
681687
`</top>`,
682688
},
683689

@@ -1100,15 +1106,6 @@ func TestUnmarshal(t *testing.T) {
11001106
if _, ok := test.Value.(*Plain); ok {
11011107
continue
11021108
}
1103-
if test.ExpectXML == `<top>`+
1104-
`<x><b xmlns="space">b</b>`+
1105-
`<b xmlns="space1">b1</b></x>`+
1106-
`</top>` {
1107-
// TODO(rogpeppe): re-enable this test in
1108-
// https://go-review.googlesource.com/#/c/5910/
1109-
continue
1110-
}
1111-
11121109
vt := reflect.TypeOf(test.Value)
11131110
dest := reflect.New(vt.Elem()).Interface()
11141111
err := Unmarshal([]byte(test.ExpectXML), dest)
@@ -1659,3 +1656,20 @@ func TestDecodeEncode(t *testing.T) {
16591656
}
16601657
}
16611658
}
1659+
1660+
// Issue 9796. Used to fail with GORACE="halt_on_error=1" -race.
1661+
func TestRace9796(t *testing.T) {
1662+
type A struct{}
1663+
type B struct {
1664+
C []A `xml:"X>Y"`
1665+
}
1666+
var wg sync.WaitGroup
1667+
for i := 0; i < 2; i++ {
1668+
wg.Add(1)
1669+
go func() {
1670+
Marshal(B{[]A{A{}}})
1671+
wg.Done()
1672+
}()
1673+
}
1674+
wg.Wait()
1675+
}

0 commit comments

Comments
 (0)