Source file src/pkg/encoding/xml/marshal.go
1
2
3
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
20
21
22 Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
23 )
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
79
80
81 func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
82 var b bytes.Buffer
83 enc := NewEncoder(&b)
84 enc.Indent(prefix, indent)
85 if err := enc.Encode(v); err != nil {
86 return nil, err
87 }
88 return b.Bytes(), nil
89 }
90
91
92 type Encoder struct {
93 printer
94 }
95
96
97 func NewEncoder(w io.Writer) *Encoder {
98 return &Encoder{printer{Writer: bufio.NewWriter(w)}}
99 }
100
101
102
103
104 func (enc *Encoder) Indent(prefix, indent string) {
105 enc.prefix = prefix
106 enc.indent = indent
107 }
108
109
110
111
112
113 func (enc *Encoder) Encode(v interface{}) error {
114 err := enc.marshalValue(reflect.ValueOf(v), nil)
115 if err != nil {
116 return err
117 }
118 return enc.Flush()
119 }
120
121 type printer struct {
122 *bufio.Writer
123 seq int
124 indent string
125 prefix string
126 depth int
127 indentedIn bool
128 putNewline bool
129 attrNS map[string]string
130 attrPrefix map[string]string
131 }
132
133
134
135 func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) {
136 if prefix = p.attrPrefix[url]; prefix != "" {
137 return prefix, false
138 }
139
140
141
142
143
144 if url == xmlURL {
145 return "xml", false
146 }
147
148
149 if p.attrPrefix == nil {
150 p.attrPrefix = make(map[string]string)
151 p.attrNS = make(map[string]string)
152 }
153
154
155
156 prefix = strings.TrimRight(url, "/")
157 if i := strings.LastIndex(prefix, "/"); i >= 0 {
158 prefix = prefix[i+1:]
159 }
160 if prefix == "" || !isName([]byte(prefix)) || strings.Contains(prefix, ":") {
161 prefix = "_"
162 }
163 if strings.HasPrefix(prefix, "xml") {
164
165 prefix = "_" + prefix
166 }
167 if p.attrNS[prefix] != "" {
168
169 for p.seq++; ; p.seq++ {
170 if id := prefix + "_" + strconv.Itoa(p.seq); p.attrNS[id] == "" {
171 prefix = id
172 break
173 }
174 }
175 }
176
177 p.attrPrefix[url] = prefix
178 p.attrNS[prefix] = url
179
180 p.WriteString(`xmlns:`)
181 p.WriteString(prefix)
182 p.WriteString(`="`)
183 EscapeText(p, []byte(url))
184 p.WriteString(`" `)
185
186 return prefix, true
187 }
188
189
190 func (p *printer) deleteAttrPrefix(prefix string) {
191 delete(p.attrPrefix, p.attrNS[prefix])
192 delete(p.attrNS, prefix)
193 }
194
195
196
197 func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
198 if !val.IsValid() {
199 return nil
200 }
201 if finfo != nil && finfo.flags&fOmitEmpty != 0 && isEmptyValue(val) {
202 return nil
203 }
204
205 kind := val.Kind()
206 typ := val.Type()
207
208
209 if kind == reflect.Ptr || kind == reflect.Interface {
210 if val.IsNil() {
211 return nil
212 }
213 return p.marshalValue(val.Elem(), finfo)
214 }
215
216
217 if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
218 for i, n := 0, val.Len(); i < n; i++ {
219 if err := p.marshalValue(val.Index(i), finfo); err != nil {
220 return err
221 }
222 }
223 return nil
224 }
225
226 tinfo, err := getTypeInfo(typ)
227 if err != nil {
228 return err
229 }
230
231
232
233
234
235 var xmlns, name string
236 if tinfo.xmlname != nil {
237 xmlname := tinfo.xmlname
238 if xmlname.name != "" {
239 xmlns, name = xmlname.xmlns, xmlname.name
240 } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
241 xmlns, name = v.Space, v.Local
242 }
243 }
244 if name == "" && finfo != nil {
245 xmlns, name = finfo.xmlns, finfo.name
246 }
247 if name == "" {
248 name = typ.Name()
249 if name == "" {
250 return &UnsupportedTypeError{typ}
251 }
252 }
253
254 p.writeIndent(1)
255 p.WriteByte('<')
256 p.WriteString(name)
257
258 if xmlns != "" {
259 p.WriteString(` xmlns="`)
260
261 if err := EscapeText(p, []byte(xmlns)); err != nil {
262 return err
263 }
264 p.WriteByte('"')
265 }
266
267
268 for i := range tinfo.fields {
269 finfo := &tinfo.fields[i]
270 if finfo.flags&fAttr == 0 {
271 continue
272 }
273 fv := finfo.value(val)
274 if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
275 continue
276 }
277 p.WriteByte(' ')
278 if finfo.xmlns != "" {
279 prefix, created := p.createAttrPrefix(finfo.xmlns)
280 if created {
281 defer p.deleteAttrPrefix(prefix)
282 }
283 p.WriteString(prefix)
284 p.WriteByte(':')
285 }
286 p.WriteString(finfo.name)
287 p.WriteString(`="`)
288 if err := p.marshalSimple(fv.Type(), fv); err != nil {
289 return err
290 }
291 p.WriteByte('"')
292 }
293 p.WriteByte('>')
294
295 if val.Kind() == reflect.Struct {
296 err = p.marshalStruct(tinfo, val)
297 } else {
298 err = p.marshalSimple(typ, val)
299 }
300 if err != nil {
301 return err
302 }
303
304 p.writeIndent(-1)
305 p.WriteByte('<')
306 p.WriteByte('/')
307 p.WriteString(name)
308 p.WriteByte('>')
309
310 return p.cachedWriteError()
311 }
312
313 var timeType = reflect.TypeOf(time.Time{})
314
315 func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error {
316
317 if val.Type() == timeType {
318 p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
319 return nil
320 }
321 switch val.Kind() {
322 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
323 p.WriteString(strconv.FormatInt(val.Int(), 10))
324 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
325 p.WriteString(strconv.FormatUint(val.Uint(), 10))
326 case reflect.Float32, reflect.Float64:
327 p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()))
328 case reflect.String:
329
330 EscapeText(p, []byte(val.String()))
331 case reflect.Bool:
332 p.WriteString(strconv.FormatBool(val.Bool()))
333 case reflect.Array:
334
335 var bytes []byte
336 if val.CanAddr() {
337 bytes = val.Slice(0, val.Len()).Bytes()
338 } else {
339 bytes = make([]byte, val.Len())
340 reflect.Copy(reflect.ValueOf(bytes), val)
341 }
342 EscapeText(p, bytes)
343 case reflect.Slice:
344
345 EscapeText(p, val.Bytes())
346 default:
347 return &UnsupportedTypeError{typ}
348 }
349 return p.cachedWriteError()
350 }
351
352 var ddBytes = []byte("--")
353
354 func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
355 if val.Type() == timeType {
356 _, err := p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
357 return err
358 }
359 s := parentStack{printer: p}
360 for i := range tinfo.fields {
361 finfo := &tinfo.fields[i]
362 if finfo.flags&fAttr != 0 {
363 continue
364 }
365 vf := finfo.value(val)
366 switch finfo.flags & fMode {
367 case fCharData:
368 var scratch [64]byte
369 switch vf.Kind() {
370 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
371 Escape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10))
372 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
373 Escape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10))
374 case reflect.Float32, reflect.Float64:
375 Escape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits()))
376 case reflect.Bool:
377 Escape(p, strconv.AppendBool(scratch[:0], vf.Bool()))
378 case reflect.String:
379 if err := EscapeText(p, []byte(vf.String())); err != nil {
380 return err
381 }
382 case reflect.Slice:
383 if elem, ok := vf.Interface().([]byte); ok {
384 if err := EscapeText(p, elem); err != nil {
385 return err
386 }
387 }
388 case reflect.Struct:
389 if vf.Type() == timeType {
390 Escape(p, []byte(vf.Interface().(time.Time).Format(time.RFC3339Nano)))
391 }
392 }
393 continue
394
395 case fComment:
396 k := vf.Kind()
397 if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) {
398 return fmt.Errorf("xml: bad type for comment field of %s", val.Type())
399 }
400 if vf.Len() == 0 {
401 continue
402 }
403 p.writeIndent(0)
404 p.WriteString("<!--")
405 dashDash := false
406 dashLast := false
407 switch k {
408 case reflect.String:
409 s := vf.String()
410 dashDash = strings.Index(s, "--") >= 0
411 dashLast = s[len(s)-1] == '-'
412 if !dashDash {
413 p.WriteString(s)
414 }
415 case reflect.Slice:
416 b := vf.Bytes()
417 dashDash = bytes.Index(b, ddBytes) >= 0
418 dashLast = b[len(b)-1] == '-'
419 if !dashDash {
420 p.Write(b)
421 }
422 default:
423 panic("can't happen")
424 }
425 if dashDash {
426 return fmt.Errorf(`xml: comments must not contain "--"`)
427 }
428 if dashLast {
429
430 p.WriteByte(' ')
431 }
432 p.WriteString("-->")
433 continue
434
435 case fInnerXml:
436 iface := vf.Interface()
437 switch raw := iface.(type) {
438 case []byte:
439 p.Write(raw)
440 continue
441 case string:
442 p.WriteString(raw)
443 continue
444 }
445
446 case fElement, fElement | fAny:
447 s.trim(finfo.parents)
448 if len(finfo.parents) > len(s.stack) {
449 if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() {
450 s.push(finfo.parents[len(s.stack):])
451 }
452 }
453 }
454 if err := p.marshalValue(vf, finfo); err != nil {
455 return err
456 }
457 }
458 s.trim(nil)
459 return p.cachedWriteError()
460 }
461
462
463 func (p *printer) cachedWriteError() error {
464 _, err := p.Write(nil)
465 return err
466 }
467
468 func (p *printer) writeIndent(depthDelta int) {
469 if len(p.prefix) == 0 && len(p.indent) == 0 {
470 return
471 }
472 if depthDelta < 0 {
473 p.depth--
474 if p.indentedIn {
475 p.indentedIn = false
476 return
477 }
478 p.indentedIn = false
479 }
480 if p.putNewline {
481 p.WriteByte('\n')
482 } else {
483 p.putNewline = true
484 }
485 if len(p.prefix) > 0 {
486 p.WriteString(p.prefix)
487 }
488 if len(p.indent) > 0 {
489 for i := 0; i < p.depth; i++ {
490 p.WriteString(p.indent)
491 }
492 }
493 if depthDelta > 0 {
494 p.depth++
495 p.indentedIn = true
496 }
497 }
498
499 type parentStack struct {
500 *printer
501 stack []string
502 }
503
504
505
506
507 func (s *parentStack) trim(parents []string) {
508 split := 0
509 for ; split < len(parents) && split < len(s.stack); split++ {
510 if parents[split] != s.stack[split] {
511 break
512 }
513 }
514 for i := len(s.stack) - 1; i >= split; i-- {
515 s.writeIndent(-1)
516 s.WriteString("</")
517 s.WriteString(s.stack[i])
518 s.WriteByte('>')
519 }
520 s.stack = parents[:split]
521 }
522
523
524 func (s *parentStack) push(parents []string) {
525 for i := 0; i < len(parents); i++ {
526 s.writeIndent(1)
527 s.WriteByte('<')
528 s.WriteString(parents[i])
529 s.WriteByte('>')
530 }
531 s.stack = append(s.stack, parents...)
532 }
533
534
535
536 type UnsupportedTypeError struct {
537 Type reflect.Type
538 }
539
540 func (e *UnsupportedTypeError) Error() string {
541 return "xml: unsupported type: " + e.Type.String()
542 }
543
544 func isEmptyValue(v reflect.Value) bool {
545 switch v.Kind() {
546 case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
547 return v.Len() == 0
548 case reflect.Bool:
549 return !v.Bool()
550 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
551 return v.Int() == 0
552 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
553 return v.Uint() == 0
554 case reflect.Float32, reflect.Float64:
555 return v.Float() == 0
556 case reflect.Interface, reflect.Ptr:
557 return v.IsNil()
558 }
559 return false
560 }
View as plain text