mirror of
https://github.com/golang/go
synced 2024-09-25 11:20:13 -06:00
xml: handle non-string attribute fields
R=kevlar, rsc CC=golang-dev https://golang.org/cl/4528114
This commit is contained in:
parent
c195cc8d82
commit
5f77bf8b20
@ -106,6 +106,11 @@ import (
|
|||||||
// The struct field may have type []byte or string.
|
// The struct field may have type []byte or string.
|
||||||
// If there is no such field, the character data is discarded.
|
// 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
|
||||||
|
// 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
|
// * If the XML element contains a sub-element whose name matches
|
||||||
// the prefix of a struct field tag formatted as "a>b>c", unmarshal
|
// the prefix of a struct field tag formatted as "a>b>c", unmarshal
|
||||||
// will descend into the XML structure looking for elements with the
|
// will descend into the XML structure looking for elements with the
|
||||||
@ -120,17 +125,22 @@ import (
|
|||||||
// maps the sub-element to that struct field.
|
// maps the sub-element to that struct field.
|
||||||
//
|
//
|
||||||
// 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 []byte.
|
// concatenation of that element's character data in the string or
|
||||||
|
// []byte.
|
||||||
//
|
//
|
||||||
// Unmarshal maps an XML element to a slice by extending the length
|
// Unmarshal maps an attribute value to a string or []byte by saving
|
||||||
// of the slice and mapping the element to the newly created value.
|
// the value in the string or slice.
|
||||||
//
|
//
|
||||||
// Unmarshal maps an XML element to a bool by setting it to the boolean
|
// Unmarshal maps an XML element to a slice by extending the length of
|
||||||
// value represented by the string.
|
// the slice and mapping the element to the newly created value.
|
||||||
//
|
//
|
||||||
// Unmarshal maps an XML element to an integer or floating-point
|
// Unmarshal maps an XML element or attribute value to a bool by
|
||||||
// field by setting the field to the result of interpreting the string
|
// setting it to the boolean value represented by the string.
|
||||||
// value in decimal. There is no check for overflow.
|
//
|
||||||
|
// Unmarshal maps an XML element or attribute value to an integer or
|
||||||
|
// floating-point field by setting the field to the result of
|
||||||
|
// interpreting the string value in decimal. There is no check for
|
||||||
|
// overflow.
|
||||||
//
|
//
|
||||||
// Unmarshal maps an XML element to an xml.Name by recording the
|
// Unmarshal maps an XML element to an xml.Name by recording the
|
||||||
// element name.
|
// element name.
|
||||||
@ -323,9 +333,6 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
|
|||||||
switch f.Tag {
|
switch f.Tag {
|
||||||
case "attr":
|
case "attr":
|
||||||
strv := sv.FieldByIndex(f.Index)
|
strv := sv.FieldByIndex(f.Index)
|
||||||
if strv.Kind() != reflect.String {
|
|
||||||
return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string")
|
|
||||||
}
|
|
||||||
// Look for attribute.
|
// Look for attribute.
|
||||||
val := ""
|
val := ""
|
||||||
k := strings.ToLower(f.Name)
|
k := strings.ToLower(f.Name)
|
||||||
@ -335,7 +342,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strv.SetString(val)
|
copyValue(strv, []byte(val))
|
||||||
|
|
||||||
case "comment":
|
case "comment":
|
||||||
if !saveComment.IsValid() {
|
if !saveComment.IsValid() {
|
||||||
@ -454,29 +461,50 @@ Loop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var err os.Error
|
if err := copyValue(saveData, data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := saveComment; t.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
t.SetString(string(comment))
|
||||||
|
case reflect.Slice:
|
||||||
|
t.Set(reflect.ValueOf(comment))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := saveXML; t.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
t.SetString(string(saveXMLData))
|
||||||
|
case reflect.Slice:
|
||||||
|
t.Set(reflect.ValueOf(saveXMLData))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyValue(dst reflect.Value, src []byte) (err os.Error) {
|
||||||
// Helper functions for integer and unsigned integer conversions
|
// Helper functions for integer and unsigned integer conversions
|
||||||
var itmp int64
|
var itmp int64
|
||||||
getInt64 := func() bool {
|
getInt64 := func() bool {
|
||||||
itmp, err = strconv.Atoi64(string(data))
|
itmp, err = strconv.Atoi64(string(src))
|
||||||
// TODO: should check sizes
|
// TODO: should check sizes
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
var utmp uint64
|
var utmp uint64
|
||||||
getUint64 := func() bool {
|
getUint64 := func() bool {
|
||||||
utmp, err = strconv.Atoui64(string(data))
|
utmp, err = strconv.Atoui64(string(src))
|
||||||
// TODO: check for overflow?
|
// TODO: check for overflow?
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
var ftmp float64
|
var ftmp float64
|
||||||
getFloat64 := func() bool {
|
getFloat64 := func() bool {
|
||||||
ftmp, err = strconv.Atof64(string(data))
|
ftmp, err = strconv.Atof64(string(src))
|
||||||
// TODO: check for overflow?
|
// TODO: check for overflow?
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save accumulated data and comments
|
// Save accumulated data and comments
|
||||||
switch t := saveData; t.Kind() {
|
switch t := dst; t.Kind() {
|
||||||
case reflect.Invalid:
|
case reflect.Invalid:
|
||||||
// Probably a comment, handled below
|
// Probably a comment, handled below
|
||||||
default:
|
default:
|
||||||
@ -497,31 +525,16 @@ Loop:
|
|||||||
}
|
}
|
||||||
t.SetFloat(ftmp)
|
t.SetFloat(ftmp)
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
value, err := strconv.Atob(strings.TrimSpace(string(data)))
|
value, err := strconv.Atob(strings.TrimSpace(string(src)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.SetBool(value)
|
t.SetBool(value)
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
t.SetString(string(data))
|
t.SetString(string(src))
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
t.Set(reflect.ValueOf(data))
|
t.Set(reflect.ValueOf(src))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t := saveComment; t.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
t.SetString(string(comment))
|
|
||||||
case reflect.Slice:
|
|
||||||
t.Set(reflect.ValueOf(comment))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t := saveXML; t.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
t.SetString(string(saveXMLData))
|
|
||||||
case reflect.Slice:
|
|
||||||
t.Set(reflect.ValueOf(saveXMLData))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,3 +325,47 @@ func TestUnmarshalBadPaths(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 "attr"
|
||||||
|
Float float64 "attr"
|
||||||
|
Uint8 uint8 "attr"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test2 struct {
|
||||||
|
Bool bool "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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user