31
31
// For example, given these definitions:
33
33
// type Email struct {
34
// Where string "attr"
34
// Where string `xml:"attr"`
38
38
// type Result struct {
39
// XMLName xml.Name "result"
39
// XMLName xml.Name `xml:"result"`
43
// Groups []string "group>value"
43
// Groups []string `xml:"group>value"`
46
46
// result := Result{Name: "name", Phone: "phone", Email: nil}
79
79
// Groups was assigned considering the element path provided in the
82
// Because Unmarshal uses the reflect package, it can only
83
// assign to upper case fields. Unmarshal uses a case-insensitive
82
// Because Unmarshal uses the reflect package, it can only assign
83
// to exported (upper case) fields. Unmarshal uses a case-insensitive
84
84
// comparison to match XML element names to struct field names.
86
// Unmarshal maps an XML element to a struct using the following rules:
86
// Unmarshal maps an XML element to a struct using the following rules.
87
// In the rules, the tag of a field refers to the value associated with the
88
// key 'xml' in the struct field's tag (see the example above).
88
90
// * If the struct has a field of type []byte or string with tag "innerxml",
89
91
// Unmarshal accumulates the raw XML nested inside the element
92
94
// * If the struct has a field named XMLName of type xml.Name,
93
95
// Unmarshal records the element name in that field.
95
// * If the XMLName field has an associated tag string of the form
96
// "tag" or "namespace-URL tag", the XML element must have
97
// the given tag (and, optionally, name space) or else Unmarshal
97
// * If the XMLName field has an associated tag of the form
98
// "name" or "namespace-URL name", the XML element must have
99
// the given name (and, optionally, name space) or else Unmarshal
98
100
// returns an error.
100
102
// * If the XML element has an attribute whose name matches a
106
108
// The struct field may have type []byte or string.
107
109
// If there is no such field, the character data is discarded.
111
// * If the XML element contains comments, they are accumulated in
112
// the first struct field that has tag "comments". The struct
113
// field may have type []byte or string. If there is no such
114
// field, the comments are discarded.
109
116
// * If the XML element contains a sub-element whose name matches
110
// the prefix of a struct field tag formatted as "a>b>c", unmarshal
117
// the prefix of a tag formatted as "a>b>c", unmarshal
111
118
// will descend into the XML structure looking for elements with the
112
119
// given names, and will map the innermost elements to that struct field.
113
// A struct field tag starting with ">" is equivalent to one starting
120
// A tag starting with ">" is equivalent to one starting
114
121
// with the field name followed by ">".
116
123
// * If the XML element contains a sub-element whose name
117
// matches a struct field whose tag is neither "attr" nor "chardata",
124
// matches a field whose tag is neither "attr" nor "chardata",
118
125
// Unmarshal maps the sub-element to that struct field.
119
126
// Otherwise, if the struct has a field named Any, unmarshal
120
127
// maps the sub-element to that struct field.
122
129
// Unmarshal maps an XML element to a string or []byte by saving the
123
// concatenation of that element's character data in the string or []byte.
125
// Unmarshal maps an XML element to a slice by extending the length
126
// of the slice and mapping the element to the newly created value.
128
// Unmarshal maps an XML element to a bool by setting it to the boolean
129
// value represented by the string.
131
// Unmarshal maps an XML element to an integer or floating-point
132
// field by setting the field to the result of interpreting the string
133
// value in decimal. There is no check for overflow.
130
// concatenation of that element's character data in the string or
133
// Unmarshal maps an attribute value to a string or []byte by saving
134
// the value in the string or slice.
136
// Unmarshal maps an XML element to a slice by extending the length of
137
// the slice and mapping the element to the newly created value.
139
// Unmarshal maps an XML element or attribute value to a bool by
140
// setting it to the boolean value represented by the string.
142
// Unmarshal maps an XML element or attribute value to an integer or
143
// floating-point field by setting the field to the result of
144
// interpreting the string value in decimal. There is no check for
135
147
// Unmarshal maps an XML element to an xml.Name by recording the
320
331
// Also, determine whether we need to save character data or comments.
321
332
for i, n := 0, typ.NumField(); i < n; i++ {
322
333
f := typ.Field(i)
334
switch f.Tag.Get("xml") {
325
336
strv := sv.FieldByIndex(f.Index)
326
if strv.Kind() != reflect.String {
327
return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string")
329
337
// Look for attribute.
331
339
k := strings.ToLower(f.Name)
362
if strings.Contains(f.Tag, ">") {
370
if tag := f.Tag.Get("xml"); strings.Contains(tag, ">") {
363
371
if fieldPaths == nil {
364
372
fieldPaths = make(map[string]pathInfo)
366
path := strings.ToLower(f.Tag)
367
if strings.HasPrefix(f.Tag, ">") {
374
path := strings.ToLower(tag)
375
if strings.HasPrefix(tag, ">") {
368
376
path = strings.ToLower(f.Name) + path
370
if strings.HasSuffix(f.Tag, ">") {
378
if strings.HasSuffix(tag, ">") {
371
379
path = path[:len(path)-1]
373
381
err := addFieldPath(sv, fieldPaths, path, f.Index)
465
if err := copyValue(saveData, data); err != nil {
469
switch t := saveComment; t.Kind() {
471
t.SetString(string(comment))
473
t.Set(reflect.ValueOf(comment))
476
switch t := saveXML; t.Kind() {
478
t.SetString(string(saveXMLData))
480
t.Set(reflect.ValueOf(saveXMLData))
486
func copyValue(dst reflect.Value, src []byte) (err os.Error) {
458
487
// Helper functions for integer and unsigned integer conversions
460
489
getInt64 := func() bool {
461
itmp, err = strconv.Atoi64(string(data))
490
itmp, err = strconv.Atoi64(string(src))
462
491
// TODO: should check sizes
463
492
return err == nil
466
495
getUint64 := func() bool {
467
utmp, err = strconv.Atoui64(string(data))
496
utmp, err = strconv.Atoui64(string(src))
468
497
// TODO: check for overflow?
469
498
return err == nil
472
501
getFloat64 := func() bool {
473
ftmp, err = strconv.Atof64(string(data))
502
ftmp, err = strconv.Atof64(string(src))
474
503
// TODO: check for overflow?
475
504
return err == nil
478
507
// Save accumulated data and comments
479
switch t := saveData; t.Kind() {
508
switch t := dst; t.Kind() {
480
509
case reflect.Invalid:
481
510
// Probably a comment, handled below
483
return os.ErrorString("cannot happen: unknown type " + t.Type().String())
512
return os.NewError("cannot happen: unknown type " + t.Type().String())
484
513
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
499
528
case reflect.Bool:
500
value, err := strconv.Atob(strings.TrimSpace(string(data)))
529
value, err := strconv.Atob(strings.TrimSpace(string(src)))
505
534
case reflect.String:
506
t.SetString(string(data))
508
t.Set(reflect.ValueOf(data))
511
switch t := saveComment; t.Kind() {
513
t.SetString(string(comment))
515
t.Set(reflect.ValueOf(comment))
518
switch t := saveXML; t.Kind() {
520
t.SetString(string(saveXMLData))
522
t.Set(reflect.ValueOf(saveXMLData))
535
t.SetString(string(src))
537
t.Set(reflect.ValueOf(src))
562
576
f1 := t.FieldByIndex(idx1)
563
577
f2 := t.FieldByIndex(idx2)
564
return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag}
578
return &TagPathError{t, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
567
581
// unmarshalPaths walks down an XML structure looking for