aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/encoding
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-01-25 20:56:26 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-01-25 20:56:26 +0000
commitdf1304ee03f41aed179545d1e8b4684cfd22bbdf (patch)
treec68d6b2a9f5b82a23171b0a488a4b7e5c63ad860 /libgo/go/encoding
parent3be18e47c33b61365786831e0f967f42b09922c9 (diff)
downloadgcc-df1304ee03f41aed179545d1e8b4684cfd22bbdf.zip
gcc-df1304ee03f41aed179545d1e8b4684cfd22bbdf.tar.gz
gcc-df1304ee03f41aed179545d1e8b4684cfd22bbdf.tar.bz2
libgo: Update to weekly.2012-01-15.
From-SVN: r183539
Diffstat (limited to 'libgo/go/encoding')
-rw-r--r--libgo/go/encoding/asn1/asn1.go3
-rw-r--r--libgo/go/encoding/asn1/asn1_test.go6
-rw-r--r--libgo/go/encoding/asn1/marshal_test.go6
-rw-r--r--libgo/go/encoding/gob/decode.go6
-rw-r--r--libgo/go/encoding/gob/encoder_test.go8
-rw-r--r--libgo/go/encoding/json/decode.go35
-rw-r--r--libgo/go/encoding/json/decode_test.go9
-rw-r--r--libgo/go/encoding/json/encode.go16
-rw-r--r--libgo/go/encoding/json/encode_test.go19
-rw-r--r--libgo/go/encoding/xml/atom_test.go27
-rw-r--r--libgo/go/encoding/xml/embed_test.go124
-rw-r--r--libgo/go/encoding/xml/marshal.go257
-rw-r--r--libgo/go/encoding/xml/marshal_test.go433
-rw-r--r--libgo/go/encoding/xml/read.go332
-rw-r--r--libgo/go/encoding/xml/read_test.go189
-rw-r--r--libgo/go/encoding/xml/typeinfo.go321
-rw-r--r--libgo/go/encoding/xml/xml_test.go96
17 files changed, 1150 insertions, 737 deletions
diff --git a/libgo/go/encoding/asn1/asn1.go b/libgo/go/encoding/asn1/asn1.go
index 22a0dde..4d1ae38 100644
--- a/libgo/go/encoding/asn1/asn1.go
+++ b/libgo/go/encoding/asn1/asn1.go
@@ -786,7 +786,8 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
// Because Unmarshal uses the reflect package, the structs
// being written to must use upper case field names.
//
-// An ASN.1 INTEGER can be written to an int, int32 or int64.
+// An ASN.1 INTEGER can be written to an int, int32, int64,
+// or *big.Int (from the math/big package).
// If the encoded value does not fit in the Go type,
// Unmarshal returns a parse error.
//
diff --git a/libgo/go/encoding/asn1/asn1_test.go b/libgo/go/encoding/asn1/asn1_test.go
index 09f9413..92c9eb6 100644
--- a/libgo/go/encoding/asn1/asn1_test.go
+++ b/libgo/go/encoding/asn1/asn1_test.go
@@ -6,6 +6,7 @@ package asn1
import (
"bytes"
+ "math/big"
"reflect"
"testing"
"time"
@@ -351,6 +352,10 @@ type TestElementsAfterString struct {
A, B int
}
+type TestBigInt struct {
+ X *big.Int
+}
+
var unmarshalTestData = []struct {
in []byte
out interface{}
@@ -369,6 +374,7 @@ var unmarshalTestData = []struct {
{[]byte{0x01, 0x01, 0x00}, newBool(false)},
{[]byte{0x01, 0x01, 0x01}, newBool(true)},
{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}},
+ {[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},
}
func TestUnmarshal(t *testing.T) {
diff --git a/libgo/go/encoding/asn1/marshal_test.go b/libgo/go/encoding/asn1/marshal_test.go
index d05b5d8..a7447f9 100644
--- a/libgo/go/encoding/asn1/marshal_test.go
+++ b/libgo/go/encoding/asn1/marshal_test.go
@@ -7,6 +7,7 @@ package asn1
import (
"bytes"
"encoding/hex"
+ "math/big"
"testing"
"time"
)
@@ -20,6 +21,10 @@ type twoIntStruct struct {
B int
}
+type bigIntStruct struct {
+ A *big.Int
+}
+
type nestedStruct struct {
A intStruct
}
@@ -65,6 +70,7 @@ var marshalTests = []marshalTest{
{-128, "020180"},
{-129, "0202ff7f"},
{intStruct{64}, "3003020140"},
+ {bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},
{twoIntStruct{64, 65}, "3006020140020141"},
{nestedStruct{intStruct{127}}, "3005300302017f"},
{[]byte{1, 2, 3}, "0403010203"},
diff --git a/libgo/go/encoding/gob/decode.go b/libgo/go/encoding/gob/decode.go
index ba1f2eb..4d1325d 100644
--- a/libgo/go/encoding/gob/decode.go
+++ b/libgo/go/encoding/gob/decode.go
@@ -1039,9 +1039,9 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[re
// Extract and compare element types.
var sw *sliceType
if tt, ok := builtinIdToType[fw]; ok {
- sw = tt.(*sliceType)
- } else {
- sw = dec.wireType[fw].SliceT
+ sw, _ = tt.(*sliceType)
+ } else if wire != nil {
+ sw = wire.SliceT
}
elem := userType(t.Elem()).base
return sw != nil && dec.compatibleType(elem, sw.Elem, inProgress)
diff --git a/libgo/go/encoding/gob/encoder_test.go b/libgo/go/encoding/gob/encoder_test.go
index cd1500d..7a30f91 100644
--- a/libgo/go/encoding/gob/encoder_test.go
+++ b/libgo/go/encoding/gob/encoder_test.go
@@ -678,3 +678,11 @@ func TestUnexportedChan(t *testing.T) {
t.Fatalf("error encoding unexported channel: %s", err)
}
}
+
+func TestSliceIncompatibility(t *testing.T) {
+ var in = []byte{1, 2, 3}
+ var out []int
+ if err := encAndDec(in, &out); err == nil {
+ t.Error("expected compatibility error")
+ }
+}
diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go
index 8287b33..87076b5 100644
--- a/libgo/go/encoding/json/decode.go
+++ b/libgo/go/encoding/json/decode.go
@@ -10,6 +10,7 @@ package json
import (
"encoding/base64"
"errors"
+ "fmt"
"reflect"
"runtime"
"strconv"
@@ -538,7 +539,7 @@ func (d *decodeState) object(v reflect.Value) {
// Read value.
if destring {
d.value(reflect.ValueOf(&d.tempstr))
- d.literalStore([]byte(d.tempstr), subv)
+ d.literalStore([]byte(d.tempstr), subv, true)
} else {
d.value(subv)
}
@@ -571,11 +572,15 @@ func (d *decodeState) literal(v reflect.Value) {
d.off--
d.scan.undo(op)
- d.literalStore(d.data[start:d.off], v)
+ d.literalStore(d.data[start:d.off], v, false)
}
// literalStore decodes a literal stored in item into v.
-func (d *decodeState) literalStore(item []byte, v reflect.Value) {
+//
+// fromQuoted indicates whether this literal came from unwrapping a
+// string from the ",string" struct tag option. this is used only to
+// produce more helpful error messages.
+func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) {
// Check for unmarshaler.
wantptr := item[0] == 'n' // null
unmarshaler, pv := d.indirect(v, wantptr)
@@ -601,7 +606,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value) {
value := c == 't'
switch v.Kind() {
default:
- d.saveError(&UnmarshalTypeError{"bool", v.Type()})
+ if fromQuoted {
+ d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+ } else {
+ d.saveError(&UnmarshalTypeError{"bool", v.Type()})
+ }
case reflect.Bool:
v.SetBool(value)
case reflect.Interface:
@@ -611,7 +620,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value) {
case '"': // string
s, ok := unquoteBytes(item)
if !ok {
- d.error(errPhase)
+ if fromQuoted {
+ d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+ } else {
+ d.error(errPhase)
+ }
}
switch v.Kind() {
default:
@@ -636,12 +649,20 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value) {
default: // number
if c != '-' && (c < '0' || c > '9') {
- d.error(errPhase)
+ if fromQuoted {
+ d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+ } else {
+ d.error(errPhase)
+ }
}
s := string(item)
switch v.Kind() {
default:
- d.error(&UnmarshalTypeError{"number", v.Type()})
+ if fromQuoted {
+ d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+ } else {
+ d.error(&UnmarshalTypeError{"number", v.Type()})
+ }
case reflect.Interface:
n, err := strconv.ParseFloat(s, 64)
if err != nil {
diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go
index 05c8a06..cc3103f 100644
--- a/libgo/go/encoding/json/decode_test.go
+++ b/libgo/go/encoding/json/decode_test.go
@@ -258,13 +258,10 @@ type wrongStringTest struct {
in, err string
}
-// TODO(bradfitz): as part of Issue 2331, fix these tests' expected
-// error values to be helpful, rather than the confusing messages they
-// are now.
var wrongStringTests = []wrongStringTest{
- {`{"result":"x"}`, "JSON decoder out of sync - data changing underfoot?"},
- {`{"result":"foo"}`, "json: cannot unmarshal bool into Go value of type string"},
- {`{"result":"123"}`, "json: cannot unmarshal number into Go value of type string"},
+ {`{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`},
+ {`{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`},
+ {`{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`},
}
// If people misuse the ,string modifier, the error message should be
diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go
index 3d2f4fc..033da2d 100644
--- a/libgo/go/encoding/json/encode.go
+++ b/libgo/go/encoding/json/encode.go
@@ -12,6 +12,7 @@ package json
import (
"bytes"
"encoding/base64"
+ "math"
"reflect"
"runtime"
"sort"
@@ -170,6 +171,15 @@ func (e *UnsupportedTypeError) Error() string {
return "json: unsupported type: " + e.Type.String()
}
+type UnsupportedValueError struct {
+ Value reflect.Value
+ Str string
+}
+
+func (e *UnsupportedValueError) Error() string {
+ return "json: unsupported value: " + e.Str
+}
+
type InvalidUTF8Error struct {
S string
}
@@ -290,7 +300,11 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
e.Write(b)
}
case reflect.Float32, reflect.Float64:
- b := strconv.AppendFloat(e.scratch[:0], v.Float(), 'g', -1, v.Type().Bits())
+ f := v.Float()
+ if math.IsInf(f, 0) || math.IsNaN(f) {
+ e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, v.Type().Bits())})
+ }
+ b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, v.Type().Bits())
if quoted {
writeString(e, string(b))
} else {
diff --git a/libgo/go/encoding/json/encode_test.go b/libgo/go/encoding/json/encode_test.go
index 9366589..0e39559 100644
--- a/libgo/go/encoding/json/encode_test.go
+++ b/libgo/go/encoding/json/encode_test.go
@@ -6,6 +6,7 @@ package json
import (
"bytes"
+ "math"
"reflect"
"testing"
)
@@ -107,3 +108,21 @@ func TestEncodeRenamedByteSlice(t *testing.T) {
t.Errorf(" got %s want %s", result, expect)
}
}
+
+var unsupportedValues = []interface{}{
+ math.NaN(),
+ math.Inf(-1),
+ math.Inf(1),
+}
+
+func TestUnsupportedValues(t *testing.T) {
+ for _, v := range unsupportedValues {
+ if _, err := Marshal(v); err != nil {
+ if _, ok := err.(*UnsupportedValueError); !ok {
+ t.Errorf("for %v, got %T want UnsupportedValueError", v, err)
+ }
+ } else {
+ t.Errorf("for %v, expected error", v)
+ }
+ }
+}
diff --git a/libgo/go/encoding/xml/atom_test.go b/libgo/go/encoding/xml/atom_test.go
index d365510..8d003aa 100644
--- a/libgo/go/encoding/xml/atom_test.go
+++ b/libgo/go/encoding/xml/atom_test.go
@@ -5,6 +5,7 @@
package xml
var atomValue = &Feed{
+ XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
Title: "Example Feed",
Link: []Link{{Href: "http://example.org/"}},
Updated: ParseTime("2003-12-13T18:30:02Z"),
@@ -24,19 +25,19 @@ var atomValue = &Feed{
var atomXml = `` +
`<feed xmlns="http://www.w3.org/2005/Atom">` +
- `<Title>Example Feed</Title>` +
- `<Id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</Id>` +
- `<Link href="http://example.org/"></Link>` +
- `<Updated>2003-12-13T18:30:02Z</Updated>` +
- `<Author><Name>John Doe</Name><URI></URI><Email></Email></Author>` +
- `<Entry>` +
- `<Title>Atom-Powered Robots Run Amok</Title>` +
- `<Id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</Id>` +
- `<Link href="http://example.org/2003/12/13/atom03"></Link>` +
- `<Updated>2003-12-13T18:30:02Z</Updated>` +
- `<Author><Name></Name><URI></URI><Email></Email></Author>` +
- `<Summary>Some text.</Summary>` +
- `</Entry>` +
+ `<title>Example Feed</title>` +
+ `<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>` +
+ `<link href="http://example.org/"></link>` +
+ `<updated>2003-12-13T18:30:02Z</updated>` +
+ `<author><name>John Doe</name><uri></uri><email></email></author>` +
+ `<entry>` +
+ `<title>Atom-Powered Robots Run Amok</title>` +
+ `<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>` +
+ `<link href="http://example.org/2003/12/13/atom03"></link>` +
+ `<updated>2003-12-13T18:30:02Z</updated>` +
+ `<author><name></name><uri></uri><email></email></author>` +
+ `<summary>Some text.</summary>` +
+ `</entry>` +
`</feed>`
func ParseTime(str string) Time {
diff --git a/libgo/go/encoding/xml/embed_test.go b/libgo/go/encoding/xml/embed_test.go
deleted file mode 100644
index ec7f478..0000000
--- a/libgo/go/encoding/xml/embed_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package xml
-
-import "testing"
-
-type C struct {
- Name string
- Open bool
-}
-
-type A struct {
- XMLName Name `xml:"http://domain a"`
- C
- B B
- FieldA string
-}
-
-type B struct {
- XMLName Name `xml:"b"`
- C
- FieldB string
-}
-
-const _1a = `
-<?xml version="1.0" encoding="UTF-8"?>
-<a xmlns="http://domain">
- <name>KmlFile</name>
- <open>1</open>
- <b>
- <name>Absolute</name>
- <open>0</open>
- <fieldb>bar</fieldb>
- </b>
- <fielda>foo</fielda>
-</a>
-`
-
-// Tests that embedded structs are marshalled.
-func TestEmbedded1(t *testing.T) {
- var a A
- if e := Unmarshal(StringReader(_1a), &a); e != nil {
- t.Fatalf("Unmarshal: %s", e)
- }
- if a.FieldA != "foo" {
- t.Fatalf("Unmarshal: expected 'foo' but found '%s'", a.FieldA)
- }
- if a.Name != "KmlFile" {
- t.Fatalf("Unmarshal: expected 'KmlFile' but found '%s'", a.Name)
- }
- if !a.Open {
- t.Fatal("Unmarshal: expected 'true' but found otherwise")
- }
- if a.B.FieldB != "bar" {
- t.Fatalf("Unmarshal: expected 'bar' but found '%s'", a.B.FieldB)
- }
- if a.B.Name != "Absolute" {
- t.Fatalf("Unmarshal: expected 'Absolute' but found '%s'", a.B.Name)
- }
- if a.B.Open {
- t.Fatal("Unmarshal: expected 'false' but found otherwise")
- }
-}
-
-type A2 struct {
- XMLName Name `xml:"http://domain a"`
- XY string
- Xy string
-}
-
-const _2a = `
-<?xml version="1.0" encoding="UTF-8"?>
-<a xmlns="http://domain">
- <xy>foo</xy>
-</a>
-`
-
-// Tests that conflicting field names get excluded.
-func TestEmbedded2(t *testing.T) {
- var a A2
- if e := Unmarshal(StringReader(_2a), &a); e != nil {
- t.Fatalf("Unmarshal: %s", e)
- }
- if a.XY != "" {
- t.Fatalf("Unmarshal: expected empty string but found '%s'", a.XY)
- }
- if a.Xy != "" {
- t.Fatalf("Unmarshal: expected empty string but found '%s'", a.Xy)
- }
-}
-
-type A3 struct {
- XMLName Name `xml:"http://domain a"`
- xy string
-}
-
-// Tests that private fields are not set.
-func TestEmbedded3(t *testing.T) {
- var a A3
- if e := Unmarshal(StringReader(_2a), &a); e != nil {
- t.Fatalf("Unmarshal: %s", e)
- }
- if a.xy != "" {
- t.Fatalf("Unmarshal: expected empty string but found '%s'", a.xy)
- }
-}
-
-type A4 struct {
- XMLName Name `xml:"http://domain a"`
- Any string
-}
-
-// Tests that private fields are not set.
-func TestEmbedded4(t *testing.T) {
- var a A4
- if e := Unmarshal(StringReader(_2a), &a); e != nil {
- t.Fatalf("Unmarshal: %s", e)
- }
- if a.Any != "foo" {
- t.Fatalf("Unmarshal: expected 'foo' but found '%s'", a.Any)
- }
-}
diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go
index e94fdbc..d25ee30 100644
--- a/libgo/go/encoding/xml/marshal.go
+++ b/libgo/go/encoding/xml/marshal.go
@@ -6,6 +6,8 @@ package xml
import (
"bufio"
+ "bytes"
+ "fmt"
"io"
"reflect"
"strconv"
@@ -42,20 +44,26 @@ type printer struct {
// elements containing the data.
//
// The name for the XML elements is taken from, in order of preference:
-// - the tag on an XMLName field, if the data is a struct
-// - the value of an XMLName field of type xml.Name
+// - the tag on the XMLName field, if the data is a struct
+// - the value of the XMLName field of type xml.Name
// - the tag of the struct field used to obtain the data
// - the name of the struct field used to obtain the data
-// - the name '???'.
+// - the name of the marshalled type
//
// The XML element for a struct contains marshalled elements for each of the
// exported fields of the struct, with these exceptions:
// - the XMLName field, described above, is omitted.
-// - a field with tag "attr" becomes an attribute in the XML element.
-// - a field with tag "chardata" is written as character data,
-// not as an XML element.
-// - a field with tag "innerxml" is written verbatim,
-// not subject to the usual marshalling procedure.
+// - a field with tag "name,attr" becomes an attribute with
+// the given name in the XML element.
+// - a field with tag ",attr" becomes an attribute with the
+// field name in the in the XML element.
+// - a field with tag ",chardata" is written as character data,
+// not as an XML element.
+// - a field with tag ",innerxml" is written verbatim, not subject
+// to the usual marshalling procedure.
+// - a field with tag ",comment" is written as an XML comment, not
+// subject to the usual marshalling procedure. It must not contain
+// the "--" string within it.
//
// If a field uses a tag "a>b>c", then the element c will be nested inside
// parent elements a and b. Fields that appear next to each other that name
@@ -63,17 +71,18 @@ type printer struct {
//
// type Result struct {
// XMLName xml.Name `xml:"result"`
+// Id int `xml:"id,attr"`
// FirstName string `xml:"person>name>first"`
// LastName string `xml:"person>name>last"`
// Age int `xml:"person>age"`
// }
//
-// xml.Marshal(w, &Result{FirstName: "John", LastName: "Doe", Age: 42})
+// xml.Marshal(w, &Result{Id: 13, FirstName: "John", LastName: "Doe", Age: 42})
//
// would be marshalled as:
//
// <result>
-// <person>
+// <person id="13">
// <name>
// <first>John</first>
// <last>Doe</last>
@@ -85,12 +94,12 @@ type printer struct {
// Marshal will return an error if asked to marshal a channel, function, or map.
func Marshal(w io.Writer, v interface{}) (err error) {
p := &printer{bufio.NewWriter(w)}
- err = p.marshalValue(reflect.ValueOf(v), "???")
+ err = p.marshalValue(reflect.ValueOf(v), nil)
p.Flush()
return err
}
-func (p *printer) marshalValue(val reflect.Value, name string) error {
+func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
if !val.IsValid() {
return nil
}
@@ -115,58 +124,75 @@ func (p *printer) marshalValue(val reflect.Value, name string) error {
if val.IsNil() {
return nil
}
- return p.marshalValue(val.Elem(), name)
+ return p.marshalValue(val.Elem(), finfo)
}
// Slices and arrays iterate over the elements. They do not have an enclosing tag.
if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
for i, n := 0, val.Len(); i < n; i++ {
- if err := p.marshalValue(val.Index(i), name); err != nil {
+ if err := p.marshalValue(val.Index(i), finfo); err != nil {
return err
}
}
return nil
}
- // Find XML name
- xmlns := ""
- if kind == reflect.Struct {
- if f, ok := typ.FieldByName("XMLName"); ok {
- if tag := f.Tag.Get("xml"); tag != "" {
- if i := strings.Index(tag, " "); i >= 0 {
- xmlns, name = tag[:i], tag[i+1:]
- } else {
- name = tag
- }
- } else if v, ok := val.FieldByIndex(f.Index).Interface().(Name); ok && v.Local != "" {
- xmlns, name = v.Space, v.Local
- }
+ tinfo, err := getTypeInfo(typ)
+ if err != nil {
+ return err
+ }
+
+ // Precedence for the XML element name is:
+ // 1. XMLName field in underlying struct;
+ // 2. field name/tag in the struct field; and
+ // 3. type name
+ var xmlns, name string
+ if tinfo.xmlname != nil {
+ xmlname := tinfo.xmlname
+ if xmlname.name != "" {
+ xmlns, name = xmlname.xmlns, xmlname.name
+ } else if v, ok := val.FieldByIndex(xmlname.idx).Interface().(Name); ok && v.Local != "" {
+ xmlns, name = v.Space, v.Local
+ }
+ }
+ if name == "" && finfo != nil {
+ xmlns, name = finfo.xmlns, finfo.name
+ }
+ if name == "" {
+ name = typ.Name()
+ if name == "" {
+ return &UnsupportedTypeError{typ}
}
}
p.WriteByte('<')
p.WriteString(name)
+ if xmlns != "" {
+ p.WriteString(` xmlns="`)
+ // TODO: EscapeString, to avoid the allocation.
+ Escape(p, []byte(xmlns))
+ p.WriteByte('"')
+ }
+
// Attributes
- if kind == reflect.Struct {
- if len(xmlns) > 0 {
- p.WriteString(` xmlns="`)
- Escape(p, []byte(xmlns))
- p.WriteByte('"')
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ if finfo.flags&fAttr == 0 {
+ continue
}
-
- for i, n := 0, typ.NumField(); i < n; i++ {
- if f := typ.Field(i); f.PkgPath == "" && f.Tag.Get("xml") == "attr" {
- if f.Type.Kind() == reflect.String {
- if str := val.Field(i).String(); str != "" {
- p.WriteByte(' ')
- p.WriteString(strings.ToLower(f.Name))
- p.WriteString(`="`)
- Escape(p, []byte(str))
- p.WriteByte('"')
- }
- }
- }
+ var str string
+ if fv := val.FieldByIndex(finfo.idx); fv.Kind() == reflect.String {
+ str = fv.String()
+ } else {
+ str = fmt.Sprint(fv.Interface())
+ }
+ if str != "" {
+ p.WriteByte(' ')
+ p.WriteString(finfo.name)
+ p.WriteString(`="`)
+ Escape(p, []byte(str))
+ p.WriteByte('"')
}
}
p.WriteByte('>')
@@ -194,58 +220,9 @@ func (p *printer) marshalValue(val reflect.Value, name string) error {
bytes := val.Interface().([]byte)
Escape(p, bytes)
case reflect.Struct:
- s := parentStack{printer: p}
- for i, n := 0, val.NumField(); i < n; i++ {
- if f := typ.Field(i); f.Name != "XMLName" && f.PkgPath == "" {
- name := f.Name
- vf := val.Field(i)
- switch tag := f.Tag.Get("xml"); tag {
- case "":
- s.trim(nil)
- case "chardata":
- if tk := f.Type.Kind(); tk == reflect.String {
- Escape(p, []byte(vf.String()))
- } else if tk == reflect.Slice {
- if elem, ok := vf.Interface().([]byte); ok {
- Escape(p, elem)
- }
- }
- continue
- case "innerxml":
- iface := vf.Interface()
- switch raw := iface.(type) {
- case []byte:
- p.Write(raw)
- continue
- case string:
- p.WriteString(raw)
- continue
- }
- case "attr":
- continue
- default:
- parents := strings.Split(tag, ">")
- if len(parents) == 1 {
- parents, name = nil, tag
- } else {
- parents, name = parents[:len(parents)-1], parents[len(parents)-1]
- if parents[0] == "" {
- parents[0] = f.Name
- }
- }
-
- s.trim(parents)
- if !(vf.Kind() == reflect.Ptr || vf.Kind() == reflect.Interface) || !vf.IsNil() {
- s.push(parents[len(s.stack):])
- }
- }
-
- if err := p.marshalValue(vf, name); err != nil {
- return err
- }
- }
+ if err := p.marshalStruct(tinfo, val); err != nil {
+ return err
}
- s.trim(nil)
default:
return &UnsupportedTypeError{typ}
}
@@ -258,6 +235,94 @@ func (p *printer) marshalValue(val reflect.Value, name string) error {
return nil
}
+var ddBytes = []byte("--")
+
+func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
+ s := parentStack{printer: p}
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ if finfo.flags&(fAttr|fAny) != 0 {
+ continue
+ }
+ vf := val.FieldByIndex(finfo.idx)
+ switch finfo.flags & fMode {
+ case fCharData:
+ switch vf.Kind() {
+ case reflect.String:
+ Escape(p, []byte(vf.String()))
+ case reflect.Slice:
+ if elem, ok := vf.Interface().([]byte); ok {
+ Escape(p, elem)
+ }
+ }
+ continue
+
+ case fComment:
+ k := vf.Kind()
+ if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) {
+ return fmt.Errorf("xml: bad type for comment field of %s", val.Type())
+ }
+ if vf.Len() == 0 {
+ continue
+ }
+ p.WriteString("<!--")
+ dashDash := false
+ dashLast := false
+ switch k {
+ case reflect.String:
+ s := vf.String()
+ dashDash = strings.Index(s, "--") >= 0
+ dashLast = s[len(s)-1] == '-'
+ if !dashDash {
+ p.WriteString(s)
+ }
+ case reflect.Slice:
+ b := vf.Bytes()
+ dashDash = bytes.Index(b, ddBytes) >= 0
+ dashLast = b[len(b)-1] == '-'
+ if !dashDash {
+ p.Write(b)
+ }
+ default:
+ panic("can't happen")
+ }
+ if dashDash {
+ return fmt.Errorf(`xml: comments must not contain "--"`)
+ }
+ if dashLast {
+ // "--->" is invalid grammar. Make it "- -->"
+ p.WriteByte(' ')
+ }
+ p.WriteString("-->")
+ continue
+
+ case fInnerXml:
+ iface := vf.Interface()
+ switch raw := iface.(type) {
+ case []byte:
+ p.Write(raw)
+ continue
+ case string:
+ p.WriteString(raw)
+ continue
+ }
+
+ case fElement:
+ s.trim(finfo.parents)
+ if len(finfo.parents) > len(s.stack) {
+ if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() {
+ s.push(finfo.parents[len(s.stack):])
+ }
+ }
+ }
+ if err := p.marshalValue(vf, finfo); err != nil {
+ return err
+ }
+ }
+ s.trim(nil)
+ return nil
+}
+
type parentStack struct {
*printer
stack []string
diff --git a/libgo/go/encoding/xml/marshal_test.go b/libgo/go/encoding/xml/marshal_test.go
index 6a24169..bec5376 100644
--- a/libgo/go/encoding/xml/marshal_test.go
+++ b/libgo/go/encoding/xml/marshal_test.go
@@ -25,10 +25,10 @@ type Passenger struct {
}
type Ship struct {
- XMLName Name `xml:"spaceship"`
+ XMLName struct{} `xml:"spaceship"`
- Name string `xml:"attr"`
- Pilot string `xml:"attr"`
+ Name string `xml:"name,attr"`
+ Pilot string `xml:"pilot,attr"`
Drive DriveType `xml:"drive"`
Age uint `xml:"age"`
Passenger []*Passenger `xml:"passenger"`
@@ -44,48 +44,50 @@ func (rx RawXML) MarshalXML() ([]byte, error) {
type NamedType string
type Port struct {
- XMLName Name `xml:"port"`
- Type string `xml:"attr"`
- Number string `xml:"chardata"`
+ XMLName struct{} `xml:"port"`
+ Type string `xml:"type,attr"`
+ Comment string `xml:",comment"`
+ Number string `xml:",chardata"`
}
type Domain struct {
- XMLName Name `xml:"domain"`
- Country string `xml:"attr"`
- Name []byte `xml:"chardata"`
+ XMLName struct{} `xml:"domain"`
+ Country string `xml:",attr"`
+ Name []byte `xml:",chardata"`
+ Comment []byte `xml:",comment"`
}
type Book struct {
- XMLName Name `xml:"book"`
- Title string `xml:"chardata"`
+ XMLName struct{} `xml:"book"`
+ Title string `xml:",chardata"`
}
type SecretAgent struct {
- XMLName Name `xml:"agent"`
- Handle string `xml:"attr"`
+ XMLName struct{} `xml:"agent"`
+ Handle string `xml:"handle,attr"`
Identity string
- Obfuscate string `xml:"innerxml"`
+ Obfuscate string `xml:",innerxml"`
}
type NestedItems struct {
- XMLName Name `xml:"result"`
+ XMLName struct{} `xml:"result"`
Items []string `xml:">item"`
Item1 []string `xml:"Items>item1"`
}
type NestedOrder struct {
- XMLName Name `xml:"result"`
- Field1 string `xml:"parent>c"`
- Field2 string `xml:"parent>b"`
- Field3 string `xml:"parent>a"`
+ XMLName struct{} `xml:"result"`
+ Field1 string `xml:"parent>c"`
+ Field2 string `xml:"parent>b"`
+ Field3 string `xml:"parent>a"`
}
type MixedNested struct {
- XMLName Name `xml:"result"`
- A string `xml:"parent1>a"`
- B string `xml:"b"`
- C string `xml:"parent1>parent2>c"`
- D string `xml:"parent1>d"`
+ XMLName struct{} `xml:"result"`
+ A string `xml:"parent1>a"`
+ B string `xml:"b"`
+ C string `xml:"parent1>parent2>c"`
+ D string `xml:"parent1>d"`
}
type NilTest struct {
@@ -95,62 +97,165 @@ type NilTest struct {
}
type Service struct {
- XMLName Name `xml:"service"`
- Domain *Domain `xml:"host>domain"`
- Port *Port `xml:"host>port"`
+ XMLName struct{} `xml:"service"`
+ Domain *Domain `xml:"host>domain"`
+ Port *Port `xml:"host>port"`
Extra1 interface{}
Extra2 interface{} `xml:"host>extra2"`
}
var nilStruct *Ship
+type EmbedA struct {
+ EmbedC
+ EmbedB EmbedB
+ FieldA string
+}
+
+type EmbedB struct {
+ FieldB string
+ EmbedC
+}
+
+type EmbedC struct {
+ FieldA1 string `xml:"FieldA>A1"`
+ FieldA2 string `xml:"FieldA>A2"`
+ FieldB string
+ FieldC string
+}
+
+type NameCasing struct {
+ XMLName struct{} `xml:"casing"`
+ Xy string
+ XY string
+ XyA string `xml:"Xy,attr"`
+ XYA string `xml:"XY,attr"`
+}
+
+type NamePrecedence struct {
+ XMLName Name `xml:"Parent"`
+ FromTag XMLNameWithoutTag `xml:"InTag"`
+ FromNameVal XMLNameWithoutTag
+ FromNameTag XMLNameWithTag
+ InFieldName string
+}
+
+type XMLNameWithTag struct {
+ XMLName Name `xml:"InXMLNameTag"`
+ Value string ",chardata"
+}
+
+type XMLNameWithoutTag struct {
+ XMLName Name
+ Value string ",chardata"
+}
+
+type AttrTest struct {
+ Int int `xml:",attr"`
+ Lower int `xml:"int,attr"`
+ Float float64 `xml:",attr"`
+ Uint8 uint8 `xml:",attr"`
+ Bool bool `xml:",attr"`
+ Str string `xml:",attr"`
+}
+
+type AnyTest struct {
+ XMLName struct{} `xml:"a"`
+ Nested string `xml:"nested>value"`
+ AnyField AnyHolder `xml:",any"`
+}
+
+type AnyHolder struct {
+ XMLName Name
+ XML string `xml:",innerxml"`
+}
+
+type RecurseA struct {
+ A string
+ B *RecurseB
+}
+
+type RecurseB struct {
+ A *RecurseA
+ B string
+}
+
+type Plain struct {
+ V interface{}
+}
+
+// Unless explicitly stated as such (or *Plain), all of the
+// tests below are two-way tests. When introducing new tests,
+// please try to make them two-way as well to ensure that
+// marshalling and unmarshalling are as symmetrical as feasible.
var marshalTests = []struct {
- Value interface{}
- ExpectXML string
+ Value interface{}
+ ExpectXML string
+ MarshalOnly bool
+ UnmarshalOnly bool
}{
// Test nil marshals to nothing
- {Value: nil, ExpectXML: ``},
- {Value: nilStruct, ExpectXML: ``},
-
- // Test value types (no tag name, so ???)
- {Value: true, ExpectXML: `<???>true</???>`},
- {Value: int(42), ExpectXML: `<???>42</???>`},
- {Value: int8(42), ExpectXML: `<???>42</???>`},
- {Value: int16(42), ExpectXML: `<???>42</???>`},
- {Value: int32(42), ExpectXML: `<???>42</???>`},
- {Value: uint(42), ExpectXML: `<???>42</???>`},
- {Value: uint8(42), ExpectXML: `<???>42</???>`},
- {Value: uint16(42), ExpectXML: `<???>42</???>`},
- {Value: uint32(42), ExpectXML: `<???>42</???>`},
- {Value: float32(1.25), ExpectXML: `<???>1.25</???>`},
- {Value: float64(1.25), ExpectXML: `<???>1.25</???>`},
- {Value: uintptr(0xFFDD), ExpectXML: `<???>65501</???>`},
- {Value: "gopher", ExpectXML: `<???>gopher</???>`},
- {Value: []byte("gopher"), ExpectXML: `<???>gopher</???>`},
- {Value: "</>", ExpectXML: `<???>&lt;/&gt;</???>`},
- {Value: []byte("</>"), ExpectXML: `<???>&lt;/&gt;</???>`},
- {Value: [3]byte{'<', '/', '>'}, ExpectXML: `<???>&lt;/&gt;</???>`},
- {Value: NamedType("potato"), ExpectXML: `<???>potato</???>`},
- {Value: []int{1, 2, 3}, ExpectXML: `<???>1</???><???>2</???><???>3</???>`},
- {Value: [3]int{1, 2, 3}, ExpectXML: `<???>1</???><???>2</???><???>3</???>`},
+ {Value: nil, ExpectXML: ``, MarshalOnly: true},
+ {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
+
+ // Test value types
+ {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
+ {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
+ {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
+ {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
+ {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
+ {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
+ {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
+ {Value: &Plain{"</>"}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
+ {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
+ {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
+ {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
+ {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
+ {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
// Test innerxml
- {Value: RawXML("</>"), ExpectXML: `</>`},
{
Value: &SecretAgent{
Handle: "007",
Identity: "James Bond",
Obfuscate: "<redacted/>",
},
- //ExpectXML: `<agent handle="007"><redacted/></agent>`,
- ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
+ ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
+ MarshalOnly: true,
+ },
+ {
+ Value: &SecretAgent{
+ Handle: "007",
+ Identity: "James Bond",
+ Obfuscate: "<Identity>James Bond</Identity><redacted/>",
+ },
+ ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
+ UnmarshalOnly: true,
+ },
+
+ // Test marshaller interface
+ {
+ Value: RawXML("</>"),
+ ExpectXML: `</>`,
+ MarshalOnly: true,
},
// Test structs
{Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
{Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
{Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="&lt;unix&gt;"></port>`},
+ {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
+ {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
{Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&amp;friends</domain>`},
+ {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
{Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride &amp; Prejudice</book>`},
{Value: atomValue, ExpectXML: atomXml},
{
@@ -203,16 +308,25 @@ var marshalTests = []struct {
`</passenger>` +
`</spaceship>`,
},
+
// Test a>b
{
- Value: NestedItems{Items: []string{}, Item1: []string{}},
+ Value: &NestedItems{Items: nil, Item1: nil},
+ ExpectXML: `<result>` +
+ `<Items>` +
+ `</Items>` +
+ `</result>`,
+ },
+ {
+ Value: &NestedItems{Items: []string{}, Item1: []string{}},
ExpectXML: `<result>` +
`<Items>` +
`</Items>` +
`</result>`,
+ MarshalOnly: true,
},
{
- Value: NestedItems{Items: []string{}, Item1: []string{"A"}},
+ Value: &NestedItems{Items: nil, Item1: []string{"A"}},
ExpectXML: `<result>` +
`<Items>` +
`<item1>A</item1>` +
@@ -220,7 +334,7 @@ var marshalTests = []struct {
`</result>`,
},
{
- Value: NestedItems{Items: []string{"A", "B"}, Item1: []string{}},
+ Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
ExpectXML: `<result>` +
`<Items>` +
`<item>A</item>` +
@@ -229,7 +343,7 @@ var marshalTests = []struct {
`</result>`,
},
{
- Value: NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
+ Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
ExpectXML: `<result>` +
`<Items>` +
`<item>A</item>` +
@@ -239,7 +353,7 @@ var marshalTests = []struct {
`</result>`,
},
{
- Value: NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
+ Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
ExpectXML: `<result>` +
`<parent>` +
`<c>C</c>` +
@@ -249,16 +363,17 @@ var marshalTests = []struct {
`</result>`,
},
{
- Value: NilTest{A: "A", B: nil, C: "C"},
- ExpectXML: `<???>` +
+ Value: &NilTest{A: "A", B: nil, C: "C"},
+ ExpectXML: `<NilTest>` +
`<parent1>` +
`<parent2><a>A</a></parent2>` +
`<parent2><c>C</c></parent2>` +
`</parent1>` +
- `</???>`,
+ `</NilTest>`,
+ MarshalOnly: true, // Uses interface{}
},
{
- Value: MixedNested{A: "A", B: "B", C: "C", D: "D"},
+ Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
ExpectXML: `<result>` +
`<parent1><a>A</a></parent1>` +
`<b>B</b>` +
@@ -269,32 +384,154 @@ var marshalTests = []struct {
`</result>`,
},
{
- Value: Service{Port: &Port{Number: "80"}},
+ Value: &Service{Port: &Port{Number: "80"}},
ExpectXML: `<service><host><port>80</port></host></service>`,
},
{
- Value: Service{},
+ Value: &Service{},
ExpectXML: `<service></service>`,
},
{
- Value: Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
+ Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
ExpectXML: `<service>` +
`<host><port>80</port></host>` +
`<Extra1>A</Extra1>` +
`<host><extra2>B</extra2></host>` +
`</service>`,
+ MarshalOnly: true,
},
{
- Value: Service{Port: &Port{Number: "80"}, Extra2: "example"},
+ Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
ExpectXML: `<service>` +
`<host><port>80</port></host>` +
`<host><extra2>example</extra2></host>` +
`</service>`,
+ MarshalOnly: true,
+ },
+
+ // Test struct embedding
+ {
+ Value: &EmbedA{
+ EmbedC: EmbedC{
+ FieldA1: "", // Shadowed by A.A
+ FieldA2: "", // Shadowed by A.A
+ FieldB: "A.C.B",
+ FieldC: "A.C.C",
+ },
+ EmbedB: EmbedB{
+ FieldB: "A.B.B",
+ EmbedC: EmbedC{
+ FieldA1: "A.B.C.A1",
+ FieldA2: "A.B.C.A2",
+ FieldB: "", // Shadowed by A.B.B
+ FieldC: "A.B.C.C",
+ },
+ },
+ FieldA: "A.A",
+ },
+ ExpectXML: `<EmbedA>` +
+ `<FieldB>A.C.B</FieldB>` +
+ `<FieldC>A.C.C</FieldC>` +
+ `<EmbedB>` +
+ `<FieldB>A.B.B</FieldB>` +
+ `<FieldA>` +
+ `<A1>A.B.C.A1</A1>` +
+ `<A2>A.B.C.A2</A2>` +
+ `</FieldA>` +
+ `<FieldC>A.B.C.C</FieldC>` +
+ `</EmbedB>` +
+ `<FieldA>A.A</FieldA>` +
+ `</EmbedA>`,
+ },
+
+ // Test that name casing matters
+ {
+ Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
+ ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
+ },
+
+ // Test the order in which the XML element name is chosen
+ {
+ Value: &NamePrecedence{
+ FromTag: XMLNameWithoutTag{Value: "A"},
+ FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
+ FromNameTag: XMLNameWithTag{Value: "C"},
+ InFieldName: "D",
+ },
+ ExpectXML: `<Parent>` +
+ `<InTag><Value>A</Value></InTag>` +
+ `<InXMLName><Value>B</Value></InXMLName>` +
+ `<InXMLNameTag><Value>C</Value></InXMLNameTag>` +
+ `<InFieldName>D</InFieldName>` +
+ `</Parent>`,
+ MarshalOnly: true,
+ },
+ {
+ Value: &NamePrecedence{
+ XMLName: Name{Local: "Parent"},
+ FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
+ FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
+ FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
+ InFieldName: "D",
+ },
+ ExpectXML: `<Parent>` +
+ `<InTag><Value>A</Value></InTag>` +
+ `<FromNameVal><Value>B</Value></FromNameVal>` +
+ `<InXMLNameTag><Value>C</Value></InXMLNameTag>` +
+ `<InFieldName>D</InFieldName>` +
+ `</Parent>`,
+ UnmarshalOnly: true,
+ },
+
+ // Test attributes
+ {
+ Value: &AttrTest{
+ Int: 8,
+ Lower: 9,
+ Float: 23.5,
+ Uint8: 255,
+ Bool: true,
+ Str: "s",
+ },
+ ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255" Bool="true" Str="s"></AttrTest>`,
+ },
+
+ // Test ",any"
+ {
+ ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
+ Value: &AnyTest{
+ Nested: "known",
+ AnyField: AnyHolder{
+ XMLName: Name{Local: "other"},
+ XML: "<sub>unknown</sub>",
+ },
+ },
+ UnmarshalOnly: true,
+ },
+ {
+ Value: &AnyTest{Nested: "known", AnyField: AnyHolder{XML: "<unknown/>"}},
+ ExpectXML: `<a><nested><value>known</value></nested></a>`,
+ MarshalOnly: true,
+ },
+
+ // Test recursive types.
+ {
+ Value: &RecurseA{
+ A: "a1",
+ B: &RecurseB{
+ A: &RecurseA{"a2", nil},
+ B: "b1",
+ },
+ },
+ ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
},
}
func TestMarshal(t *testing.T) {
for idx, test := range marshalTests {
+ if test.UnmarshalOnly {
+ continue
+ }
buf := bytes.NewBuffer(nil)
err := Marshal(buf, test.Value)
if err != nil {
@@ -303,9 +540,9 @@ func TestMarshal(t *testing.T) {
}
if got, want := buf.String(), test.ExpectXML; got != want {
if strings.Contains(want, "\n") {
- t.Errorf("#%d: marshal(%#v) - GOT:\n%s\nWANT:\n%s", idx, test.Value, got, want)
+ t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
} else {
- t.Errorf("#%d: marshal(%#v) = %#q want %#q", idx, test.Value, got, want)
+ t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
}
}
}
@@ -334,6 +571,10 @@ var marshalErrorTests = []struct {
Err: "xml: unsupported type: map[*xml.Ship]bool",
Kind: reflect.Map,
},
+ {
+ Value: &Domain{Comment: []byte("f--bar")},
+ Err: `xml: comments must not contain "--"`,
+ },
}
func TestMarshalErrors(t *testing.T) {
@@ -341,10 +582,12 @@ func TestMarshalErrors(t *testing.T) {
buf := bytes.NewBuffer(nil)
err := Marshal(buf, test.Value)
if err == nil || err.Error() != test.Err {
- t.Errorf("#%d: marshal(%#v) = [error] %q, want %q", idx, test.Value, err, test.Err)
+ t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
}
- if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
- t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
+ if test.Kind != reflect.Invalid {
+ if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
+ t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
+ }
}
}
}
@@ -352,39 +595,20 @@ func TestMarshalErrors(t *testing.T) {
// Do invertibility testing on the various structures that we test
func TestUnmarshal(t *testing.T) {
for i, test := range marshalTests {
- // Skip the nil pointers
- if i <= 1 {
+ if test.MarshalOnly {
continue
}
-
- var dest interface{}
-
- switch test.Value.(type) {
- case *Ship, Ship:
- dest = &Ship{}
- case *Port, Port:
- dest = &Port{}
- case *Domain, Domain:
- dest = &Domain{}
- case *Feed, Feed:
- dest = &Feed{}
- default:
+ if _, ok := test.Value.(*Plain); ok {
continue
}
+ vt := reflect.TypeOf(test.Value)
+ dest := reflect.New(vt.Elem()).Interface()
buffer := bytes.NewBufferString(test.ExpectXML)
err := Unmarshal(buffer, dest)
- // Don't compare XMLNames
switch fix := dest.(type) {
- case *Ship:
- fix.XMLName = Name{}
- case *Port:
- fix.XMLName = Name{}
- case *Domain:
- fix.XMLName = Name{}
case *Feed:
- fix.XMLName = Name{}
fix.Author.InnerXML = ""
for i := range fix.Entry {
fix.Entry[i].Author.InnerXML = ""
@@ -394,30 +618,23 @@ func TestUnmarshal(t *testing.T) {
if err != nil {
t.Errorf("#%d: unexpected error: %#v", i, err)
} else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
- t.Errorf("#%d: unmarshal(%q) = %#v, want %#v", i, test.ExpectXML, got, want)
+ t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
}
}
}
func BenchmarkMarshal(b *testing.B) {
- idx := len(marshalTests) - 1
- test := marshalTests[idx]
-
buf := bytes.NewBuffer(nil)
for i := 0; i < b.N; i++ {
- Marshal(buf, test.Value)
+ Marshal(buf, atomValue)
buf.Truncate(0)
}
}
func BenchmarkUnmarshal(b *testing.B) {
- idx := len(marshalTests) - 1
- test := marshalTests[idx]
- sm := &Ship{}
- xml := []byte(test.ExpectXML)
-
+ xml := []byte(atomXml)
for i := 0; i < b.N; i++ {
buffer := bytes.NewBuffer(xml)
- Unmarshal(buffer, sm)
+ Unmarshal(buffer, &Feed{})
}
}
diff --git a/libgo/go/encoding/xml/read.go b/libgo/go/encoding/xml/read.go
index 6dd3654..dde68de 100644
--- a/libgo/go/encoding/xml/read.go
+++ b/libgo/go/encoding/xml/read.go
@@ -7,13 +7,10 @@ package xml
import (
"bytes"
"errors"
- "fmt"
"io"
"reflect"
"strconv"
"strings"
- "unicode"
- "unicode/utf8"
)
// BUG(rsc): Mapping between XML elements and data structures is inherently flawed:
@@ -31,7 +28,7 @@ import (
// For example, given these definitions:
//
// type Email struct {
-// Where string `xml:"attr"`
+// Where string `xml:",attr"`
// Addr string
// }
//
@@ -64,7 +61,8 @@ import (
//
// via Unmarshal(r, &result) is equivalent to assigning
//
-// r = Result{xml.Name{"", "result"},
+// r = Result{
+// xml.Name{Local: "result"},
// "Grace R. Emlin", // name
// "phone", // no phone given
// []Email{
@@ -87,9 +85,9 @@ import (
// In the rules, the tag of a field refers to the value associated with the
// key 'xml' in the struct field's tag (see the example above).
//
-// * If the struct has a field of type []byte or string with tag "innerxml",
-// Unmarshal accumulates the raw XML nested inside the element
-// in that field. The rest of the rules still apply.
+// * If the struct has a field of type []byte or string with tag
+// ",innerxml", Unmarshal accumulates the raw XML nested inside the
+// element in that field. The rest of the rules still apply.
//
// * If the struct has a field named XMLName of type xml.Name,
// Unmarshal records the element name in that field.
@@ -100,8 +98,9 @@ import (
// returns an error.
//
// * If the XML element has an attribute whose name matches a
-// struct field of type string with tag "attr", Unmarshal records
-// the attribute value in that field.
+// struct field name with an associated tag containing ",attr" or
+// the explicit name in a struct field tag of the form "name,attr",
+// Unmarshal records the attribute value in that field.
//
// * If the XML element contains character data, that data is
// accumulated in the first struct field that has tag "chardata".
@@ -109,23 +108,30 @@ import (
// If there is no such field, the character data is discarded.
//
// * If the XML element contains comments, they are accumulated in
-// the first struct field that has tag "comments". The struct
+// the first struct field that has tag ",comments". The struct
// field may have type []byte or string. If there is no such
// field, the comments are discarded.
//
// * If the XML element contains a sub-element whose name matches
-// the prefix of a tag formatted as "a>b>c", unmarshal
+// the prefix of a tag formatted as "a" or "a>b>c", unmarshal
// will descend into the XML structure looking for elements with the
-// given names, and will map the innermost elements to that struct field.
-// A tag starting with ">" is equivalent to one starting
+// given names, and will map the innermost elements to that struct
+// field. A tag starting with ">" is equivalent to one starting
// with the field name followed by ">".
//
-// * If the XML element contains a sub-element whose name
-// matches a field whose tag is neither "attr" nor "chardata",
-// Unmarshal maps the sub-element to that struct field.
-// Otherwise, if the struct has a field named Any, unmarshal
+// * If the XML element contains a sub-element whose name matches
+// a struct field's XMLName tag and the struct field has no
+// explicit name tag as per the previous rule, unmarshal maps
+// the sub-element to that struct field.
+//
+// * If the XML element contains a sub-element whose name matches a
+// field without any mode flags (",attr", ",chardata", etc), Unmarshal
// maps the sub-element to that struct field.
//
+// * If the XML element contains a sub-element that hasn't matched any
+// of the above rules and the struct has a field with tag ",any",
+// unmarshal maps the sub-element to that struct field.
+//
// Unmarshal maps an XML element to a string or []byte by saving the
// concatenation of that element's character data in the string or
// []byte.
@@ -169,18 +175,6 @@ type UnmarshalError string
func (e UnmarshalError) Error() string { return string(e) }
-// A TagPathError represents an error in the unmarshalling process
-// caused by the use of field tags with conflicting paths.
-type TagPathError struct {
- Struct reflect.Type
- Field1, Tag1 string
- Field2, Tag2 string
-}
-
-func (e *TagPathError) Error() string {
- return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
-}
-
// The Parser's Unmarshal method is like xml.Unmarshal
// except that it can be passed a pointer to the initial start element,
// useful when a client reads some raw XML tokens itself
@@ -195,26 +189,6 @@ func (p *Parser) Unmarshal(val interface{}, start *StartElement) error {
return p.unmarshal(v.Elem(), start)
}
-// fieldName strips invalid characters from an XML name
-// to create a valid Go struct name. It also converts the
-// name to lower case letters.
-func fieldName(original string) string {
-
- var i int
- //remove leading underscores, without exhausting all characters
- for i = 0; i < len(original)-1 && original[i] == '_'; i++ {
- }
-
- return strings.Map(
- func(x rune) rune {
- if x == '_' || unicode.IsDigit(x) || unicode.IsLetter(x) {
- return unicode.ToLower(x)
- }
- return -1
- },
- original[i:])
-}
-
// Unmarshal a single XML element into val.
func (p *Parser) unmarshal(val reflect.Value, start *StartElement) error {
// Find start element if we need it.
@@ -246,15 +220,22 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) error {
saveXML reflect.Value
saveXMLIndex int
saveXMLData []byte
+ saveAny reflect.Value
sv reflect.Value
- styp reflect.Type
- fieldPaths map[string]pathInfo
+ tinfo *typeInfo
+ err error
)
switch v := val; v.Kind() {
default:
return errors.New("unknown type " + v.Type().String())
+ case reflect.Interface:
+ // TODO: For now, simply ignore the field. In the near
+ // future we may choose to unmarshal the start
+ // element on it, if not nil.
+ return p.Skip()
+
case reflect.Slice:
typ := v.Type()
if typ.Elem().Kind() == reflect.Uint8 {
@@ -288,75 +269,69 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) error {
saveData = v
case reflect.Struct:
- if _, ok := v.Interface().(Name); ok {
- v.Set(reflect.ValueOf(start.Name))
- break
- }
-
sv = v
typ := sv.Type()
- styp = typ
- // Assign name.
- if f, ok := typ.FieldByName("XMLName"); ok {
- // Validate element name.
- if tag := f.Tag.Get("xml"); tag != "" {
- ns := ""
- i := strings.LastIndex(tag, " ")
- if i >= 0 {
- ns, tag = tag[0:i], tag[i+1:]
- }
- if tag != start.Name.Local {
- return UnmarshalError("expected element type <" + tag + "> but have <" + start.Name.Local + ">")
- }
- if ns != "" && ns != start.Name.Space {
- e := "expected element <" + tag + "> in name space " + ns + " but have "
- if start.Name.Space == "" {
- e += "no name space"
- } else {
- e += start.Name.Space
- }
- return UnmarshalError(e)
+ tinfo, err = getTypeInfo(typ)
+ if err != nil {
+ return err
+ }
+
+ // Validate and assign element name.
+ if tinfo.xmlname != nil {
+ finfo := tinfo.xmlname
+ if finfo.name != "" && finfo.name != start.Name.Local {
+ return UnmarshalError("expected element type <" + finfo.name + "> but have <" + start.Name.Local + ">")
+ }
+ if finfo.xmlns != "" && finfo.xmlns != start.Name.Space {
+ e := "expected element <" + finfo.name + "> in name space " + finfo.xmlns + " but have "
+ if start.Name.Space == "" {
+ e += "no name space"
+ } else {
+ e += start.Name.Space
}
+ return UnmarshalError(e)
}
-
- // Save
- v := sv.FieldByIndex(f.Index)
- if _, ok := v.Interface().(Name); ok {
- v.Set(reflect.ValueOf(start.Name))
+ fv := sv.FieldByIndex(finfo.idx)
+ if _, ok := fv.Interface().(Name); ok {
+ fv.Set(reflect.ValueOf(start.Name))
}
}
// Assign attributes.
// Also, determine whether we need to save character data or comments.
- for i, n := 0, typ.NumField(); i < n; i++ {
- f := typ.Field(i)
- switch f.Tag.Get("xml") {
- case "attr":
- strv := sv.FieldByIndex(f.Index)
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ switch finfo.flags & fMode {
+ case fAttr:
+ strv := sv.FieldByIndex(finfo.idx)
// Look for attribute.
val := ""
- k := strings.ToLower(f.Name)
for _, a := range start.Attr {
- if fieldName(a.Name.Local) == k {
+ if a.Name.Local == finfo.name {
val = a.Value
break
}
}
copyValue(strv, []byte(val))
- case "comment":
+ case fCharData:
+ if !saveData.IsValid() {
+ saveData = sv.FieldByIndex(finfo.idx)
+ }
+
+ case fComment:
if !saveComment.IsValid() {
- saveComment = sv.FieldByIndex(f.Index)
+ saveComment = sv.FieldByIndex(finfo.idx)
}
- case "chardata":
- if !saveData.IsValid() {
- saveData = sv.FieldByIndex(f.Index)
+ case fAny:
+ if !saveAny.IsValid() {
+ saveAny = sv.FieldByIndex(finfo.idx)
}
- case "innerxml":
+ case fInnerXml:
if !saveXML.IsValid() {
- saveXML = sv.FieldByIndex(f.Index)
+ saveXML = sv.FieldByIndex(finfo.idx)
if p.saved == nil {
saveXMLIndex = 0
p.saved = new(bytes.Buffer)
@@ -364,24 +339,6 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) error {
saveXMLIndex = p.savedOffset()
}
}
-
- default:
- if tag := f.Tag.Get("xml"); strings.Contains(tag, ">") {
- if fieldPaths == nil {
- fieldPaths = make(map[string]pathInfo)
- }
- path := strings.ToLower(tag)
- if strings.HasPrefix(tag, ">") {
- path = strings.ToLower(f.Name) + path
- }
- if strings.HasSuffix(tag, ">") {
- path = path[:len(path)-1]
- }
- err := addFieldPath(sv, fieldPaths, path, f.Index)
- if err != nil {
- return err
- }
- }
}
}
}
@@ -400,44 +357,23 @@ Loop:
}
switch t := tok.(type) {
case StartElement:
- // Sub-element.
- // Look up by tag name.
+ consumed := false
if sv.IsValid() {
- k := fieldName(t.Name.Local)
-
- if fieldPaths != nil {
- if _, found := fieldPaths[k]; found {
- if err := p.unmarshalPaths(sv, fieldPaths, k, &t); err != nil {
- return err
- }
- continue Loop
- }
- }
-
- match := func(s string) bool {
- // check if the name matches ignoring case
- if strings.ToLower(s) != k {
- return false
- }
- // now check that it's public
- c, _ := utf8.DecodeRuneInString(s)
- return unicode.IsUpper(c)
- }
-
- f, found := styp.FieldByNameFunc(match)
- if !found { // fall back to mop-up field named "Any"
- f, found = styp.FieldByName("Any")
+ consumed, err = p.unmarshalPath(tinfo, sv, nil, &t)
+ if err != nil {
+ return err
}
- if found {
- if err := p.unmarshal(sv.FieldByIndex(f.Index), &t); err != nil {
+ if !consumed && saveAny.IsValid() {
+ consumed = true
+ if err := p.unmarshal(saveAny, &t); err != nil {
return err
}
- continue Loop
}
}
- // Not saving sub-element but still have to skip over it.
- if err := p.Skip(); err != nil {
- return err
+ if !consumed {
+ if err := p.Skip(); err != nil {
+ return err
+ }
}
case EndElement:
@@ -503,10 +439,10 @@ func copyValue(dst reflect.Value, src []byte) (err error) {
return err == nil
}
- // Save accumulated data and comments
+ // Save accumulated data.
switch t := dst; t.Kind() {
case reflect.Invalid:
- // Probably a comment, handled below
+ // Probably a comment.
default:
return errors.New("cannot happen: unknown type " + t.Type().String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -538,70 +474,66 @@ func copyValue(dst reflect.Value, src []byte) (err error) {
return nil
}
-type pathInfo struct {
- fieldIdx []int
- complete bool
-}
-
-// addFieldPath takes an element path such as "a>b>c" and fills the
-// paths map with all paths leading to it ("a", "a>b", and "a>b>c").
-// It is okay for paths to share a common, shorter prefix but not ok
-// for one path to itself be a prefix of another.
-func addFieldPath(sv reflect.Value, paths map[string]pathInfo, path string, fieldIdx []int) error {
- if info, found := paths[path]; found {
- return tagError(sv, info.fieldIdx, fieldIdx)
- }
- paths[path] = pathInfo{fieldIdx, true}
- for {
- i := strings.LastIndex(path, ">")
- if i < 0 {
- break
+// unmarshalPath walks down an XML structure looking for wanted
+// paths, and calls unmarshal on them.
+// The consumed result tells whether XML elements have been consumed
+// from the Parser until start's matching end element, or if it's
+// still untouched because start is uninteresting for sv's fields.
+func (p *Parser) unmarshalPath(tinfo *typeInfo, sv reflect.Value, parents []string, start *StartElement) (consumed bool, err error) {
+ recurse := false
+Loop:
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ if finfo.flags&fElement == 0 || len(finfo.parents) < len(parents) {
+ continue
}
- path = path[:i]
- if info, found := paths[path]; found {
- if info.complete {
- return tagError(sv, info.fieldIdx, fieldIdx)
+ for j := range parents {
+ if parents[j] != finfo.parents[j] {
+ continue Loop
}
- } else {
- paths[path] = pathInfo{fieldIdx, false}
+ }
+ if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
+ // It's a perfect match, unmarshal the field.
+ return true, p.unmarshal(sv.FieldByIndex(finfo.idx), start)
+ }
+ if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local {
+ // It's a prefix for the field. Break and recurse
+ // since it's not ok for one field path to be itself
+ // the prefix for another field path.
+ recurse = true
+
+ // We can reuse the same slice as long as we
+ // don't try to append to it.
+ parents = finfo.parents[:len(parents)+1]
+ break
}
}
- return nil
-
-}
-
-func tagError(sv reflect.Value, idx1 []int, idx2 []int) error {
- t := sv.Type()
- f1 := t.FieldByIndex(idx1)
- f2 := t.FieldByIndex(idx2)
- return &TagPathError{t, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
-}
-
-// unmarshalPaths walks down an XML structure looking for
-// wanted paths, and calls unmarshal on them.
-func (p *Parser) unmarshalPaths(sv reflect.Value, paths map[string]pathInfo, path string, start *StartElement) error {
- if info, _ := paths[path]; info.complete {
- return p.unmarshal(sv.FieldByIndex(info.fieldIdx), start)
+ if !recurse {
+ // We have no business with this element.
+ return false, nil
}
+ // The element is not a perfect match for any field, but one
+ // or more fields have the path to this element as a parent
+ // prefix. Recurse and attempt to match these.
for {
- tok, err := p.Token()
+ var tok Token
+ tok, err = p.Token()
if err != nil {
- return err
+ return true, err
}
switch t := tok.(type) {
case StartElement:
- k := path + ">" + fieldName(t.Name.Local)
- if _, found := paths[k]; found {
- if err := p.unmarshalPaths(sv, paths, k, &t); err != nil {
- return err
- }
- continue
+ consumed2, err := p.unmarshalPath(tinfo, sv, parents, &t)
+ if err != nil {
+ return true, err
}
- if err := p.Skip(); err != nil {
- return err
+ if !consumed2 {
+ if err := p.Skip(); err != nil {
+ return true, err
+ }
}
case EndElement:
- return nil
+ return true, nil
}
}
panic("unreachable")
diff --git a/libgo/go/encoding/xml/read_test.go b/libgo/go/encoding/xml/read_test.go
index fbb7fd5..ff61bd7 100644
--- a/libgo/go/encoding/xml/read_test.go
+++ b/libgo/go/encoding/xml/read_test.go
@@ -6,6 +6,7 @@ package xml
import (
"reflect"
+ "strings"
"testing"
)
@@ -13,7 +14,7 @@ import (
func TestUnmarshalFeed(t *testing.T) {
var f Feed
- if err := Unmarshal(StringReader(atomFeedString), &f); err != nil {
+ if err := Unmarshal(strings.NewReader(atomFeedString), &f); err != nil {
t.Fatalf("Unmarshal: %s", err)
}
if !reflect.DeepEqual(f, atomFeed) {
@@ -24,8 +25,8 @@ func TestUnmarshalFeed(t *testing.T) {
// hget http://codereview.appspot.com/rss/mine/rsc
const atomFeedString = `
<?xml version="1.0" encoding="utf-8"?>
-<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><li-nk href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></li-nk><id>http://codereview.appspot.com/</id><updated>2009-10-04T01:35:58+00:00</updated><author><name>rietveld&lt;&gt;</name></author><entry><title>rietveld: an attempt at pubsubhubbub
-</title><link hre-f="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
+<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><updated>2009-10-04T01:35:58+00:00</updated><author><name>rietveld&lt;&gt;</name></author><entry><title>rietveld: an attempt at pubsubhubbub
+</title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
An attempt at adding pubsubhubbub support to Rietveld.
http://code.google.com/p/pubsubhubbub
http://code.google.com/p/rietveld/issues/detail?id=155
@@ -78,39 +79,39 @@ not being used from outside intra_region_diff.py.
</summary></entry></feed> `
type Feed struct {
- XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
- Title string
- Id string
- Link []Link
- Updated Time
- Author Person
- Entry []Entry
+ XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
+ Title string `xml:"title"`
+ Id string `xml:"id"`
+ Link []Link `xml:"link"`
+ Updated Time `xml:"updated"`
+ Author Person `xml:"author"`
+ Entry []Entry `xml:"entry"`
}
type Entry struct {
- Title string
- Id string
- Link []Link
- Updated Time
- Author Person
- Summary Text
+ Title string `xml:"title"`
+ Id string `xml:"id"`
+ Link []Link `xml:"link"`
+ Updated Time `xml:"updated"`
+ Author Person `xml:"author"`
+ Summary Text `xml:"summary"`
}
type Link struct {
- Rel string `xml:"attr"`
- Href string `xml:"attr"`
+ Rel string `xml:"rel,attr"`
+ Href string `xml:"href,attr"`
}
type Person struct {
- Name string
- URI string
- Email string
- InnerXML string `xml:"innerxml"`
+ Name string `xml:"name"`
+ URI string `xml:"uri"`
+ Email string `xml:"email"`
+ InnerXML string `xml:",innerxml"`
}
type Text struct {
- Type string `xml:"attr"`
- Body string `xml:"chardata"`
+ Type string `xml:"type,attr"`
+ Body string `xml:",chardata"`
}
type Time string
@@ -213,44 +214,26 @@ not being used from outside intra_region_diff.py.
},
}
-type FieldNameTest struct {
- in, out string
-}
-
-var FieldNameTests = []FieldNameTest{
- {"Profile-Image", "profileimage"},
- {"_score", "score"},
-}
-
-func TestFieldName(t *testing.T) {
- for _, tt := range FieldNameTests {
- a := fieldName(tt.in)
- if a != tt.out {
- t.Fatalf("have %#v\nwant %#v\n\n", a, tt.out)
- }
- }
-}
-
const pathTestString = `
-<result>
- <before>1</before>
- <items>
- <item1>
- <value>A</value>
- </item1>
- <item2>
- <value>B</value>
- </item2>
+<Result>
+ <Before>1</Before>
+ <Items>
+ <Item1>
+ <Value>A</Value>
+ </Item1>
+ <Item2>
+ <Value>B</Value>
+ </Item2>
<Item1>
<Value>C</Value>
<Value>D</Value>
</Item1>
<_>
- <value>E</value>
+ <Value>E</Value>
</_>
- </items>
- <after>2</after>
-</result>
+ </Items>
+ <After>2</After>
+</Result>
`
type PathTestItem struct {
@@ -258,18 +241,18 @@ type PathTestItem struct {
}
type PathTestA struct {
- Items []PathTestItem `xml:">item1"`
+ Items []PathTestItem `xml:">Item1"`
Before, After string
}
type PathTestB struct {
- Other []PathTestItem `xml:"items>Item1"`
+ Other []PathTestItem `xml:"Items>Item1"`
Before, After string
}
type PathTestC struct {
- Values1 []string `xml:"items>item1>value"`
- Values2 []string `xml:"items>item2>value"`
+ Values1 []string `xml:"Items>Item1>Value"`
+ Values2 []string `xml:"Items>Item2>Value"`
Before, After string
}
@@ -278,12 +261,12 @@ type PathTestSet struct {
}
type PathTestD struct {
- Other PathTestSet `xml:"items>"`
+ Other PathTestSet `xml:"Items"`
Before, After string
}
type PathTestE struct {
- Underline string `xml:"items>_>value"`
+ Underline string `xml:"Items>_>Value"`
Before, After string
}
@@ -298,7 +281,7 @@ var pathTests = []interface{}{
func TestUnmarshalPaths(t *testing.T) {
for _, pt := range pathTests {
v := reflect.New(reflect.TypeOf(pt).Elem()).Interface()
- if err := Unmarshal(StringReader(pathTestString), v); err != nil {
+ if err := Unmarshal(strings.NewReader(pathTestString), v); err != nil {
t.Fatalf("Unmarshal: %s", err)
}
if !reflect.DeepEqual(v, pt) {
@@ -310,7 +293,7 @@ func TestUnmarshalPaths(t *testing.T) {
type BadPathTestA struct {
First string `xml:"items>item1"`
Other string `xml:"items>item2"`
- Second string `xml:"items>"`
+ Second string `xml:"items"`
}
type BadPathTestB struct {
@@ -319,81 +302,55 @@ type BadPathTestB struct {
Second string `xml:"items>item1>value"`
}
+type BadPathTestC struct {
+ First string
+ Second string `xml:"First"`
+}
+
+type BadPathTestD struct {
+ BadPathEmbeddedA
+ BadPathEmbeddedB
+}
+
+type BadPathEmbeddedA struct {
+ First string
+}
+
+type BadPathEmbeddedB struct {
+ Second string `xml:"First"`
+}
+
var badPathTests = []struct {
v, e interface{}
}{
- {&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items>"}},
+ {&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items"}},
{&BadPathTestB{}, &TagPathError{reflect.TypeOf(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}},
+ {&BadPathTestC{}, &TagPathError{reflect.TypeOf(BadPathTestC{}), "First", "", "Second", "First"}},
+ {&BadPathTestD{}, &TagPathError{reflect.TypeOf(BadPathTestD{}), "First", "", "Second", "First"}},
}
func TestUnmarshalBadPaths(t *testing.T) {
for _, tt := range badPathTests {
- err := Unmarshal(StringReader(pathTestString), tt.v)
+ err := Unmarshal(strings.NewReader(pathTestString), tt.v)
if !reflect.DeepEqual(err, tt.e) {
- t.Fatalf("Unmarshal with %#v didn't fail properly: %#v", tt.v, err)
+ t.Fatalf("Unmarshal with %#v didn't fail properly:\nhave %#v,\nwant %#v", tt.v, err, tt.e)
}
}
}
-func TestUnmarshalAttrs(t *testing.T) {
- var f AttrTest
- if err := Unmarshal(StringReader(attrString), &f); err != nil {
- t.Fatalf("Unmarshal: %s", err)
- }
- if !reflect.DeepEqual(f, attrStruct) {
- t.Fatalf("have %#v\nwant %#v", f, attrStruct)
- }
-}
-
-type AttrTest struct {
- Test1 Test1
- Test2 Test2
-}
-
-type Test1 struct {
- Int int `xml:"attr"`
- Float float64 `xml:"attr"`
- Uint8 uint8 `xml:"attr"`
-}
-
-type Test2 struct {
- Bool bool `xml:"attr"`
-}
-
-const attrString = `
-<?xml version="1.0" charset="utf-8"?>
-<attrtest>
- <test1 int="8" float="23.5" uint8="255"/>
- <test2 bool="true"/>
-</attrtest>
-`
-
-var attrStruct = AttrTest{
- Test1: Test1{
- Int: 8,
- Float: 23.5,
- Uint8: 255,
- },
- Test2: Test2{
- Bool: true,
- },
-}
-
-// test data for TestUnmarshalWithoutNameType
-
const OK = "OK"
const withoutNameTypeData = `
<?xml version="1.0" charset="utf-8"?>
-<Test3 attr="OK" />`
+<Test3 Attr="OK" />`
type TestThree struct {
- XMLName bool `xml:"Test3"` // XMLName field without an xml.Name type
- Attr string `xml:"attr"`
+ XMLName Name `xml:"Test3"`
+ Attr string `xml:",attr"`
}
func TestUnmarshalWithoutNameType(t *testing.T) {
var x TestThree
- if err := Unmarshal(StringReader(withoutNameTypeData), &x); err != nil {
+ if err := Unmarshal(strings.NewReader(withoutNameTypeData), &x); err != nil {
t.Fatalf("Unmarshal: %s", err)
}
if x.Attr != OK {
diff --git a/libgo/go/encoding/xml/typeinfo.go b/libgo/go/encoding/xml/typeinfo.go
new file mode 100644
index 0000000..8f79c4e
--- /dev/null
+++ b/libgo/go/encoding/xml/typeinfo.go
@@ -0,0 +1,321 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xml
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+)
+
+// typeInfo holds details for the xml representation of a type.
+type typeInfo struct {
+ xmlname *fieldInfo
+ fields []fieldInfo
+}
+
+// fieldInfo holds details for the xml representation of a single field.
+type fieldInfo struct {
+ idx []int
+ name string
+ xmlns string
+ flags fieldFlags
+ parents []string
+}
+
+type fieldFlags int
+
+const (
+ fElement fieldFlags = 1 << iota
+ fAttr
+ fCharData
+ fInnerXml
+ fComment
+ fAny
+
+ // TODO:
+ //fIgnore
+ //fOmitEmpty
+
+ fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
+)
+
+var tinfoMap = make(map[reflect.Type]*typeInfo)
+var tinfoLock sync.RWMutex
+
+// getTypeInfo returns the typeInfo structure with details necessary
+// for marshalling and unmarshalling typ.
+func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
+ tinfoLock.RLock()
+ tinfo, ok := tinfoMap[typ]
+ tinfoLock.RUnlock()
+ if ok {
+ return tinfo, nil
+ }
+ tinfo = &typeInfo{}
+ if typ.Kind() == reflect.Struct {
+ n := typ.NumField()
+ for i := 0; i < n; i++ {
+ f := typ.Field(i)
+ if f.PkgPath != "" {
+ continue // Private field
+ }
+
+ // For embedded structs, embed its fields.
+ if f.Anonymous {
+ if f.Type.Kind() != reflect.Struct {
+ continue
+ }
+ inner, err := getTypeInfo(f.Type)
+ if err != nil {
+ return nil, err
+ }
+ for _, finfo := range inner.fields {
+ finfo.idx = append([]int{i}, finfo.idx...)
+ if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
+ return nil, err
+ }
+ }
+ continue
+ }
+
+ finfo, err := structFieldInfo(typ, &f)
+ if err != nil {
+ return nil, err
+ }
+
+ if f.Name == "XMLName" {
+ tinfo.xmlname = finfo
+ continue
+ }
+
+ // Add the field if it doesn't conflict with other fields.
+ if err := addFieldInfo(typ, tinfo, finfo); err != nil {
+ return nil, err
+ }
+ }
+ }
+ tinfoLock.Lock()
+ tinfoMap[typ] = tinfo
+ tinfoLock.Unlock()
+ return tinfo, nil
+}
+
+// structFieldInfo builds and returns a fieldInfo for f.
+func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) {
+ finfo := &fieldInfo{idx: f.Index}
+
+ // Split the tag from the xml namespace if necessary.
+ tag := f.Tag.Get("xml")
+ if i := strings.Index(tag, " "); i >= 0 {
+ finfo.xmlns, tag = tag[:i], tag[i+1:]
+ }
+
+ // Parse flags.
+ tokens := strings.Split(tag, ",")
+ if len(tokens) == 1 {
+ finfo.flags = fElement
+ } else {
+ tag = tokens[0]
+ for _, flag := range tokens[1:] {
+ switch flag {
+ case "attr":
+ finfo.flags |= fAttr
+ case "chardata":
+ finfo.flags |= fCharData
+ case "innerxml":
+ finfo.flags |= fInnerXml
+ case "comment":
+ finfo.flags |= fComment
+ case "any":
+ finfo.flags |= fAny
+ }
+ }
+
+ // Validate the flags used.
+ switch mode := finfo.flags & fMode; mode {
+ case 0:
+ finfo.flags |= fElement
+ case fAttr, fCharData, fInnerXml, fComment, fAny:
+ if f.Name != "XMLName" && (tag == "" || mode == fAttr) {
+ break
+ }
+ fallthrough
+ default:
+ // This will also catch multiple modes in a single field.
+ return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
+ f.Name, typ, f.Tag.Get("xml"))
+ }
+ }
+
+ // Use of xmlns without a name is not allowed.
+ if finfo.xmlns != "" && tag == "" {
+ return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
+ f.Name, typ, f.Tag.Get("xml"))
+ }
+
+ if f.Name == "XMLName" {
+ // The XMLName field records the XML element name. Don't
+ // process it as usual because its name should default to
+ // empty rather than to the field name.
+ finfo.name = tag
+ return finfo, nil
+ }
+
+ if tag == "" {
+ // If the name part of the tag is completely empty, get
+ // default from XMLName of underlying struct if feasible,
+ // or field name otherwise.
+ if xmlname := lookupXMLName(f.Type); xmlname != nil {
+ finfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name
+ } else {
+ finfo.name = f.Name
+ }
+ return finfo, nil
+ }
+
+ // Prepare field name and parents.
+ tokens = strings.Split(tag, ">")
+ if tokens[0] == "" {
+ tokens[0] = f.Name
+ }
+ if tokens[len(tokens)-1] == "" {
+ return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
+ }
+ finfo.name = tokens[len(tokens)-1]
+ if len(tokens) > 1 {
+ finfo.parents = tokens[:len(tokens)-1]
+ }
+
+ // If the field type has an XMLName field, the names must match
+ // so that the behavior of both marshalling and unmarshalling
+ // is straighforward and unambiguous.
+ if finfo.flags&fElement != 0 {
+ ftyp := f.Type
+ xmlname := lookupXMLName(ftyp)
+ if xmlname != nil && xmlname.name != finfo.name {
+ return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
+ finfo.name, typ, f.Name, xmlname.name, ftyp)
+ }
+ }
+ return finfo, nil
+}
+
+// lookupXMLName returns the fieldInfo for typ's XMLName field
+// in case it exists and has a valid xml field tag, otherwise
+// it returns nil.
+func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
+ for typ.Kind() == reflect.Ptr {
+ typ = typ.Elem()
+ }
+ if typ.Kind() != reflect.Struct {
+ return nil
+ }
+ for i, n := 0, typ.NumField(); i < n; i++ {
+ f := typ.Field(i)
+ if f.Name != "XMLName" {
+ continue
+ }
+ finfo, err := structFieldInfo(typ, &f)
+ if finfo.name != "" && err == nil {
+ return finfo
+ }
+ // Also consider errors as a non-existent field tag
+ // and let getTypeInfo itself report the error.
+ break
+ }
+ return nil
+}
+
+func min(a, b int) int {
+ if a <= b {
+ return a
+ }
+ return b
+}
+
+// addFieldInfo adds finfo to tinfo.fields if there are no
+// conflicts, or if conflicts arise from previous fields that were
+// obtained from deeper embedded structures than finfo. In the latter
+// case, the conflicting entries are dropped.
+// A conflict occurs when the path (parent + name) to a field is
+// itself a prefix of another path, or when two paths match exactly.
+// It is okay for field paths to share a common, shorter prefix.
+func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
+ var conflicts []int
+Loop:
+ // First, figure all conflicts. Most working code will have none.
+ for i := range tinfo.fields {
+ oldf := &tinfo.fields[i]
+ if oldf.flags&fMode != newf.flags&fMode {
+ continue
+ }
+ minl := min(len(newf.parents), len(oldf.parents))
+ for p := 0; p < minl; p++ {
+ if oldf.parents[p] != newf.parents[p] {
+ continue Loop
+ }
+ }
+ if len(oldf.parents) > len(newf.parents) {
+ if oldf.parents[len(newf.parents)] == newf.name {
+ conflicts = append(conflicts, i)
+ }
+ } else if len(oldf.parents) < len(newf.parents) {
+ if newf.parents[len(oldf.parents)] == oldf.name {
+ conflicts = append(conflicts, i)
+ }
+ } else {
+ if newf.name == oldf.name {
+ conflicts = append(conflicts, i)
+ }
+ }
+ }
+ // Without conflicts, add the new field and return.
+ if conflicts == nil {
+ tinfo.fields = append(tinfo.fields, *newf)
+ return nil
+ }
+
+ // If any conflict is shallower, ignore the new field.
+ // This matches the Go field resolution on embedding.
+ for _, i := range conflicts {
+ if len(tinfo.fields[i].idx) < len(newf.idx) {
+ return nil
+ }
+ }
+
+ // Otherwise, if any of them is at the same depth level, it's an error.
+ for _, i := range conflicts {
+ oldf := &tinfo.fields[i]
+ if len(oldf.idx) == len(newf.idx) {
+ f1 := typ.FieldByIndex(oldf.idx)
+ f2 := typ.FieldByIndex(newf.idx)
+ return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
+ }
+ }
+
+ // Otherwise, the new field is shallower, and thus takes precedence,
+ // so drop the conflicting fields from tinfo and append the new one.
+ for c := len(conflicts) - 1; c >= 0; c-- {
+ i := conflicts[c]
+ copy(tinfo.fields[i:], tinfo.fields[i+1:])
+ tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
+ }
+ tinfo.fields = append(tinfo.fields, *newf)
+ return nil
+}
+
+// A TagPathError represents an error in the unmarshalling process
+// caused by the use of field tags with conflicting paths.
+type TagPathError struct {
+ Struct reflect.Type
+ Field1, Tag1 string
+ Field2, Tag2 string
+}
+
+func (e *TagPathError) Error() string {
+ return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
+}
diff --git a/libgo/go/encoding/xml/xml_test.go b/libgo/go/encoding/xml/xml_test.go
index 25ffc91..524d4dda 100644
--- a/libgo/go/encoding/xml/xml_test.go
+++ b/libgo/go/encoding/xml/xml_test.go
@@ -154,36 +154,8 @@ var xmlInput = []string{
"<t>cdata]]></t>",
}
-type stringReader struct {
- s string
- off int
-}
-
-func (r *stringReader) Read(b []byte) (n int, err error) {
- if r.off >= len(r.s) {
- return 0, io.EOF
- }
- for r.off < len(r.s) && n < len(b) {
- b[n] = r.s[r.off]
- n++
- r.off++
- }
- return
-}
-
-func (r *stringReader) ReadByte() (b byte, err error) {
- if r.off >= len(r.s) {
- return 0, io.EOF
- }
- b = r.s[r.off]
- r.off++
- return
-}
-
-func StringReader(s string) io.Reader { return &stringReader{s, 0} }
-
func TestRawToken(t *testing.T) {
- p := NewParser(StringReader(testInput))
+ p := NewParser(strings.NewReader(testInput))
testRawToken(t, p, rawTokens)
}
@@ -207,7 +179,7 @@ func (d *downCaser) Read(p []byte) (int, error) {
func TestRawTokenAltEncoding(t *testing.T) {
sawEncoding := ""
- p := NewParser(StringReader(testInputAltEncoding))
+ p := NewParser(strings.NewReader(testInputAltEncoding))
p.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
sawEncoding = charset
if charset != "x-testing-uppercase" {
@@ -219,7 +191,7 @@ func TestRawTokenAltEncoding(t *testing.T) {
}
func TestRawTokenAltEncodingNoConverter(t *testing.T) {
- p := NewParser(StringReader(testInputAltEncoding))
+ p := NewParser(strings.NewReader(testInputAltEncoding))
token, err := p.RawToken()
if token == nil {
t.Fatalf("expected a token on first RawToken call")
@@ -286,7 +258,7 @@ var nestedDirectivesTokens = []Token{
}
func TestNestedDirectives(t *testing.T) {
- p := NewParser(StringReader(nestedDirectivesInput))
+ p := NewParser(strings.NewReader(nestedDirectivesInput))
for i, want := range nestedDirectivesTokens {
have, err := p.Token()
@@ -300,7 +272,7 @@ func TestNestedDirectives(t *testing.T) {
}
func TestToken(t *testing.T) {
- p := NewParser(StringReader(testInput))
+ p := NewParser(strings.NewReader(testInput))
for i, want := range cookedTokens {
have, err := p.Token()
@@ -315,7 +287,7 @@ func TestToken(t *testing.T) {
func TestSyntax(t *testing.T) {
for i := range xmlInput {
- p := NewParser(StringReader(xmlInput[i]))
+ p := NewParser(strings.NewReader(xmlInput[i]))
var err error
for _, err = p.Token(); err == nil; _, err = p.Token() {
}
@@ -372,26 +344,26 @@ var all = allScalars{
var sixteen = "16"
const testScalarsInput = `<allscalars>
- <true1>true</true1>
- <true2>1</true2>
- <false1>false</false1>
- <false2>0</false2>
- <int>1</int>
- <int8>-2</int8>
- <int16>3</int16>
- <int32>-4</int32>
- <int64>5</int64>
- <uint>6</uint>
- <uint8>7</uint8>
- <uint16>8</uint16>
- <uint32>9</uint32>
- <uint64>10</uint64>
- <uintptr>11</uintptr>
- <float>12.0</float>
- <float32>13.0</float32>
- <float64>14.0</float64>
- <string>15</string>
- <ptrstring>16</ptrstring>
+ <True1>true</True1>
+ <True2>1</True2>
+ <False1>false</False1>
+ <False2>0</False2>
+ <Int>1</Int>
+ <Int8>-2</Int8>
+ <Int16>3</Int16>
+ <Int32>-4</Int32>
+ <Int64>5</Int64>
+ <Uint>6</Uint>
+ <Uint8>7</Uint8>
+ <Uint16>8</Uint16>
+ <Uint32>9</Uint32>
+ <Uint64>10</Uint64>
+ <Uintptr>11</Uintptr>
+ <Float>12.0</Float>
+ <Float32>13.0</Float32>
+ <Float64>14.0</Float64>
+ <String>15</String>
+ <PtrString>16</PtrString>
</allscalars>`
func TestAllScalars(t *testing.T) {
@@ -412,7 +384,7 @@ type item struct {
}
func TestIssue569(t *testing.T) {
- data := `<item><field_a>abcd</field_a></item>`
+ data := `<item><Field_a>abcd</Field_a></item>`
var i item
buf := bytes.NewBufferString(data)
err := Unmarshal(buf, &i)
@@ -424,7 +396,7 @@ func TestIssue569(t *testing.T) {
func TestUnquotedAttrs(t *testing.T) {
data := "<tag attr=azAZ09:-_\t>"
- p := NewParser(StringReader(data))
+ p := NewParser(strings.NewReader(data))
p.Strict = false
token, err := p.Token()
if _, ok := err.(*SyntaxError); ok {
@@ -450,7 +422,7 @@ func TestValuelessAttrs(t *testing.T) {
{"<input checked />", "input", "checked"},
}
for _, test := range tests {
- p := NewParser(StringReader(test[0]))
+ p := NewParser(strings.NewReader(test[0]))
p.Strict = false
token, err := p.Token()
if _, ok := err.(*SyntaxError); ok {
@@ -500,7 +472,7 @@ func TestCopyTokenStartElement(t *testing.T) {
func TestSyntaxErrorLineNum(t *testing.T) {
testInput := "<P>Foo<P>\n\n<P>Bar</>\n"
- p := NewParser(StringReader(testInput))
+ p := NewParser(strings.NewReader(testInput))
var err error
for _, err = p.Token(); err == nil; _, err = p.Token() {
}
@@ -515,7 +487,7 @@ func TestSyntaxErrorLineNum(t *testing.T) {
func TestTrailingRawToken(t *testing.T) {
input := `<FOO></FOO> `
- p := NewParser(StringReader(input))
+ p := NewParser(strings.NewReader(input))
var err error
for _, err = p.RawToken(); err == nil; _, err = p.RawToken() {
}
@@ -526,7 +498,7 @@ func TestTrailingRawToken(t *testing.T) {
func TestTrailingToken(t *testing.T) {
input := `<FOO></FOO> `
- p := NewParser(StringReader(input))
+ p := NewParser(strings.NewReader(input))
var err error
for _, err = p.Token(); err == nil; _, err = p.Token() {
}
@@ -537,7 +509,7 @@ func TestTrailingToken(t *testing.T) {
func TestEntityInsideCDATA(t *testing.T) {
input := `<test><![CDATA[ &val=foo ]]></test>`
- p := NewParser(StringReader(input))
+ p := NewParser(strings.NewReader(input))
var err error
for _, err = p.Token(); err == nil; _, err = p.Token() {
}
@@ -569,7 +541,7 @@ var characterTests = []struct {
func TestDisallowedCharacters(t *testing.T) {
for i, tt := range characterTests {
- p := NewParser(StringReader(tt.in))
+ p := NewParser(strings.NewReader(tt.in))
var err error
for err == nil {