mirror of
https://github.com/golang/go
synced 2024-11-18 03:54:50 -07:00
encoding/xml: unmarshal allow empty, non-string values
When unmarshaling, if an element is empty, eg. '<tag></tag>', and destination type is int, uint, float or bool, do not attempt to parse value (""). Set to its zero value instead. Fixes #13417 Change-Id: I2d79f6d8f39192bb277b1a9129727d5abbb2dd1f Reviewed-on: https://go-review.googlesource.com/38386 Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
1295b745d1
commit
0a0186fb78
@ -120,6 +120,9 @@ import (
|
||||
// Unmarshal maps an XML element to a pointer by setting the pointer
|
||||
// to a freshly allocated value and then mapping the element to that value.
|
||||
//
|
||||
// A missing element or empty attribute value will be unmarshaled as a zero value.
|
||||
// If the field is a slice, a zero value will be appended to the field. Otherwise, the
|
||||
// field will be set to its zero value.
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
return NewDecoder(bytes.NewReader(data)).Decode(v)
|
||||
}
|
||||
@ -607,24 +610,40 @@ func copyValue(dst reflect.Value, src []byte) (err error) {
|
||||
default:
|
||||
return errors.New("cannot unmarshal into " + dst0.Type().String())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if len(src) == 0 {
|
||||
dst.SetInt(0)
|
||||
return nil
|
||||
}
|
||||
itmp, err := strconv.ParseInt(string(src), 10, dst.Type().Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.SetInt(itmp)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
if len(src) == 0 {
|
||||
dst.SetUint(0)
|
||||
return nil
|
||||
}
|
||||
utmp, err := strconv.ParseUint(string(src), 10, dst.Type().Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.SetUint(utmp)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if len(src) == 0 {
|
||||
dst.SetFloat(0)
|
||||
return nil
|
||||
}
|
||||
ftmp, err := strconv.ParseFloat(string(src), dst.Type().Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.SetFloat(ftmp)
|
||||
case reflect.Bool:
|
||||
if len(src) == 0 {
|
||||
dst.SetBool(false)
|
||||
return nil
|
||||
}
|
||||
value, err := strconv.ParseBool(strings.TrimSpace(string(src)))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -752,3 +752,159 @@ func TestInvalidInnerXMLType(t *testing.T) {
|
||||
t.Errorf("NotInnerXML = %v, want nil", v.NotInnerXML)
|
||||
}
|
||||
}
|
||||
|
||||
type Child struct {
|
||||
G struct {
|
||||
I int
|
||||
}
|
||||
}
|
||||
|
||||
type ChildToEmbed struct {
|
||||
X bool
|
||||
}
|
||||
|
||||
type Parent struct {
|
||||
I int
|
||||
IPtr *int
|
||||
Is []int
|
||||
IPtrs []*int
|
||||
F float32
|
||||
FPtr *float32
|
||||
Fs []float32
|
||||
FPtrs []*float32
|
||||
B bool
|
||||
BPtr *bool
|
||||
Bs []bool
|
||||
BPtrs []*bool
|
||||
Bytes []byte
|
||||
BytesPtr *[]byte
|
||||
S string
|
||||
SPtr *string
|
||||
Ss []string
|
||||
SPtrs []*string
|
||||
MyI MyInt
|
||||
Child Child
|
||||
Children []Child
|
||||
ChildPtr *Child
|
||||
ChildToEmbed
|
||||
}
|
||||
|
||||
const (
|
||||
emptyXML = `
|
||||
<Parent>
|
||||
<I></I>
|
||||
<IPtr></IPtr>
|
||||
<Is></Is>
|
||||
<IPtrs></IPtrs>
|
||||
<F></F>
|
||||
<FPtr></FPtr>
|
||||
<Fs></Fs>
|
||||
<FPtrs></FPtrs>
|
||||
<B></B>
|
||||
<BPtr></BPtr>
|
||||
<Bs></Bs>
|
||||
<BPtrs></BPtrs>
|
||||
<Bytes></Bytes>
|
||||
<BytesPtr></BytesPtr>
|
||||
<S></S>
|
||||
<SPtr></SPtr>
|
||||
<Ss></Ss>
|
||||
<SPtrs></SPtrs>
|
||||
<MyI></MyI>
|
||||
<Child></Child>
|
||||
<Children></Children>
|
||||
<ChildPtr></ChildPtr>
|
||||
<X></X>
|
||||
</Parent>
|
||||
`
|
||||
)
|
||||
|
||||
// github.com/golang/go/issues/13417
|
||||
func TestUnmarshalEmptyValues(t *testing.T) {
|
||||
// Test first with a zero-valued dst.
|
||||
v := new(Parent)
|
||||
if err := Unmarshal([]byte(emptyXML), v); err != nil {
|
||||
t.Fatalf("zero: Unmarshal failed: got %v", err)
|
||||
}
|
||||
|
||||
zBytes, zInt, zStr, zFloat, zBool := []byte{}, 0, "", float32(0), false
|
||||
want := &Parent{
|
||||
IPtr: &zInt,
|
||||
Is: []int{zInt},
|
||||
IPtrs: []*int{&zInt},
|
||||
FPtr: &zFloat,
|
||||
Fs: []float32{zFloat},
|
||||
FPtrs: []*float32{&zFloat},
|
||||
BPtr: &zBool,
|
||||
Bs: []bool{zBool},
|
||||
BPtrs: []*bool{&zBool},
|
||||
Bytes: []byte{},
|
||||
BytesPtr: &zBytes,
|
||||
SPtr: &zStr,
|
||||
Ss: []string{zStr},
|
||||
SPtrs: []*string{&zStr},
|
||||
Children: []Child{{}},
|
||||
ChildPtr: new(Child),
|
||||
ChildToEmbed: ChildToEmbed{},
|
||||
}
|
||||
if !reflect.DeepEqual(v, want) {
|
||||
t.Fatalf("zero: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want)
|
||||
}
|
||||
|
||||
// Test with a pre-populated dst.
|
||||
// Multiple addressable copies, as pointer-to fields will replace value during unmarshal.
|
||||
vBytes0, vInt0, vStr0, vFloat0, vBool0 := []byte("x"), 1, "x", float32(1), true
|
||||
vBytes1, vInt1, vStr1, vFloat1, vBool1 := []byte("x"), 1, "x", float32(1), true
|
||||
vInt2, vStr2, vFloat2, vBool2 := 1, "x", float32(1), true
|
||||
v = &Parent{
|
||||
I: vInt0,
|
||||
IPtr: &vInt1,
|
||||
Is: []int{vInt0},
|
||||
IPtrs: []*int{&vInt2},
|
||||
F: vFloat0,
|
||||
FPtr: &vFloat1,
|
||||
Fs: []float32{vFloat0},
|
||||
FPtrs: []*float32{&vFloat2},
|
||||
B: vBool0,
|
||||
BPtr: &vBool1,
|
||||
Bs: []bool{vBool0},
|
||||
BPtrs: []*bool{&vBool2},
|
||||
Bytes: vBytes0,
|
||||
BytesPtr: &vBytes1,
|
||||
S: vStr0,
|
||||
SPtr: &vStr1,
|
||||
Ss: []string{vStr0},
|
||||
SPtrs: []*string{&vStr2},
|
||||
MyI: MyInt(vInt0),
|
||||
Child: Child{G: struct{ I int }{I: vInt0}},
|
||||
Children: []Child{{G: struct{ I int }{I: vInt0}}},
|
||||
ChildPtr: &Child{G: struct{ I int }{I: vInt0}},
|
||||
ChildToEmbed: ChildToEmbed{X: vBool0},
|
||||
}
|
||||
if err := Unmarshal([]byte(emptyXML), v); err != nil {
|
||||
t.Fatalf("populated: Unmarshal failed: got %v", err)
|
||||
}
|
||||
|
||||
want = &Parent{
|
||||
IPtr: &zInt,
|
||||
Is: []int{vInt0, zInt},
|
||||
IPtrs: []*int{&vInt0, &zInt},
|
||||
FPtr: &zFloat,
|
||||
Fs: []float32{vFloat0, zFloat},
|
||||
FPtrs: []*float32{&vFloat0, &zFloat},
|
||||
BPtr: &zBool,
|
||||
Bs: []bool{vBool0, zBool},
|
||||
BPtrs: []*bool{&vBool0, &zBool},
|
||||
Bytes: []byte{},
|
||||
BytesPtr: &zBytes,
|
||||
SPtr: &zStr,
|
||||
Ss: []string{vStr0, zStr},
|
||||
SPtrs: []*string{&vStr0, &zStr},
|
||||
Child: Child{G: struct{ I int }{I: vInt0}}, // I should == zInt0? (zero value)
|
||||
Children: []Child{{G: struct{ I int }{I: vInt0}}, {}},
|
||||
ChildPtr: &Child{G: struct{ I int }{I: vInt0}}, // I should == zInt0? (zero value)
|
||||
}
|
||||
if !reflect.DeepEqual(v, want) {
|
||||
t.Fatalf("populated: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user