Source file src/pkg/encoding/xml/marshal.go
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 "bytes"
10 "fmt"
11 "io"
12 "reflect"
13 "strconv"
14 "strings"
15 "time"
16 )
17
18 const (
19 // A generic XML header suitable for use with the output of Marshal.
20 // This is not automatically added to any output of this package,
21 // it is provided as a convenience.
22 Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
23 )
24
25 // Marshal returns the XML encoding of v.
26 //
27 // Marshal handles an array or slice by marshalling each of the elements.
28 // Marshal handles a pointer by marshalling the value it points at or, if the
29 // pointer is nil, by writing nothing. Marshal handles an interface value by
30 // marshalling the value it contains or, if the interface value is nil, by
31 // writing nothing. Marshal handles all other data by writing one or more XML
32 // elements containing the data.
33 //
34 // The name for the XML elements is taken from, in order of preference:
35 // - the tag on the XMLName field, if the data is a struct
36 // - the value of the XMLName field of type xml.Name
37 // - the tag of the struct field used to obtain the data
38 // - the name of the struct field used to obtain the data
39 // - the name of the marshalled type
40 //
41 // The XML element for a struct contains marshalled elements for each of the
42 // exported fields of the struct, with these exceptions:
43 // - the XMLName field, described above, is omitted.
44 // - a field with tag "-" is omitted.
45 // - a field with tag "name,attr" becomes an attribute with
46 // the given name in the XML element.
47 // - a field with tag ",attr" becomes an attribute with the
48 // field name in the in the XML element.
49 // - a field with tag ",chardata" is written as character data,
50 // not as an XML element.
51 // - a field with tag ",innerxml" is written verbatim, not subject
52 // to the usual marshalling procedure.
53 // - a field with tag ",comment" is written as an XML comment, not
54 // subject to the usual marshalling procedure. It must not contain
55 // the "--" string within it.
56 // - a field with a tag including the "omitempty" option is omitted
57 // if the field value is empty. The empty values are false, 0, any
58 // nil pointer or interface value, and any array, slice, map, or
59 // string of length zero.
60 // - a non-pointer anonymous struct field is handled as if the
61 // fields of its value were part of the outer struct.
62 //
63 // If a field uses a tag "a>b>c", then the element c will be nested inside
64 // parent elements a and b. Fields that appear next to each other that name
65 // the same parent will be enclosed in one XML element.
66 //
67 // See MarshalIndent for an example.
68 //
69 // Marshal will return an error if asked to marshal a channel, function, or map.
70 func Marshal(v interface{}) ([]byte, error) {
71 var b bytes.Buffer
72 if err := NewEncoder(&b).Encode(v); err != nil {
73 return nil, err
74 }
75 return b.Bytes(), nil
76 }
77
78 // MarshalIndent works like Marshal, but each XML element begins on a new
79 // indented line that starts with prefix and is followed by one or more
80 // copies of indent according to the nesting depth.
81 func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
82 var b bytes.Buffer
83 enc := NewEncoder(&b)
84 enc.prefix = prefix
85 enc.indent = indent
86 err := enc.marshalValue(reflect.ValueOf(v), nil)
87 enc.Flush()
88 if err != nil {
89 return nil, err
90 }
91 return b.Bytes(), nil
92 }
93
94 // An Encoder writes XML data to an output stream.
95 type Encoder struct {
96 printer
97 }
98
99 // NewEncoder returns a new encoder that writes to w.
100 func NewEncoder(w io.Writer) *Encoder {
101 return &Encoder{printer{Writer: bufio.NewWriter(w)}}
102 }
103
104 // Encode writes the XML encoding of v to the stream.
105 //
106 // See the documentation for Marshal for details about the conversion
107 // of Go values to XML.
108 func (enc *Encoder) Encode(v interface{}) error {
109 err := enc.marshalValue(reflect.ValueOf(v), nil)
110 enc.Flush()
111 return err
112 }
113
114 type printer struct {
115 *bufio.Writer
116 indent string
117 prefix string
118 depth int
119 indentedIn bool
120 }
121
122 // marshalValue writes one or more XML elements representing val.
123 // If val was obtained from a struct field, finfo must have its details.
124 func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
125 if !val.IsValid() {
126 return nil
127 }
128 if finfo != nil && finfo.flags&fOmitEmpty != 0 && isEmptyValue(val) {
129 return nil
130 }
131
132 kind := val.Kind()
133 typ := val.Type()
134
135 // Drill into pointers/interfaces
136 if kind == reflect.Ptr || kind == reflect.Interface {
137 if val.IsNil() {
138 return nil
139 }
140 return p.marshalValue(val.Elem(), finfo)
141 }
142
143 // Slices and arrays iterate over the elements. They do not have an enclosing tag.
144 if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
145 for i, n := 0, val.Len(); i < n; i++ {
146 if err := p.marshalValue(val.Index(i), finfo); err != nil {
147 return err
148 }
149 }
150 return nil
151 }
152
153 tinfo, err := getTypeInfo(typ)
154 if err != nil {
155 return err
156 }
157
158 // Precedence for the XML element name is:
159 // 1. XMLName field in underlying struct;
160 // 2. field name/tag in the struct field; and
161 // 3. type name
162 var xmlns, name string
163 if tinfo.xmlname != nil {
164 xmlname := tinfo.xmlname
165 if xmlname.name != "" {
166 xmlns, name = xmlname.xmlns, xmlname.name
167 } else if v, ok := val.FieldByIndex(xmlname.idx).Interface().(Name); ok && v.Local != "" {
168 xmlns, name = v.Space, v.Local
169 }
170 }
171 if name == "" && finfo != nil {
172 xmlns, name = finfo.xmlns, finfo.name
173 }
174 if name == "" {
175 name = typ.Name()
176 if name == "" {
177 return &UnsupportedTypeError{typ}
178 }
179 }
180
181 p.writeIndent(1)
182 p.WriteByte('<')
183 p.WriteString(name)
184
185 if xmlns != "" {
186 p.WriteString(` xmlns="`)
187 // TODO: EscapeString, to avoid the allocation.
188 Escape(p, []byte(xmlns))
189 p.WriteByte('"')
190 }
191
192 // Attributes
193 for i := range tinfo.fields {
194 finfo := &tinfo.fields[i]
195 if finfo.flags&fAttr == 0 {
196 continue
197 }
198 fv := val.FieldByIndex(finfo.idx)
199 if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
200 continue
201 }
202 p.WriteByte(' ')
203 p.WriteString(finfo.name)
204 p.WriteString(`="`)
205 if err := p.marshalSimple(fv.Type(), fv); err != nil {
206 return err
207 }
208 p.WriteByte('"')
209 }
210 p.WriteByte('>')
211
212 if val.Kind() == reflect.Struct {
213 err = p.marshalStruct(tinfo, val)
214 } else {
215 err = p.marshalSimple(typ, val)
216 }
217 if err != nil {
218 return err
219 }
220
221 p.writeIndent(-1)
222 p.WriteByte('<')
223 p.WriteByte('/')
224 p.WriteString(name)
225 p.WriteByte('>')
226
227 return nil
228 }
229
230 var timeType = reflect.TypeOf(time.Time{})
231
232 func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error {
233 // Normally we don't see structs, but this can happen for an attribute.
234 if val.Type() == timeType {
235 p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
236 return nil
237 }
238 switch val.Kind() {
239 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
240 p.WriteString(strconv.FormatInt(val.Int(), 10))
241 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
242 p.WriteString(strconv.FormatUint(val.Uint(), 10))
243 case reflect.Float32, reflect.Float64:
244 p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, 64))
245 case reflect.String:
246 // TODO: Add EscapeString.
247 Escape(p, []byte(val.String()))
248 case reflect.Bool:
249 p.WriteString(strconv.FormatBool(val.Bool()))
250 case reflect.Array:
251 // will be [...]byte
252 bytes := make([]byte, val.Len())
253 for i := range bytes {
254 bytes[i] = val.Index(i).Interface().(byte)
255 }
256 Escape(p, bytes)
257 case reflect.Slice:
258 // will be []byte
259 Escape(p, val.Bytes())
260 default:
261 return &UnsupportedTypeError{typ}
262 }
263 return nil
264 }
265
266 var ddBytes = []byte("--")
267
268 func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
269 if val.Type() == timeType {
270 p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
271 return nil
272 }
273 s := parentStack{printer: p}
274 for i := range tinfo.fields {
275 finfo := &tinfo.fields[i]
276 if finfo.flags&(fAttr|fAny) != 0 {
277 continue
278 }
279 vf := val.FieldByIndex(finfo.idx)
280 switch finfo.flags & fMode {
281 case fCharData:
282 switch vf.Kind() {
283 case reflect.String:
284 Escape(p, []byte(vf.String()))
285 case reflect.Slice:
286 if elem, ok := vf.Interface().([]byte); ok {
287 Escape(p, elem)
288 }
289 }
290 continue
291
292 case fComment:
293 k := vf.Kind()
294 if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) {
295 return fmt.Errorf("xml: bad type for comment field of %s", val.Type())
296 }
297 if vf.Len() == 0 {
298 continue
299 }
300 p.writeIndent(0)
301 p.WriteString("<!--")
302 dashDash := false
303 dashLast := false
304 switch k {
305 case reflect.String:
306 s := vf.String()
307 dashDash = strings.Index(s, "--") >= 0
308 dashLast = s[len(s)-1] == '-'
309 if !dashDash {
310 p.WriteString(s)
311 }
312 case reflect.Slice:
313 b := vf.Bytes()
314 dashDash = bytes.Index(b, ddBytes) >= 0
315 dashLast = b[len(b)-1] == '-'
316 if !dashDash {
317 p.Write(b)
318 }
319 default:
320 panic("can't happen")
321 }
322 if dashDash {
323 return fmt.Errorf(`xml: comments must not contain "--"`)
324 }
325 if dashLast {
326 // "--->" is invalid grammar. Make it "- -->"
327 p.WriteByte(' ')
328 }
329 p.WriteString("-->")
330 continue
331
332 case fInnerXml:
333 iface := vf.Interface()
334 switch raw := iface.(type) {
335 case []byte:
336 p.Write(raw)
337 continue
338 case string:
339 p.WriteString(raw)
340 continue
341 }
342
343 case fElement:
344 s.trim(finfo.parents)
345 if len(finfo.parents) > len(s.stack) {
346 if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() {
347 s.push(finfo.parents[len(s.stack):])
348 }
349 }
350 }
351 if err := p.marshalValue(vf, finfo); err != nil {
352 return err
353 }
354 }
355 s.trim(nil)
356 return nil
357 }
358
359 func (p *printer) writeIndent(depthDelta int) {
360 if len(p.prefix) == 0 && len(p.indent) == 0 {
361 return
362 }
363 if depthDelta < 0 {
364 p.depth--
365 if p.indentedIn {
366 p.indentedIn = false
367 return
368 }
369 p.indentedIn = false
370 }
371 p.WriteByte('\n')
372 if len(p.prefix) > 0 {
373 p.WriteString(p.prefix)
374 }
375 if len(p.indent) > 0 {
376 for i := 0; i < p.depth; i++ {
377 p.WriteString(p.indent)
378 }
379 }
380 if depthDelta > 0 {
381 p.depth++
382 p.indentedIn = true
383 }
384 }
385
386 type parentStack struct {
387 *printer
388 stack []string
389 }
390
391 // trim updates the XML context to match the longest common prefix of the stack
392 // and the given parents. A closing tag will be written for every parent
393 // popped. Passing a zero slice or nil will close all the elements.
394 func (s *parentStack) trim(parents []string) {
395 split := 0
396 for ; split < len(parents) && split < len(s.stack); split++ {
397 if parents[split] != s.stack[split] {
398 break
399 }
400 }
401 for i := len(s.stack) - 1; i >= split; i-- {
402 s.writeIndent(-1)
403 s.WriteString("</")
404 s.WriteString(s.stack[i])
405 s.WriteByte('>')
406 }
407 s.stack = parents[:split]
408 }
409
410 // push adds parent elements to the stack and writes open tags.
411 func (s *parentStack) push(parents []string) {
412 for i := 0; i < len(parents); i++ {
413 s.writeIndent(1)
414 s.WriteByte('<')
415 s.WriteString(parents[i])
416 s.WriteByte('>')
417 }
418 s.stack = append(s.stack, parents...)
419 }
420
421 // A MarshalXMLError is returned when Marshal encounters a type
422 // that cannot be converted into XML.
423 type UnsupportedTypeError struct {
424 Type reflect.Type
425 }
426
427 func (e *UnsupportedTypeError) Error() string {
428 return "xml: unsupported type: " + e.Type.String()
429 }
430
431 func isEmptyValue(v reflect.Value) bool {
432 switch v.Kind() {
433 case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
434 return v.Len() == 0
435 case reflect.Bool:
436 return !v.Bool()
437 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
438 return v.Int() == 0
439 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
440 return v.Uint() == 0
441 case reflect.Float32, reflect.Float64:
442 return v.Float() == 0
443 case reflect.Interface, reflect.Ptr:
444 return v.IsNil()
445 }
446 return false
447 }