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
5package xml
6
7import (
8	"bufio"
9	"bytes"
10	"encoding"
11	"fmt"
12	"io"
13	"reflect"
14	"strconv"
15	"strings"
16)
17
18const (
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 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//     - an anonymous struct field is handled as if the fields of its
61//       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.
70func 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// Marshaler is the interface implemented by objects that can marshal
79// themselves into valid XML elements.
80//
81// MarshalXML encodes the receiver as zero or more XML elements.
82// By convention, arrays or slices are typically encoded as a sequence
83// of elements, one per entry.
84// Using start as the element tag is not required, but doing so
85// will enable Unmarshal to match the XML elements to the correct
86// struct field.
87// One common implementation strategy is to construct a separate
88// value with a layout corresponding to the desired XML and then
89// to encode it using e.EncodeElement.
90// Another common strategy is to use repeated calls to e.EncodeToken
91// to generate the XML output one token at a time.
92// The sequence of encoded tokens must make up zero or more valid
93// XML elements.
94type Marshaler interface {
95	MarshalXML(e *Encoder, start StartElement) error
96}
97
98// MarshalerAttr is the interface implemented by objects that can marshal
99// themselves into valid XML attributes.
100//
101// MarshalXMLAttr returns an XML attribute with the encoded value of the receiver.
102// Using name as the attribute name is not required, but doing so
103// will enable Unmarshal to match the attribute to the correct
104// struct field.
105// If MarshalXMLAttr returns the zero attribute Attr{}, no attribute
106// will be generated in the output.
107// MarshalXMLAttr is used only for struct fields with the
108// "attr" option in the field tag.
109type MarshalerAttr interface {
110	MarshalXMLAttr(name Name) (Attr, error)
111}
112
113// MarshalIndent works like Marshal, but each XML element begins on a new
114// indented line that starts with prefix and is followed by one or more
115// copies of indent according to the nesting depth.
116func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
117	var b bytes.Buffer
118	enc := NewEncoder(&b)
119	enc.Indent(prefix, indent)
120	if err := enc.Encode(v); err != nil {
121		return nil, err
122	}
123	return b.Bytes(), nil
124}
125
126// An Encoder writes XML data to an output stream.
127type Encoder struct {
128	p printer
129}
130
131// NewEncoder returns a new encoder that writes to w.
132func NewEncoder(w io.Writer) *Encoder {
133	e := &Encoder{printer{Writer: bufio.NewWriter(w)}}
134	e.p.encoder = e
135	return e
136}
137
138// Indent sets the encoder to generate XML in which each element
139// begins on a new indented line that starts with prefix and is followed by
140// one or more copies of indent according to the nesting depth.
141func (enc *Encoder) Indent(prefix, indent string) {
142	enc.p.prefix = prefix
143	enc.p.indent = indent
144}
145
146// Encode writes the XML encoding of v to the stream.
147//
148// See the documentation for Marshal for details about the conversion
149// of Go values to XML.
150//
151// Encode calls Flush before returning.
152func (enc *Encoder) Encode(v interface{}) error {
153	err := enc.p.marshalValue(reflect.ValueOf(v), nil, nil)
154	if err != nil {
155		return err
156	}
157	return enc.p.Flush()
158}
159
160// EncodeElement writes the XML encoding of v to the stream,
161// using start as the outermost tag in the encoding.
162//
163// See the documentation for Marshal for details about the conversion
164// of Go values to XML.
165//
166// EncodeElement calls Flush before returning.
167func (enc *Encoder) EncodeElement(v interface{}, start StartElement) error {
168	err := enc.p.marshalValue(reflect.ValueOf(v), nil, &start)
169	if err != nil {
170		return err
171	}
172	return enc.p.Flush()
173}
174
175var (
176	endComment   = []byte("-->")
177	endProcInst  = []byte("?>")
178	endDirective = []byte(">")
179)
180
181// EncodeToken writes the given XML token to the stream.
182// It returns an error if StartElement and EndElement tokens are not properly matched.
183//
184// EncodeToken does not call Flush, because usually it is part of a larger operation
185// such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked
186// during those), and those will call Flush when finished.
187// Callers that create an Encoder and then invoke EncodeToken directly, without
188// using Encode or EncodeElement, need to call Flush when finished to ensure
189// that the XML is written to the underlying writer.
190//
191// EncodeToken allows writing a ProcInst with Target set to "xml" only as the first token
192// in the stream.
193func (enc *Encoder) EncodeToken(t Token) error {
194	p := &enc.p
195	switch t := t.(type) {
196	case StartElement:
197		if err := p.writeStart(&t); err != nil {
198			return err
199		}
200	case EndElement:
201		if err := p.writeEnd(t.Name); err != nil {
202			return err
203		}
204	case CharData:
205		EscapeText(p, t)
206	case Comment:
207		if bytes.Contains(t, endComment) {
208			return fmt.Errorf("xml: EncodeToken of Comment containing --> marker")
209		}
210		p.WriteString("<!--")
211		p.Write(t)
212		p.WriteString("-->")
213		return p.cachedWriteError()
214	case ProcInst:
215		// First token to be encoded which is also a ProcInst with target of xml
216		// is the xml declaration.  The only ProcInst where target of xml is allowed.
217		if t.Target == "xml" && p.Buffered() != 0 {
218			return fmt.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
219		}
220		if !isNameString(t.Target) {
221			return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target")
222		}
223		if bytes.Contains(t.Inst, endProcInst) {
224			return fmt.Errorf("xml: EncodeToken of ProcInst containing ?> marker")
225		}
226		p.WriteString("<?")
227		p.WriteString(t.Target)
228		if len(t.Inst) > 0 {
229			p.WriteByte(' ')
230			p.Write(t.Inst)
231		}
232		p.WriteString("?>")
233	case Directive:
234		if bytes.Contains(t, endDirective) {
235			return fmt.Errorf("xml: EncodeToken of Directive containing > marker")
236		}
237		p.WriteString("<!")
238		p.Write(t)
239		p.WriteString(">")
240	}
241	return p.cachedWriteError()
242}
243
244// Flush flushes any buffered XML to the underlying writer.
245// See the EncodeToken documentation for details about when it is necessary.
246func (enc *Encoder) Flush() error {
247	return enc.p.Flush()
248}
249
250type printer struct {
251	*bufio.Writer
252	encoder    *Encoder
253	seq        int
254	indent     string
255	prefix     string
256	depth      int
257	indentedIn bool
258	putNewline bool
259	attrNS     map[string]string // map prefix -> name space
260	attrPrefix map[string]string // map name space -> prefix
261	prefixes   []string
262	tags       []Name
263}
264
265// createAttrPrefix finds the name space prefix attribute to use for the given name space,
266// defining a new prefix if necessary. It returns the prefix.
267func (p *printer) createAttrPrefix(url string) string {
268	if prefix := p.attrPrefix[url]; prefix != "" {
269		return prefix
270	}
271
272	// The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml"
273	// and must be referred to that way.
274	// (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns",
275	// but users should not be trying to use that one directly - that's our job.)
276	if url == xmlURL {
277		return "xml"
278	}
279
280	// Need to define a new name space.
281	if p.attrPrefix == nil {
282		p.attrPrefix = make(map[string]string)
283		p.attrNS = make(map[string]string)
284	}
285
286	// Pick a name. We try to use the final element of the path
287	// but fall back to _.
288	prefix := strings.TrimRight(url, "/")
289	if i := strings.LastIndex(prefix, "/"); i >= 0 {
290		prefix = prefix[i+1:]
291	}
292	if prefix == "" || !isName([]byte(prefix)) || strings.Contains(prefix, ":") {
293		prefix = "_"
294	}
295	if strings.HasPrefix(prefix, "xml") {
296		// xmlanything is reserved.
297		prefix = "_" + prefix
298	}
299	if p.attrNS[prefix] != "" {
300		// Name is taken. Find a better one.
301		for p.seq++; ; p.seq++ {
302			if id := prefix + "_" + strconv.Itoa(p.seq); p.attrNS[id] == "" {
303				prefix = id
304				break
305			}
306		}
307	}
308
309	p.attrPrefix[url] = prefix
310	p.attrNS[prefix] = url
311
312	p.WriteString(`xmlns:`)
313	p.WriteString(prefix)
314	p.WriteString(`="`)
315	EscapeText(p, []byte(url))
316	p.WriteString(`" `)
317
318	p.prefixes = append(p.prefixes, prefix)
319
320	return prefix
321}
322
323// deleteAttrPrefix removes an attribute name space prefix.
324func (p *printer) deleteAttrPrefix(prefix string) {
325	delete(p.attrPrefix, p.attrNS[prefix])
326	delete(p.attrNS, prefix)
327}
328
329func (p *printer) markPrefix() {
330	p.prefixes = append(p.prefixes, "")
331}
332
333func (p *printer) popPrefix() {
334	for len(p.prefixes) > 0 {
335		prefix := p.prefixes[len(p.prefixes)-1]
336		p.prefixes = p.prefixes[:len(p.prefixes)-1]
337		if prefix == "" {
338			break
339		}
340		p.deleteAttrPrefix(prefix)
341	}
342}
343
344var (
345	marshalerType     = reflect.TypeOf((*Marshaler)(nil)).Elem()
346	marshalerAttrType = reflect.TypeOf((*MarshalerAttr)(nil)).Elem()
347	textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
348)
349
350// marshalValue writes one or more XML elements representing val.
351// If val was obtained from a struct field, finfo must have its details.
352func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error {
353	if startTemplate != nil && startTemplate.Name.Local == "" {
354		return fmt.Errorf("xml: EncodeElement of StartElement with missing name")
355	}
356
357	if !val.IsValid() {
358		return nil
359	}
360	if finfo != nil && finfo.flags&fOmitEmpty != 0 && isEmptyValue(val) {
361		return nil
362	}
363
364	// Drill into interfaces and pointers.
365	// This can turn into an infinite loop given a cyclic chain,
366	// but it matches the Go 1 behavior.
367	for val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
368		if val.IsNil() {
369			return nil
370		}
371		val = val.Elem()
372	}
373
374	kind := val.Kind()
375	typ := val.Type()
376
377	// Check for marshaler.
378	if val.CanInterface() && typ.Implements(marshalerType) {
379		return p.marshalInterface(val.Interface().(Marshaler), defaultStart(typ, finfo, startTemplate))
380	}
381	if val.CanAddr() {
382		pv := val.Addr()
383		if pv.CanInterface() && pv.Type().Implements(marshalerType) {
384			return p.marshalInterface(pv.Interface().(Marshaler), defaultStart(pv.Type(), finfo, startTemplate))
385		}
386	}
387
388	// Check for text marshaler.
389	if val.CanInterface() && typ.Implements(textMarshalerType) {
390		return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), defaultStart(typ, finfo, startTemplate))
391	}
392	if val.CanAddr() {
393		pv := val.Addr()
394		if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
395			return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), defaultStart(pv.Type(), finfo, startTemplate))
396		}
397	}
398
399	// Slices and arrays iterate over the elements. They do not have an enclosing tag.
400	if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
401		for i, n := 0, val.Len(); i < n; i++ {
402			if err := p.marshalValue(val.Index(i), finfo, startTemplate); err != nil {
403				return err
404			}
405		}
406		return nil
407	}
408
409	tinfo, err := getTypeInfo(typ)
410	if err != nil {
411		return err
412	}
413
414	// Create start element.
415	// Precedence for the XML element name is:
416	// 0. startTemplate
417	// 1. XMLName field in underlying struct;
418	// 2. field name/tag in the struct field; and
419	// 3. type name
420	var start StartElement
421
422	if startTemplate != nil {
423		start.Name = startTemplate.Name
424		start.Attr = append(start.Attr, startTemplate.Attr...)
425	} else if tinfo.xmlname != nil {
426		xmlname := tinfo.xmlname
427		if xmlname.name != "" {
428			start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name
429		} else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
430			start.Name = v
431		}
432	}
433	if start.Name.Local == "" && finfo != nil {
434		start.Name.Space, start.Name.Local = finfo.xmlns, finfo.name
435	}
436	if start.Name.Local == "" {
437		name := typ.Name()
438		if name == "" {
439			return &UnsupportedTypeError{typ}
440		}
441		start.Name.Local = name
442	}
443
444	// Add type attribute if necessary
445	if finfo != nil && finfo.flags&fTypeAttr != 0 {
446		start.Attr = append(start.Attr, Attr{xmlSchemaInstance, typeToString(typ)})
447	}
448
449	// Attributes
450	for i := range tinfo.fields {
451		finfo := &tinfo.fields[i]
452		if finfo.flags&fAttr == 0 {
453			continue
454		}
455		fv := finfo.value(val)
456		name := Name{Space: finfo.xmlns, Local: finfo.name}
457
458		if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
459			continue
460		}
461
462		if fv.Kind() == reflect.Interface && fv.IsNil() {
463			continue
464		}
465
466		if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
467			attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
468			if err != nil {
469				return err
470			}
471			if attr.Name.Local != "" {
472				start.Attr = append(start.Attr, attr)
473			}
474			continue
475		}
476
477		if fv.CanAddr() {
478			pv := fv.Addr()
479			if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
480				attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
481				if err != nil {
482					return err
483				}
484				if attr.Name.Local != "" {
485					start.Attr = append(start.Attr, attr)
486				}
487				continue
488			}
489		}
490
491		if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
492			text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
493			if err != nil {
494				return err
495			}
496			start.Attr = append(start.Attr, Attr{name, string(text)})
497			continue
498		}
499
500		if fv.CanAddr() {
501			pv := fv.Addr()
502			if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
503				text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
504				if err != nil {
505					return err
506				}
507				start.Attr = append(start.Attr, Attr{name, string(text)})
508				continue
509			}
510		}
511
512		// Dereference or skip nil pointer, interface values.
513		switch fv.Kind() {
514		case reflect.Ptr, reflect.Interface:
515			if fv.IsNil() {
516				continue
517			}
518			fv = fv.Elem()
519		}
520
521		s, b, err := p.marshalSimple(fv.Type(), fv)
522		if err != nil {
523			return err
524		}
525		if b != nil {
526			s = string(b)
527		}
528		start.Attr = append(start.Attr, Attr{name, s})
529	}
530
531	if err := p.writeStart(&start); err != nil {
532		return err
533	}
534
535	if val.Kind() == reflect.Struct {
536		err = p.marshalStruct(tinfo, val)
537	} else {
538		s, b, err1 := p.marshalSimple(typ, val)
539		if err1 != nil {
540			err = err1
541		} else if b != nil {
542			EscapeText(p, b)
543		} else {
544			p.EscapeString(s)
545		}
546	}
547	if err != nil {
548		return err
549	}
550
551	if err := p.writeEnd(start.Name); err != nil {
552		return err
553	}
554
555	return p.cachedWriteError()
556}
557
558// defaultStart returns the default start element to use,
559// given the reflect type, field info, and start template.
560func defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement {
561	var start StartElement
562	// Precedence for the XML element name is as above,
563	// except that we do not look inside structs for the first field.
564	if startTemplate != nil {
565		start.Name = startTemplate.Name
566		start.Attr = append(start.Attr, startTemplate.Attr...)
567	} else if finfo != nil && finfo.name != "" {
568		start.Name.Local = finfo.name
569		start.Name.Space = finfo.xmlns
570	} else if typ.Name() != "" {
571		start.Name.Local = typ.Name()
572	} else {
573		// Must be a pointer to a named type,
574		// since it has the Marshaler methods.
575		start.Name.Local = typ.Elem().Name()
576	}
577
578	// Add type attribute if necessary
579	if finfo != nil && finfo.flags&fTypeAttr != 0 {
580		start.Attr = append(start.Attr, Attr{xmlSchemaInstance, typeToString(typ)})
581	}
582
583	return start
584}
585
586// marshalInterface marshals a Marshaler interface value.
587func (p *printer) marshalInterface(val Marshaler, start StartElement) error {
588	// Push a marker onto the tag stack so that MarshalXML
589	// cannot close the XML tags that it did not open.
590	p.tags = append(p.tags, Name{})
591	n := len(p.tags)
592
593	err := val.MarshalXML(p.encoder, start)
594	if err != nil {
595		return err
596	}
597
598	// Make sure MarshalXML closed all its tags. p.tags[n-1] is the mark.
599	if len(p.tags) > n {
600		return fmt.Errorf("xml: %s.MarshalXML wrote invalid XML: <%s> not closed", receiverType(val), p.tags[len(p.tags)-1].Local)
601	}
602	p.tags = p.tags[:n-1]
603	return nil
604}
605
606// marshalTextInterface marshals a TextMarshaler interface value.
607func (p *printer) marshalTextInterface(val encoding.TextMarshaler, start StartElement) error {
608	if err := p.writeStart(&start); err != nil {
609		return err
610	}
611	text, err := val.MarshalText()
612	if err != nil {
613		return err
614	}
615	EscapeText(p, text)
616	return p.writeEnd(start.Name)
617}
618
619// writeStart writes the given start element.
620func (p *printer) writeStart(start *StartElement) error {
621	if start.Name.Local == "" {
622		return fmt.Errorf("xml: start tag with no name")
623	}
624
625	p.tags = append(p.tags, start.Name)
626	p.markPrefix()
627
628	p.writeIndent(1)
629	p.WriteByte('<')
630	p.WriteString(start.Name.Local)
631
632	if start.Name.Space != "" {
633		p.WriteString(` xmlns="`)
634		p.EscapeString(start.Name.Space)
635		p.WriteByte('"')
636	}
637
638	// Attributes
639	for _, attr := range start.Attr {
640		name := attr.Name
641		if name.Local == "" {
642			continue
643		}
644		p.WriteByte(' ')
645		if name.Space != "" {
646			p.WriteString(p.createAttrPrefix(name.Space))
647			p.WriteByte(':')
648		}
649		p.WriteString(name.Local)
650		p.WriteString(`="`)
651		p.EscapeString(attr.Value)
652		p.WriteByte('"')
653	}
654	p.WriteByte('>')
655	return nil
656}
657
658func (p *printer) writeEnd(name Name) error {
659	if name.Local == "" {
660		return fmt.Errorf("xml: end tag with no name")
661	}
662	if len(p.tags) == 0 || p.tags[len(p.tags)-1].Local == "" {
663		return fmt.Errorf("xml: end tag </%s> without start tag", name.Local)
664	}
665	if top := p.tags[len(p.tags)-1]; top != name {
666		if top.Local != name.Local {
667			return fmt.Errorf("xml: end tag </%s> does not match start tag <%s>", name.Local, top.Local)
668		}
669		return fmt.Errorf("xml: end tag </%s> in namespace %s does not match start tag <%s> in namespace %s", name.Local, name.Space, top.Local, top.Space)
670	}
671	p.tags = p.tags[:len(p.tags)-1]
672
673	p.writeIndent(-1)
674	p.WriteByte('<')
675	p.WriteByte('/')
676	p.WriteString(name.Local)
677	p.WriteByte('>')
678	p.popPrefix()
679	return nil
680}
681
682func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) (string, []byte, error) {
683	switch val.Kind() {
684	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
685		return strconv.FormatInt(val.Int(), 10), nil, nil
686	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
687		return strconv.FormatUint(val.Uint(), 10), nil, nil
688	case reflect.Float32, reflect.Float64:
689		return strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()), nil, nil
690	case reflect.String:
691		return val.String(), nil, nil
692	case reflect.Bool:
693		return strconv.FormatBool(val.Bool()), nil, nil
694	case reflect.Array:
695		if typ.Elem().Kind() != reflect.Uint8 {
696			break
697		}
698		// [...]byte
699		var bytes []byte
700		if val.CanAddr() {
701			bytes = val.Slice(0, val.Len()).Bytes()
702		} else {
703			bytes = make([]byte, val.Len())
704			reflect.Copy(reflect.ValueOf(bytes), val)
705		}
706		return "", bytes, nil
707	case reflect.Slice:
708		if typ.Elem().Kind() != reflect.Uint8 {
709			break
710		}
711		// []byte
712		return "", val.Bytes(), nil
713	}
714	return "", nil, &UnsupportedTypeError{typ}
715}
716
717var ddBytes = []byte("--")
718
719func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
720	s := parentStack{p: p}
721	for i := range tinfo.fields {
722		finfo := &tinfo.fields[i]
723		if finfo.flags&fAttr != 0 {
724			continue
725		}
726		vf := finfo.value(val)
727
728		// Dereference or skip nil pointer, interface values.
729		switch vf.Kind() {
730		case reflect.Ptr, reflect.Interface:
731			if !vf.IsNil() {
732				vf = vf.Elem()
733			}
734		}
735
736		switch finfo.flags & fMode {
737		case fCharData:
738			if vf.CanInterface() && vf.Type().Implements(textMarshalerType) {
739				data, err := vf.Interface().(encoding.TextMarshaler).MarshalText()
740				if err != nil {
741					return err
742				}
743				Escape(p, data)
744				continue
745			}
746			if vf.CanAddr() {
747				pv := vf.Addr()
748				if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
749					data, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
750					if err != nil {
751						return err
752					}
753					Escape(p, data)
754					continue
755				}
756			}
757			var scratch [64]byte
758			switch vf.Kind() {
759			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
760				Escape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10))
761			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
762				Escape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10))
763			case reflect.Float32, reflect.Float64:
764				Escape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits()))
765			case reflect.Bool:
766				Escape(p, strconv.AppendBool(scratch[:0], vf.Bool()))
767			case reflect.String:
768				if err := EscapeText(p, []byte(vf.String())); err != nil {
769					return err
770				}
771			case reflect.Slice:
772				if elem, ok := vf.Interface().([]byte); ok {
773					if err := EscapeText(p, elem); err != nil {
774						return err
775					}
776				}
777			}
778			continue
779
780		case fComment:
781			k := vf.Kind()
782			if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) {
783				return fmt.Errorf("xml: bad type for comment field of %s", val.Type())
784			}
785			if vf.Len() == 0 {
786				continue
787			}
788			p.writeIndent(0)
789			p.WriteString("<!--")
790			dashDash := false
791			dashLast := false
792			switch k {
793			case reflect.String:
794				s := vf.String()
795				dashDash = strings.Index(s, "--") >= 0
796				dashLast = s[len(s)-1] == '-'
797				if !dashDash {
798					p.WriteString(s)
799				}
800			case reflect.Slice:
801				b := vf.Bytes()
802				dashDash = bytes.Index(b, ddBytes) >= 0
803				dashLast = b[len(b)-1] == '-'
804				if !dashDash {
805					p.Write(b)
806				}
807			default:
808				panic("can't happen")
809			}
810			if dashDash {
811				return fmt.Errorf(`xml: comments must not contain "--"`)
812			}
813			if dashLast {
814				// "--->" is invalid grammar. Make it "- -->"
815				p.WriteByte(' ')
816			}
817			p.WriteString("-->")
818			continue
819
820		case fInnerXml:
821			iface := vf.Interface()
822			switch raw := iface.(type) {
823			case []byte:
824				p.Write(raw)
825				continue
826			case string:
827				p.WriteString(raw)
828				continue
829			}
830
831		case fElement, fElement | fAny:
832			if err := s.trim(finfo.parents); err != nil {
833				return err
834			}
835			if len(finfo.parents) > len(s.stack) {
836				if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() {
837					if err := s.push(finfo.parents[len(s.stack):]); err != nil {
838						return err
839					}
840				}
841			}
842		}
843		if err := p.marshalValue(vf, finfo, nil); err != nil {
844			return err
845		}
846	}
847	s.trim(nil)
848	return p.cachedWriteError()
849}
850
851// return the bufio Writer's cached write error
852func (p *printer) cachedWriteError() error {
853	_, err := p.Write(nil)
854	return err
855}
856
857func (p *printer) writeIndent(depthDelta int) {
858	if len(p.prefix) == 0 && len(p.indent) == 0 {
859		return
860	}
861	if depthDelta < 0 {
862		p.depth--
863		if p.indentedIn {
864			p.indentedIn = false
865			return
866		}
867		p.indentedIn = false
868	}
869	if p.putNewline {
870		p.WriteByte('\n')
871	} else {
872		p.putNewline = true
873	}
874	if len(p.prefix) > 0 {
875		p.WriteString(p.prefix)
876	}
877	if len(p.indent) > 0 {
878		for i := 0; i < p.depth; i++ {
879			p.WriteString(p.indent)
880		}
881	}
882	if depthDelta > 0 {
883		p.depth++
884		p.indentedIn = true
885	}
886}
887
888type parentStack struct {
889	p     *printer
890	stack []string
891}
892
893// trim updates the XML context to match the longest common prefix of the stack
894// and the given parents.  A closing tag will be written for every parent
895// popped.  Passing a zero slice or nil will close all the elements.
896func (s *parentStack) trim(parents []string) error {
897	split := 0
898	for ; split < len(parents) && split < len(s.stack); split++ {
899		if parents[split] != s.stack[split] {
900			break
901		}
902	}
903	for i := len(s.stack) - 1; i >= split; i-- {
904		if err := s.p.writeEnd(Name{Local: s.stack[i]}); err != nil {
905			return err
906		}
907	}
908	s.stack = parents[:split]
909	return nil
910}
911
912// push adds parent elements to the stack and writes open tags.
913func (s *parentStack) push(parents []string) error {
914	for i := 0; i < len(parents); i++ {
915		if err := s.p.writeStart(&StartElement{Name: Name{Local: parents[i]}}); err != nil {
916			return err
917		}
918	}
919	s.stack = append(s.stack, parents...)
920	return nil
921}
922
923// A MarshalXMLError is returned when Marshal encounters a type
924// that cannot be converted into XML.
925type UnsupportedTypeError struct {
926	Type reflect.Type
927}
928
929func (e *UnsupportedTypeError) Error() string {
930	return "xml: unsupported type: " + e.Type.String()
931}
932
933func isEmptyValue(v reflect.Value) bool {
934	switch v.Kind() {
935	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
936		return v.Len() == 0
937	case reflect.Bool:
938		return !v.Bool()
939	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
940		return v.Int() == 0
941	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
942		return v.Uint() == 0
943	case reflect.Float32, reflect.Float64:
944		return v.Float() == 0
945	case reflect.Interface, reflect.Ptr:
946		return v.IsNil()
947	}
948	return false
949}
950