~john-koepi/ubuntu/trusty/golang/default

« back to all changes in this revision

Viewing changes to src/pkg/xml/marshal.go

  • Committer: Bazaar Package Importer
  • Author(s): Ondřej Surý
  • Date: 2011-08-03 17:04:59 UTC
  • mfrom: (14.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20110803170459-wzd99m3567y80ila
Tags: 1:59-1
* Imported Upstream version 59
* Refresh patches to a new release
* Fix FTBFS on ARM (Closes: #634270)
* Update version.bash to work with Debian packaging and not hg
  repository

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2011 The Go Authors. All rights reserved.
 
2
// Use of this source code is governed by a BSD-style
 
3
// license that can be found in the LICENSE file.
 
4
 
 
5
package xml
 
6
 
 
7
import (
 
8
        "bufio"
 
9
        "io"
 
10
        "os"
 
11
        "reflect"
 
12
        "strconv"
 
13
        "strings"
 
14
)
 
15
 
 
16
const (
 
17
        // A generic XML header suitable for use with the output of Marshal and MarshalIndent.
 
18
        // This is not automatically added to any output of this package, it is provided as a
 
19
        // convenience.
 
20
        Header = `<?xml version="1.0" encoding="UTF-8">\n`
 
21
)
 
22
 
 
23
// A Marshaler can produce well-formatted XML representing its internal state.
 
24
// It is used by both Marshal and MarshalIndent.
 
25
type Marshaler interface {
 
26
        MarshalXML() ([]byte, os.Error)
 
27
}
 
28
 
 
29
type printer struct {
 
30
        *bufio.Writer
 
31
}
 
32
 
 
33
// Marshal writes an XML-formatted representation of v to w.
 
34
//
 
35
// If v implements Marshaler, then Marshal calls its MarshalXML method.
 
36
// Otherwise, Marshal uses the following procedure to create the XML.
 
37
//
 
38
// Marshal handles an array or slice by marshalling each of the elements.
 
39
// Marshal handles a pointer by marshalling the value it points at or, if the
 
40
// pointer is nil, by writing nothing.  Marshal handles an interface value by
 
41
// marshalling the value it contains or, if the interface value is nil, by
 
42
// writing nothing.  Marshal handles all other data by writing a single XML
 
43
// element containing the data.
 
44
//
 
45
// The name of that XML element is taken from, in order of preference:
 
46
//     - the tag on an XMLName field, if the data is a struct
 
47
//     - the value of an XMLName field of type xml.Name
 
48
//     - the tag of the struct field used to obtain the data
 
49
//     - the name of the struct field used to obtain the data
 
50
//     - the name '???'.
 
51
//
 
52
// The XML element for a struct contains marshalled elements for each of the
 
53
// exported fields of the struct, with these exceptions:
 
54
//     - the XMLName field, described above, is omitted.
 
55
//     - a field with tag "attr" becomes an attribute in the XML element.
 
56
//     - a field with tag "chardata" is written as character data,
 
57
//        not as an XML element.
 
58
//     - a field with tag "innerxml" is written verbatim,
 
59
//        not subject to the usual marshalling procedure.
 
60
//
 
61
// Marshal will return an error if asked to marshal a channel, function, or map.
 
62
func Marshal(w io.Writer, v interface{}) (err os.Error) {
 
63
        p := &printer{bufio.NewWriter(w)}
 
64
        err = p.marshalValue(reflect.ValueOf(v), "???")
 
65
        p.Flush()
 
66
        return err
 
67
}
 
68
 
 
69
func (p *printer) marshalValue(val reflect.Value, name string) os.Error {
 
70
        if !val.IsValid() {
 
71
                return nil
 
72
        }
 
73
 
 
74
        kind := val.Kind()
 
75
        typ := val.Type()
 
76
 
 
77
        // Try Marshaler
 
78
        if typ.NumMethod() > 0 {
 
79
                if marshaler, ok := val.Interface().(Marshaler); ok {
 
80
                        bytes, err := marshaler.MarshalXML()
 
81
                        if err != nil {
 
82
                                return err
 
83
                        }
 
84
                        p.Write(bytes)
 
85
                        return nil
 
86
                }
 
87
        }
 
88
 
 
89
        // Drill into pointers/interfaces
 
90
        if kind == reflect.Ptr || kind == reflect.Interface {
 
91
                if val.IsNil() {
 
92
                        return nil
 
93
                }
 
94
                return p.marshalValue(val.Elem(), name)
 
95
        }
 
96
 
 
97
        // Slices and arrays iterate over the elements. They do not have an enclosing tag.
 
98
        if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
 
99
                for i, n := 0, val.Len(); i < n; i++ {
 
100
                        if err := p.marshalValue(val.Index(i), name); err != nil {
 
101
                                return err
 
102
                        }
 
103
                }
 
104
                return nil
 
105
        }
 
106
 
 
107
        // Find XML name
 
108
        xmlns := ""
 
109
        if kind == reflect.Struct {
 
110
                if f, ok := typ.FieldByName("XMLName"); ok {
 
111
                        if tag := f.Tag.Get("xml"); tag != "" {
 
112
                                if i := strings.Index(tag, " "); i >= 0 {
 
113
                                        xmlns, name = tag[:i], tag[i+1:]
 
114
                                } else {
 
115
                                        name = tag
 
116
                                }
 
117
                        } else if v, ok := val.FieldByIndex(f.Index).Interface().(Name); ok && v.Local != "" {
 
118
                                xmlns, name = v.Space, v.Local
 
119
                        }
 
120
                }
 
121
        }
 
122
 
 
123
        p.WriteByte('<')
 
124
        p.WriteString(name)
 
125
 
 
126
        // Attributes
 
127
        if kind == reflect.Struct {
 
128
                if len(xmlns) > 0 {
 
129
                        p.WriteString(` xmlns="`)
 
130
                        Escape(p, []byte(xmlns))
 
131
                        p.WriteByte('"')
 
132
                }
 
133
 
 
134
                for i, n := 0, typ.NumField(); i < n; i++ {
 
135
                        if f := typ.Field(i); f.PkgPath == "" && f.Tag.Get("xml") == "attr" {
 
136
                                if f.Type.Kind() == reflect.String {
 
137
                                        if str := val.Field(i).String(); str != "" {
 
138
                                                p.WriteByte(' ')
 
139
                                                p.WriteString(strings.ToLower(f.Name))
 
140
                                                p.WriteString(`="`)
 
141
                                                Escape(p, []byte(str))
 
142
                                                p.WriteByte('"')
 
143
                                        }
 
144
                                }
 
145
                        }
 
146
                }
 
147
        }
 
148
        p.WriteByte('>')
 
149
 
 
150
        switch k := val.Kind(); k {
 
151
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 
152
                p.WriteString(strconv.Itoa64(val.Int()))
 
153
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 
154
                p.WriteString(strconv.Uitoa64(val.Uint()))
 
155
        case reflect.Float32, reflect.Float64:
 
156
                p.WriteString(strconv.Ftoa64(val.Float(), 'g', -1))
 
157
        case reflect.String:
 
158
                Escape(p, []byte(val.String()))
 
159
        case reflect.Bool:
 
160
                p.WriteString(strconv.Btoa(val.Bool()))
 
161
        case reflect.Array:
 
162
                // will be [...]byte
 
163
                bytes := make([]byte, val.Len())
 
164
                for i := range bytes {
 
165
                        bytes[i] = val.Index(i).Interface().(byte)
 
166
                }
 
167
                Escape(p, bytes)
 
168
        case reflect.Slice:
 
169
                // will be []byte
 
170
                bytes := val.Interface().([]byte)
 
171
                Escape(p, bytes)
 
172
        case reflect.Struct:
 
173
                for i, n := 0, val.NumField(); i < n; i++ {
 
174
                        if f := typ.Field(i); f.Name != "XMLName" && f.PkgPath == "" {
 
175
                                name := f.Name
 
176
                                switch tag := f.Tag.Get("xml"); tag {
 
177
                                case "":
 
178
                                case "chardata":
 
179
                                        if tk := f.Type.Kind(); tk == reflect.String {
 
180
                                                p.Write([]byte(val.Field(i).String()))
 
181
                                        } else if tk == reflect.Slice {
 
182
                                                if elem, ok := val.Field(i).Interface().([]byte); ok {
 
183
                                                        Escape(p, elem)
 
184
                                                }
 
185
                                        }
 
186
                                        continue
 
187
                                case "innerxml":
 
188
                                        iface := val.Field(i).Interface()
 
189
                                        switch raw := iface.(type) {
 
190
                                        case []byte:
 
191
                                                p.Write(raw)
 
192
                                                continue
 
193
                                        case string:
 
194
                                                p.WriteString(raw)
 
195
                                                continue
 
196
                                        }
 
197
                                case "attr":
 
198
                                        continue
 
199
                                default:
 
200
                                        name = tag
 
201
                                }
 
202
 
 
203
                                if err := p.marshalValue(val.Field(i), name); err != nil {
 
204
                                        return err
 
205
                                }
 
206
                        }
 
207
                }
 
208
        default:
 
209
                return &UnsupportedTypeError{typ}
 
210
        }
 
211
 
 
212
        p.WriteByte('<')
 
213
        p.WriteByte('/')
 
214
        p.WriteString(name)
 
215
        p.WriteByte('>')
 
216
 
 
217
        return nil
 
218
}
 
219
 
 
220
// A MarshalXMLError is returned when Marshal or MarshalIndent encounter a type
 
221
// that cannot be converted into XML.
 
222
type UnsupportedTypeError struct {
 
223
        Type reflect.Type
 
224
}
 
225
 
 
226
func (e *UnsupportedTypeError) String() string {
 
227
        return "xml: unsupported type: " + e.Type.String()
 
228
}