Commit 5f77bf8b authored by Maxim Ushakov's avatar Maxim Ushakov Committed by Russ Cox

xml: handle non-string attribute fields

R=kevlar, rsc
CC=golang-dev
https://golang.org/cl/4528114
parent c195cc8d
...@@ -106,6 +106,11 @@ import ( ...@@ -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 ( ...@@ -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 attribute value to a string or []byte by saving
// the value in the string or slice.
// //
// Unmarshal maps an XML element to a slice by extending the length // Unmarshal maps an XML element to a slice by extending the length of
// of the slice and mapping the element to the newly created value. // the slice and mapping the element to the newly created value.
// //
// Unmarshal maps an XML element to a bool by setting it to the boolean // Unmarshal maps an XML element or attribute value to a bool by
// value represented by the string. // setting it to the boolean value represented by the string.
// //
// Unmarshal maps an XML element to an integer or floating-point // Unmarshal maps an XML element or attribute value to an integer or
// field by setting the field to the result of interpreting the string // floating-point field by setting the field to the result of
// value in decimal. There is no check for overflow. // 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 { ...@@ -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 { ...@@ -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: ...@@ -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: ...@@ -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) { ...@@ -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,
},
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment