mirror of
https://github.com/golang/go
synced 2024-11-22 03:24:41 -07:00
encoding/xml: improve []byte handling
Marshalling of []byte in attributes and the general marshalling of named []byte types was fixed. A []byte field also won't be nil if an XML element was mapped to it, even if the element is empty. Tests were introduced to make sure that *struct{} fields works correctly for element presence testing. No changes to the logic made in that regard. R=rsc CC=golang-dev https://golang.org/cl/5539070
This commit is contained in:
parent
9d47526585
commit
57007fe12b
@ -181,23 +181,43 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
|
|||||||
if finfo.flags&fAttr == 0 {
|
if finfo.flags&fAttr == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var str string
|
fv := val.FieldByIndex(finfo.idx)
|
||||||
if fv := val.FieldByIndex(finfo.idx); fv.Kind() == reflect.String {
|
switch fv.Kind() {
|
||||||
str = fv.String()
|
case reflect.String, reflect.Array, reflect.Slice:
|
||||||
} else {
|
// TODO: Should we really do this once ,omitempty is in?
|
||||||
str = fmt.Sprint(fv.Interface())
|
if fv.Len() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if str != "" {
|
p.WriteByte(' ')
|
||||||
p.WriteByte(' ')
|
p.WriteString(finfo.name)
|
||||||
p.WriteString(finfo.name)
|
p.WriteString(`="`)
|
||||||
p.WriteString(`="`)
|
if err := p.marshalSimple(fv.Type(), fv); err != nil {
|
||||||
Escape(p, []byte(str))
|
return err
|
||||||
p.WriteByte('"')
|
|
||||||
}
|
}
|
||||||
|
p.WriteByte('"')
|
||||||
}
|
}
|
||||||
p.WriteByte('>')
|
p.WriteByte('>')
|
||||||
|
|
||||||
switch k := val.Kind(); k {
|
if val.Kind() == reflect.Struct {
|
||||||
|
err = p.marshalStruct(tinfo, val)
|
||||||
|
} else {
|
||||||
|
err = p.marshalSimple(typ, val)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.WriteByte('<')
|
||||||
|
p.WriteByte('/')
|
||||||
|
p.WriteString(name)
|
||||||
|
p.WriteByte('>')
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error {
|
||||||
|
switch val.Kind() {
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
p.WriteString(strconv.FormatInt(val.Int(), 10))
|
p.WriteString(strconv.FormatInt(val.Int(), 10))
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
@ -205,6 +225,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
|
|||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, 64))
|
p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, 64))
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
|
// TODO: Add EscapeString.
|
||||||
Escape(p, []byte(val.String()))
|
Escape(p, []byte(val.String()))
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
p.WriteString(strconv.FormatBool(val.Bool()))
|
p.WriteString(strconv.FormatBool(val.Bool()))
|
||||||
@ -217,21 +238,10 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
|
|||||||
Escape(p, bytes)
|
Escape(p, bytes)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
// will be []byte
|
// will be []byte
|
||||||
bytes := val.Interface().([]byte)
|
Escape(p, val.Bytes())
|
||||||
Escape(p, bytes)
|
|
||||||
case reflect.Struct:
|
|
||||||
if err := p.marshalStruct(tinfo, val); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return &UnsupportedTypeError{typ}
|
return &UnsupportedTypeError{typ}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.WriteByte('<')
|
|
||||||
p.WriteByte('/')
|
|
||||||
p.WriteString(name)
|
|
||||||
p.WriteByte('>')
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +184,18 @@ type RecurseB struct {
|
|||||||
B string
|
B string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PresenceTest struct {
|
||||||
|
Exists *struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyBytes []byte
|
||||||
|
|
||||||
|
type Data struct {
|
||||||
|
Bytes []byte
|
||||||
|
Attr []byte `xml:",attr"`
|
||||||
|
Custom MyBytes
|
||||||
|
}
|
||||||
|
|
||||||
type Plain struct {
|
type Plain struct {
|
||||||
V interface{}
|
V interface{}
|
||||||
}
|
}
|
||||||
@ -225,6 +237,44 @@ var marshalTests = []struct {
|
|||||||
{Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</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>`},
|
{Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
|
||||||
|
|
||||||
|
// A pointer to struct{} may be used to test for an element's presence.
|
||||||
|
{
|
||||||
|
Value: &PresenceTest{new(struct{})},
|
||||||
|
ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: &PresenceTest{},
|
||||||
|
ExpectXML: `<PresenceTest></PresenceTest>`,
|
||||||
|
},
|
||||||
|
|
||||||
|
// A pointer to struct{} may be used to test for an element's presence.
|
||||||
|
{
|
||||||
|
Value: &PresenceTest{new(struct{})},
|
||||||
|
ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: &PresenceTest{},
|
||||||
|
ExpectXML: `<PresenceTest></PresenceTest>`,
|
||||||
|
},
|
||||||
|
|
||||||
|
// A []byte field is only nil if the element was not found.
|
||||||
|
{
|
||||||
|
Value: &Data{},
|
||||||
|
ExpectXML: `<Data></Data>`,
|
||||||
|
UnmarshalOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
|
||||||
|
ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
|
||||||
|
UnmarshalOnly: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Check that []byte works, including named []byte types.
|
||||||
|
{
|
||||||
|
Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
|
||||||
|
ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
|
||||||
|
},
|
||||||
|
|
||||||
// Test innerxml
|
// Test innerxml
|
||||||
{
|
{
|
||||||
Value: &SecretAgent{
|
Value: &SecretAgent{
|
||||||
|
@ -134,7 +134,7 @@ import (
|
|||||||
//
|
//
|
||||||
// Unmarshal maps an XML element to a string or []byte by saving the
|
// Unmarshal maps an XML element to a string or []byte by saving the
|
||||||
// concatenation of that element's character data in the string or
|
// concatenation of that element's character data in the string or
|
||||||
// []byte.
|
// []byte. The saved []byte is never nil.
|
||||||
//
|
//
|
||||||
// Unmarshal maps an attribute value to a string or []byte by saving
|
// Unmarshal maps an attribute value to a string or []byte by saving
|
||||||
// the value in the string or slice.
|
// the value in the string or slice.
|
||||||
@ -309,14 +309,12 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) error {
|
|||||||
case fAttr:
|
case fAttr:
|
||||||
strv := sv.FieldByIndex(finfo.idx)
|
strv := sv.FieldByIndex(finfo.idx)
|
||||||
// Look for attribute.
|
// Look for attribute.
|
||||||
val := ""
|
|
||||||
for _, a := range start.Attr {
|
for _, a := range start.Attr {
|
||||||
if a.Name.Local == finfo.name {
|
if a.Name.Local == finfo.name {
|
||||||
val = a.Value
|
copyValue(strv, []byte(a.Value))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copyValue(strv, []byte(val))
|
|
||||||
|
|
||||||
case fCharData:
|
case fCharData:
|
||||||
if !saveData.IsValid() {
|
if !saveData.IsValid() {
|
||||||
@ -473,7 +471,11 @@ func copyValue(dst reflect.Value, src []byte) (err error) {
|
|||||||
case reflect.String:
|
case reflect.String:
|
||||||
t.SetString(string(src))
|
t.SetString(string(src))
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
t.Set(reflect.ValueOf(src))
|
if len(src) == 0 {
|
||||||
|
// non-nil to flag presence
|
||||||
|
src = []byte{}
|
||||||
|
}
|
||||||
|
t.SetBytes(src)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user