diff options
Diffstat (limited to 'libgo/go/xml/read.go')
-rw-r--r-- | libgo/go/xml/read.go | 128 |
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 |