aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/xml/read.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/xml/read.go')
-rw-r--r--libgo/go/xml/read.go128
1 files changed, 71 insertions, 57 deletions
diff --git a/libgo/go/xml/read.go b/libgo/go/xml/read.go
index e2b349c..786b69f 100644
--- a/libgo/go/xml/read.go
+++ b/libgo/go/xml/read.go
@@ -31,16 +31,16 @@ import (
// For example, given these definitions:
//
// type Email struct {
-// Where string "attr"
+// Where string `xml:"attr"`
// Addr string
// }
//
// type Result struct {
-// XMLName xml.Name "result"
+// XMLName xml.Name `xml:"result"`
// Name string
// Phone string
// Email []Email
-// Groups []string "group>value"
+// Groups []string `xml:"group>value"`
// }
//
// result := Result{Name: "name", Phone: "phone", Email: nil}
@@ -79,11 +79,13 @@ import (
// Groups was assigned considering the element path provided in the
// field tag.
//
-// Because Unmarshal uses the reflect package, it can only
-// assign to upper case fields. Unmarshal uses a case-insensitive
+// Because Unmarshal uses the reflect package, it can only assign
+// to exported (upper case) fields. Unmarshal uses a case-insensitive
// comparison to match XML element names to struct field names.
//
-// Unmarshal maps an XML element to a struct using the following rules:
+// Unmarshal maps an XML element to a struct using the following rules.
+// 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
@@ -92,9 +94,9 @@ import (
// * If the struct has a field named XMLName of type xml.Name,
// Unmarshal records the element name in that field.
//
-// * If the XMLName field has an associated tag string of the form
-// "tag" or "namespace-URL tag", the XML element must have
-// the given tag (and, optionally, name space) or else Unmarshal
+// * If the XMLName field has an associated tag of the form
+// "name" or "namespace-URL name", the XML element must have
+// the given name (and, optionally, name space) or else Unmarshal
// returns an error.
//
// * If the XML element has an attribute whose name matches a
@@ -106,31 +108,41 @@ import (
// The struct field may have type []byte or string.
// 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
-// the prefix of a struct field tag formatted as "a>b>c", unmarshal
+// the prefix of a tag formatted as "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 struct field tag starting with ">" is equivalent to one starting
+// 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 struct field whose tag is neither "attr" nor "chardata",
+// 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
// 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.
+// 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
-// of the slice and mapping the element to the newly created value.
+// Unmarshal maps an XML element to a slice by extending the length of
+// 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
-// value represented by the string.
+// Unmarshal maps an XML element or attribute value to a bool by
+// setting it to the boolean value represented by the string.
//
-// Unmarshal maps an XML element 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 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
// element name.
@@ -241,7 +253,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
switch v := val; v.Kind() {
default:
- return os.ErrorString("unknown type " + v.Type().String())
+ return os.NewError("unknown type " + v.Type().String())
case reflect.Slice:
typ := v.Type()
@@ -287,8 +299,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
// Assign name.
if f, ok := typ.FieldByName("XMLName"); ok {
// Validate element name.
- if f.Tag != "" {
- tag := f.Tag
+ if tag := f.Tag.Get("xml"); tag != "" {
ns := ""
i := strings.LastIndex(tag, " ")
if i >= 0 {
@@ -320,12 +331,9 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
// 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 {
+ switch f.Tag.Get("xml") {
case "attr":
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.
val := ""
k := strings.ToLower(f.Name)
@@ -335,7 +343,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
break
}
}
- strv.SetString(val)
+ copyValue(strv, []byte(val))
case "comment":
if !saveComment.IsValid() {
@@ -359,15 +367,15 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
}
default:
- if strings.Contains(f.Tag, ">") {
+ if tag := f.Tag.Get("xml"); strings.Contains(tag, ">") {
if fieldPaths == nil {
fieldPaths = make(map[string]pathInfo)
}
- path := strings.ToLower(f.Tag)
- if strings.HasPrefix(f.Tag, ">") {
+ path := strings.ToLower(tag)
+ if strings.HasPrefix(tag, ">") {
path = strings.ToLower(f.Name) + path
}
- if strings.HasSuffix(f.Tag, ">") {
+ if strings.HasSuffix(tag, ">") {
path = path[:len(path)-1]
}
err := addFieldPath(sv, fieldPaths, path, f.Index)
@@ -454,33 +462,54 @@ 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
var itmp int64
getInt64 := func() bool {
- itmp, err = strconv.Atoi64(string(data))
+ itmp, err = strconv.Atoi64(string(src))
// TODO: should check sizes
return err == nil
}
var utmp uint64
getUint64 := func() bool {
- utmp, err = strconv.Atoui64(string(data))
+ utmp, err = strconv.Atoui64(string(src))
// TODO: check for overflow?
return err == nil
}
var ftmp float64
getFloat64 := func() bool {
- ftmp, err = strconv.Atof64(string(data))
+ ftmp, err = strconv.Atof64(string(src))
// TODO: check for overflow?
return err == nil
}
// Save accumulated data and comments
- switch t := saveData; t.Kind() {
+ switch t := dst; t.Kind() {
case reflect.Invalid:
// Probably a comment, handled below
default:
- return os.ErrorString("cannot happen: unknown type " + t.Type().String())
+ return os.NewError("cannot happen: unknown type " + t.Type().String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if !getInt64() {
return err
@@ -497,31 +526,16 @@ Loop:
}
t.SetFloat(ftmp)
case reflect.Bool:
- value, err := strconv.Atob(strings.TrimSpace(string(data)))
+ value, err := strconv.Atob(strings.TrimSpace(string(src)))
if err != nil {
return err
}
t.SetBool(value)
case reflect.String:
- t.SetString(string(data))
+ t.SetString(string(src))
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
}
@@ -561,7 +575,7 @@ func tagError(sv reflect.Value, idx1 []int, idx2 []int) os.Error {
t := sv.Type()
f1 := t.FieldByIndex(idx1)
f2 := t.FieldByIndex(idx2)
- return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag}
+ return &TagPathError{t, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
}
// unmarshalPaths walks down an XML structure looking for