mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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 <nigeltao@golang.org>
This commit is contained in:
parent
25da594c6a
commit
b69ea01851
2 changed files with 86 additions and 48 deletions
|
|
@ -1018,25 +1018,22 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
case fElement, fElement | fAny:
|
case fElement, fElement | fAny:
|
||||||
if err := s.trim(finfo.parents); err != nil {
|
if err := s.setParents(finfo, vf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(finfo.parents) > len(s.stack) {
|
|
||||||
if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() {
|
|
||||||
if err := s.push(finfo.parents[len(s.stack):]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err := p.marshalValue(vf, finfo, nil); err != nil {
|
if err := p.marshalValue(vf, finfo, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.trim(nil)
|
if err := s.setParents(&noField, reflect.Value{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return p.cachedWriteError()
|
return p.cachedWriteError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var noField fieldInfo
|
||||||
|
|
||||||
// return the bufio Writer's cached write error
|
// return the bufio Writer's cached write error
|
||||||
func (p *printer) cachedWriteError() error {
|
func (p *printer) cachedWriteError() error {
|
||||||
_, err := p.Write(nil)
|
_, err := p.Write(nil)
|
||||||
|
|
@ -1075,37 +1072,64 @@ func (p *printer) writeIndent(depthDelta int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type parentStack struct {
|
type parentStack struct {
|
||||||
p *printer
|
p *printer
|
||||||
stack []string
|
xmlns string
|
||||||
|
parents []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// trim updates the XML context to match the longest common prefix of the stack
|
// setParents sets the stack of current parents to those found in finfo.
|
||||||
// and the given parents. A closing tag will be written for every parent
|
// It only writes the start elements if vf holds a non-nil value.
|
||||||
// popped. Passing a zero slice or nil will close all the elements.
|
// If finfo is &noField, it pops all elements.
|
||||||
func (s *parentStack) trim(parents []string) error {
|
func (s *parentStack) setParents(finfo *fieldInfo, vf reflect.Value) error {
|
||||||
split := 0
|
xmlns := s.p.defaultNS
|
||||||
for ; split < len(parents) && split < len(s.stack); split++ {
|
if finfo.xmlns != "" {
|
||||||
if parents[split] != s.stack[split] {
|
xmlns = finfo.xmlns
|
||||||
break
|
}
|
||||||
|
commonParents := 0
|
||||||
|
if xmlns == s.xmlns {
|
||||||
|
for ; commonParents < len(finfo.parents) && commonParents < len(s.parents); commonParents++ {
|
||||||
|
if finfo.parents[commonParents] != s.parents[commonParents] {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := len(s.stack) - 1; i >= split; i-- {
|
// Pop off any parents that aren't in common with the previous field.
|
||||||
if err := s.p.writeEnd(Name{Local: s.stack[i]}); err != nil {
|
for i := len(s.parents) - 1; i >= commonParents; i-- {
|
||||||
|
if err := s.p.writeEnd(Name{
|
||||||
|
Space: s.xmlns,
|
||||||
|
Local: s.parents[i],
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.stack = parents[:split]
|
s.parents = finfo.parents
|
||||||
return nil
|
s.xmlns = xmlns
|
||||||
}
|
if commonParents >= len(s.parents) {
|
||||||
|
// No new elements to push.
|
||||||
// push adds parent elements to the stack and writes open tags.
|
return nil
|
||||||
func (s *parentStack) push(parents []string) error {
|
}
|
||||||
for i := 0; i < len(parents); i++ {
|
if (vf.Kind() == reflect.Ptr || vf.Kind() == reflect.Interface) && vf.IsNil() {
|
||||||
if err := s.p.writeStart(&StartElement{Name: Name{Local: parents[i]}}); err != nil {
|
// The element is nil, so no need for the start elements.
|
||||||
|
s.parents = s.parents[:commonParents]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Push any new parents required.
|
||||||
|
for _, name := range s.parents[commonParents:] {
|
||||||
|
start := &StartElement{
|
||||||
|
Name: Name{
|
||||||
|
Space: s.xmlns,
|
||||||
|
Local: name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Set the default name space for parent elements
|
||||||
|
// to match what we do with other elements.
|
||||||
|
if s.xmlns != s.p.defaultNS {
|
||||||
|
start.setDefaultNamespace()
|
||||||
|
}
|
||||||
|
if err := s.p.writeStart(start); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.stack = append(s.stack, parents...)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -625,17 +626,21 @@ var marshalTests = []struct {
|
||||||
C string `xml:"space x>c"`
|
C string `xml:"space x>c"`
|
||||||
C1 string `xml:"space1 x>c"`
|
C1 string `xml:"space1 x>c"`
|
||||||
D1 string `xml:"space1 x>d"`
|
D1 string `xml:"space1 x>d"`
|
||||||
|
E1 string `xml:"x>e"`
|
||||||
}{
|
}{
|
||||||
A: "a",
|
A: "a",
|
||||||
B: "b",
|
B: "b",
|
||||||
C: "c",
|
C: "c",
|
||||||
C1: "c1",
|
C1: "c1",
|
||||||
D1: "d1",
|
D1: "d1",
|
||||||
|
E1: "e1",
|
||||||
},
|
},
|
||||||
ExpectXML: `<top xmlns="space">` +
|
ExpectXML: `<top xmlns="space">` +
|
||||||
`<x xmlns=""><a>a</a><b>b</b><c xmlns="space">c</c>` +
|
`<x><a>a</a><b>b</b><c>c</c></x>` +
|
||||||
`<c xmlns="space1">c1</c>` +
|
`<x xmlns="space1">` +
|
||||||
`<d xmlns="space1">d1</d>` +
|
`<c>c1</c>` +
|
||||||
|
`<d>d1</d>` +
|
||||||
|
`<e>e1</e>` +
|
||||||
`</x>` +
|
`</x>` +
|
||||||
`</top>`,
|
`</top>`,
|
||||||
},
|
},
|
||||||
|
|
@ -659,10 +664,11 @@ var marshalTests = []struct {
|
||||||
D1: "d1",
|
D1: "d1",
|
||||||
},
|
},
|
||||||
ExpectXML: `<top xmlns="space0">` +
|
ExpectXML: `<top xmlns="space0">` +
|
||||||
`<x xmlns=""><a>a</a><b>b</b>` +
|
`<x><a>a</a><b>b</b></x>` +
|
||||||
`<c xmlns="space">c</c>` +
|
`<x xmlns="space"><c>c</c></x>` +
|
||||||
`<c xmlns="space1">c1</c>` +
|
`<x xmlns="space1">` +
|
||||||
`<d xmlns="space1">d1</d>` +
|
`<c>c1</c>` +
|
||||||
|
`<d>d1</d>` +
|
||||||
`</x>` +
|
`</x>` +
|
||||||
`</top>`,
|
`</top>`,
|
||||||
},
|
},
|
||||||
|
|
@ -676,8 +682,8 @@ var marshalTests = []struct {
|
||||||
B1: "b1",
|
B1: "b1",
|
||||||
},
|
},
|
||||||
ExpectXML: `<top>` +
|
ExpectXML: `<top>` +
|
||||||
`<x><b xmlns="space">b</b>` +
|
`<x xmlns="space"><b>b</b></x>` +
|
||||||
`<b xmlns="space1">b1</b></x>` +
|
`<x xmlns="space1"><b>b1</b></x>` +
|
||||||
`</top>`,
|
`</top>`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1100,15 +1106,6 @@ func TestUnmarshal(t *testing.T) {
|
||||||
if _, ok := test.Value.(*Plain); ok {
|
if _, ok := test.Value.(*Plain); ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if test.ExpectXML == `<top>`+
|
|
||||||
`<x><b xmlns="space">b</b>`+
|
|
||||||
`<b xmlns="space1">b1</b></x>`+
|
|
||||||
`</top>` {
|
|
||||||
// TODO(rogpeppe): re-enable this test in
|
|
||||||
// https://go-review.googlesource.com/#/c/5910/
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
vt := reflect.TypeOf(test.Value)
|
vt := reflect.TypeOf(test.Value)
|
||||||
dest := reflect.New(vt.Elem()).Interface()
|
dest := reflect.New(vt.Elem()).Interface()
|
||||||
err := Unmarshal([]byte(test.ExpectXML), dest)
|
err := Unmarshal([]byte(test.ExpectXML), dest)
|
||||||
|
|
@ -1659,3 +1656,20 @@ func TestDecodeEncode(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 9796. Used to fail with GORACE="halt_on_error=1" -race.
|
||||||
|
func TestRace9796(t *testing.T) {
|
||||||
|
type A struct{}
|
||||||
|
type B struct {
|
||||||
|
C []A `xml:"X>Y"`
|
||||||
|
}
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
Marshal(B{[]A{A{}}})
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue