summaryrefslogtreecommitdiff
path: root/src/pkg/encoding/xml
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/encoding/xml')
-rw-r--r--src/pkg/encoding/xml/marshal.go11
-rw-r--r--src/pkg/encoding/xml/marshal_test.go115
-rw-r--r--src/pkg/encoding/xml/read.go11
-rw-r--r--src/pkg/encoding/xml/read_test.go27
-rw-r--r--src/pkg/encoding/xml/typeinfo.go3
-rw-r--r--src/pkg/encoding/xml/xml.go2
6 files changed, 166 insertions, 3 deletions
diff --git a/src/pkg/encoding/xml/marshal.go b/src/pkg/encoding/xml/marshal.go
index d9522e0b3..8c6342013 100644
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -184,10 +184,12 @@ var (
// EncodeToken does not call Flush, because usually it is part of a larger operation
// such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked
// during those), and those will call Flush when finished.
-//
// Callers that create an Encoder and then invoke EncodeToken directly, without
// using Encode or EncodeElement, need to call Flush when finished to ensure
// that the XML is written to the underlying writer.
+//
+// EncodeToken allows writing a ProcInst with Target set to "xml" only as the first token
+// in the stream.
func (enc *Encoder) EncodeToken(t Token) error {
p := &enc.p
switch t := t.(type) {
@@ -210,7 +212,12 @@ func (enc *Encoder) EncodeToken(t Token) error {
p.WriteString("-->")
return p.cachedWriteError()
case ProcInst:
- if t.Target == "xml" || !isNameString(t.Target) {
+ // First token to be encoded which is also a ProcInst with target of xml
+ // is the xml declaration. The only ProcInst where target of xml is allowed.
+ if t.Target == "xml" && p.Buffered() != 0 {
+ return fmt.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
+ }
+ if !isNameString(t.Target) {
return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target")
}
if bytes.Contains(t.Inst, endProcInst) {
diff --git a/src/pkg/encoding/xml/marshal_test.go b/src/pkg/encoding/xml/marshal_test.go
index d34118a3d..14f73a75d 100644
--- a/src/pkg/encoding/xml/marshal_test.go
+++ b/src/pkg/encoding/xml/marshal_test.go
@@ -314,6 +314,31 @@ type MarshalerStruct struct {
Foo MyMarshalerAttrTest `xml:",attr"`
}
+type InnerStruct struct {
+ XMLName Name `xml:"testns outer"`
+}
+
+type OuterStruct struct {
+ InnerStruct
+ IntAttr int `xml:"int,attr"`
+}
+
+type OuterNamedStruct struct {
+ InnerStruct
+ XMLName Name `xml:"outerns test"`
+ IntAttr int `xml:"int,attr"`
+}
+
+type OuterNamedOrderedStruct struct {
+ XMLName Name `xml:"outerns test"`
+ InnerStruct
+ IntAttr int `xml:"int,attr"`
+}
+
+type OuterOuterStruct struct {
+ OuterStruct
+}
+
func ifaceptr(x interface{}) interface{} {
return &x
}
@@ -883,6 +908,22 @@ var marshalTests = []struct {
ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
Value: &MarshalerStruct{},
},
+ {
+ ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
+ Value: &OuterStruct{IntAttr: 10},
+ },
+ {
+ ExpectXML: `<test xmlns="outerns" int="10"></test>`,
+ Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
+ },
+ {
+ ExpectXML: `<test xmlns="outerns" int="10"></test>`,
+ Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
+ },
+ {
+ ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
+ Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}},
+ },
}
func TestMarshal(t *testing.T) {
@@ -1149,3 +1190,77 @@ func TestStructPointerMarshal(t *testing.T) {
t.Fatal(err)
}
}
+
+var encodeTokenTests = []struct {
+ tok Token
+ want string
+ ok bool
+}{
+ {StartElement{Name{"space", "local"}, nil}, "<local xmlns=\"space\">", true},
+ {StartElement{Name{"space", ""}, nil}, "", false},
+ {EndElement{Name{"space", ""}}, "", false},
+ {CharData("foo"), "foo", true},
+ {Comment("foo"), "<!--foo-->", true},
+ {Comment("foo-->"), "", false},
+ {ProcInst{"Target", []byte("Instruction")}, "<?Target Instruction?>", true},
+ {ProcInst{"", []byte("Instruction")}, "", false},
+ {ProcInst{"Target", []byte("Instruction?>")}, "", false},
+ {Directive("foo"), "<!foo>", true},
+ {Directive("foo>"), "", false},
+}
+
+func TestEncodeToken(t *testing.T) {
+ for _, tt := range encodeTokenTests {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ err := enc.EncodeToken(tt.tok)
+ switch {
+ case !tt.ok && err == nil:
+ t.Errorf("enc.EncodeToken(%#v): expected error; got none", tt.tok)
+ case tt.ok && err != nil:
+ t.Fatalf("enc.EncodeToken: %v", err)
+ case !tt.ok && err != nil:
+ // expected error, got one
+ }
+ if err := enc.Flush(); err != nil {
+ t.Fatalf("enc.EncodeToken: %v", err)
+ }
+ if got := buf.String(); got != tt.want {
+ t.Errorf("enc.EncodeToken = %s; want: %s", got, tt.want)
+ }
+ }
+}
+
+func TestProcInstEncodeToken(t *testing.T) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+
+ if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
+ t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
+ }
+
+ if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil {
+ t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst")
+ }
+
+ if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil {
+ t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token")
+ }
+}
+
+func TestDecodeEncode(t *testing.T) {
+ var in, out bytes.Buffer
+ in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
+<?Target Instruction?>
+<root>
+</root>
+`)
+ dec := NewDecoder(&in)
+ enc := NewEncoder(&out)
+ for tok, err := dec.Token(); err == nil; tok, err = dec.Token() {
+ err = enc.EncodeToken(tok)
+ if err != nil {
+ t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err)
+ }
+ }
+}
diff --git a/src/pkg/encoding/xml/read.go b/src/pkg/encoding/xml/read.go
index 8890508f8..75b9f2ba1 100644
--- a/src/pkg/encoding/xml/read.go
+++ b/src/pkg/encoding/xml/read.go
@@ -112,7 +112,7 @@ import (
// to a freshly allocated value and then mapping the element to that value.
//
func Unmarshal(data []byte, v interface{}) error {
- return NewDecoder(bytes.NewBuffer(data)).Decode(v)
+ return NewDecoder(bytes.NewReader(data)).Decode(v)
}
// Decode works like xml.Unmarshal, except it reads the decoder
@@ -284,6 +284,15 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
}
}
+ // Load value from interface, but only if the result will be
+ // usefully addressable.
+ if val.Kind() == reflect.Interface && !val.IsNil() {
+ e := val.Elem()
+ if e.Kind() == reflect.Ptr && !e.IsNil() {
+ val = e
+ }
+ }
+
if val.Kind() == reflect.Ptr {
if val.IsNil() {
val.Set(reflect.New(val.Type().Elem()))
diff --git a/src/pkg/encoding/xml/read_test.go b/src/pkg/encoding/xml/read_test.go
index 1404c900f..01f55d0dd 100644
--- a/src/pkg/encoding/xml/read_test.go
+++ b/src/pkg/encoding/xml/read_test.go
@@ -685,3 +685,30 @@ func TestUnmarshaler(t *testing.T) {
t.Errorf("m=%#+v\n", m)
}
}
+
+type Pea struct {
+ Cotelydon string
+}
+
+type Pod struct {
+ Pea interface{} `xml:"Pea"`
+}
+
+// https://code.google.com/p/go/issues/detail?id=6836
+func TestUnmarshalIntoInterface(t *testing.T) {
+ pod := new(Pod)
+ pod.Pea = new(Pea)
+ xml := `<Pod><Pea><Cotelydon>Green stuff</Cotelydon></Pea></Pod>`
+ err := Unmarshal([]byte(xml), pod)
+ if err != nil {
+ t.Fatalf("failed to unmarshal %q: %v", xml, err)
+ }
+ pea, ok := pod.Pea.(*Pea)
+ if !ok {
+ t.Fatalf("unmarshalled into wrong type: have %T want *Pea", pod.Pea)
+ }
+ have, want := pea.Cotelydon, "Green stuff"
+ if have != want {
+ t.Errorf("failed to unmarshal into interface, have %q want %q", have, want)
+ }
+}
diff --git a/src/pkg/encoding/xml/typeinfo.go b/src/pkg/encoding/xml/typeinfo.go
index 83e65402c..22248d20a 100644
--- a/src/pkg/encoding/xml/typeinfo.go
+++ b/src/pkg/encoding/xml/typeinfo.go
@@ -75,6 +75,9 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
if err != nil {
return nil, err
}
+ if tinfo.xmlname == nil {
+ tinfo.xmlname = inner.xmlname
+ }
for _, finfo := range inner.fields {
finfo.idx = append([]int{i}, finfo.idx...)
if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
diff --git a/src/pkg/encoding/xml/xml.go b/src/pkg/encoding/xml/xml.go
index 5b9d67002..b473cb845 100644
--- a/src/pkg/encoding/xml/xml.go
+++ b/src/pkg/encoding/xml/xml.go
@@ -200,6 +200,8 @@ type Decoder struct {
}
// NewDecoder creates a new XML parser reading from r.
+// If r does not implement io.ByteReader, NewDecoder will
+// do its own buffering.
func NewDecoder(r io.Reader) *Decoder {
d := &Decoder{
ns: make(map[string]string),