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.
19 // Header is 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"
25 // Marshal returns the XML encoding of v.
27 // Marshal handles an array or slice by marshaling each of the elements.
28 // Marshal handles a pointer by marshaling the value it points at or, if the
29 // pointer is nil, by writing nothing. Marshal handles an interface value by
30 // marshaling 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.
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 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 marshaled type
41 // The XML element for a struct contains marshaled 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 ",cdata" is written as character data
52 // wrapped in one or more <![CDATA[ ... ]]> tags, not as an XML element.
53 // - a field with tag ",innerxml" is written verbatim, not subject
54 // to the usual marshaling procedure.
55 // - a field with tag ",comment" is written as an XML comment, not
56 // subject to the usual marshaling procedure. It must not contain
57 // the "--" string within it.
58 // - a field with a tag including the "omitempty" option is omitted
59 // if the field value is empty. The empty values are false, 0, any
60 // nil pointer or interface value, and any array, slice, map, or
61 // string of length zero.
62 // - an anonymous struct field is handled as if the fields of its
63 // value were part of the outer struct.
65 // If a field uses a tag "a>b>c", then the element c will be nested inside
66 // parent elements a and b. Fields that appear next to each other that name
67 // the same parent will be enclosed in one XML element.
69 // If the XML name for a struct field is defined by both the field tag and the
70 // struct's XMLName field, the names must match.
72 // See MarshalIndent for an example.
74 // Marshal will return an error if asked to marshal a channel, function, or map.
75 func Marshal(v
interface{}) ([]byte, error
) {
77 if err
:= NewEncoder(&b
).Encode(v
); err
!= nil {
83 // Marshaler is the interface implemented by objects that can marshal
84 // themselves into valid XML elements.
86 // MarshalXML encodes the receiver as zero or more XML elements.
87 // By convention, arrays or slices are typically encoded as a sequence
88 // of elements, one per entry.
89 // Using start as the element tag is not required, but doing so
90 // will enable Unmarshal to match the XML elements to the correct
92 // One common implementation strategy is to construct a separate
93 // value with a layout corresponding to the desired XML and then
94 // to encode it using e.EncodeElement.
95 // Another common strategy is to use repeated calls to e.EncodeToken
96 // to generate the XML output one token at a time.
97 // The sequence of encoded tokens must make up zero or more valid
99 type Marshaler
interface {
100 MarshalXML(e
*Encoder
, start StartElement
) error
103 // MarshalerAttr is the interface implemented by objects that can marshal
104 // themselves into valid XML attributes.
106 // MarshalXMLAttr returns an XML attribute with the encoded value of the receiver.
107 // Using name as the attribute name is not required, but doing so
108 // will enable Unmarshal to match the attribute to the correct
110 // If MarshalXMLAttr returns the zero attribute Attr{}, no attribute
111 // will be generated in the output.
112 // MarshalXMLAttr is used only for struct fields with the
113 // "attr" option in the field tag.
114 type MarshalerAttr
interface {
115 MarshalXMLAttr(name Name
) (Attr
, error
)
118 // MarshalIndent works like Marshal, but each XML element begins on a new
119 // indented line that starts with prefix and is followed by one or more
120 // copies of indent according to the nesting depth.
121 func MarshalIndent(v
interface{}, prefix
, indent
string) ([]byte, error
) {
123 enc
:= NewEncoder(&b
)
124 enc
.Indent(prefix
, indent
)
125 if err
:= enc
.Encode(v
); err
!= nil {
128 return b
.Bytes(), nil
131 // An Encoder writes XML data to an output stream.
132 type Encoder
struct {
136 // NewEncoder returns a new encoder that writes to w.
137 func NewEncoder(w io
.Writer
) *Encoder
{
138 e
:= &Encoder
{printer
{Writer
: bufio
.NewWriter(w
)}}
143 // Indent sets the encoder to generate XML in which each element
144 // begins on a new indented line that starts with prefix and is followed by
145 // one or more copies of indent according to the nesting depth.
146 func (enc
*Encoder
) Indent(prefix
, indent
string) {
147 enc
.p
.prefix
= prefix
148 enc
.p
.indent
= indent
151 // Encode writes the XML encoding of v to the stream.
153 // See the documentation for Marshal for details about the conversion
154 // of Go values to XML.
156 // Encode calls Flush before returning.
157 func (enc
*Encoder
) Encode(v
interface{}) error
{
158 err
:= enc
.p
.marshalValue(reflect
.ValueOf(v
), nil, nil)
165 // EncodeElement writes the XML encoding of v to the stream,
166 // using start as the outermost tag in the encoding.
168 // See the documentation for Marshal for details about the conversion
169 // of Go values to XML.
171 // EncodeElement calls Flush before returning.
172 func (enc
*Encoder
) EncodeElement(v
interface{}, start StartElement
) error
{
173 err
:= enc
.p
.marshalValue(reflect
.ValueOf(v
), nil, &start
)
181 begComment
= []byte("<!--")
182 endComment
= []byte("-->")
183 endProcInst
= []byte("?>")
186 // EncodeToken writes the given XML token to the stream.
187 // It returns an error if StartElement and EndElement tokens are not properly matched.
189 // EncodeToken does not call Flush, because usually it is part of a larger operation
190 // such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked
191 // during those), and those will call Flush when finished.
192 // Callers that create an Encoder and then invoke EncodeToken directly, without
193 // using Encode or EncodeElement, need to call Flush when finished to ensure
194 // that the XML is written to the underlying writer.
196 // EncodeToken allows writing a ProcInst with Target set to "xml" only as the first token
198 func (enc
*Encoder
) EncodeToken(t Token
) error
{
201 switch t
:= t
.(type) {
203 if err
:= p
.writeStart(&t
); err
!= nil {
207 if err
:= p
.writeEnd(t
.Name
); err
!= nil {
211 escapeText(p
, t
, false)
213 if bytes
.Contains(t
, endComment
) {
214 return fmt
.Errorf("xml: EncodeToken of Comment containing --> marker")
216 p
.WriteString("<!--")
219 return p
.cachedWriteError()
221 // First token to be encoded which is also a ProcInst with target of xml
222 // is the xml declaration. The only ProcInst where target of xml is allowed.
223 if t
.Target
== "xml" && p
.Buffered() != 0 {
224 return fmt
.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
226 if !isNameString(t
.Target
) {
227 return fmt
.Errorf("xml: EncodeToken of ProcInst with invalid Target")
229 if bytes
.Contains(t
.Inst
, endProcInst
) {
230 return fmt
.Errorf("xml: EncodeToken of ProcInst containing ?> marker")
233 p
.WriteString(t
.Target
)
240 if !isValidDirective(t
) {
241 return fmt
.Errorf("xml: EncodeToken of Directive containing wrong < or > markers")
247 return fmt
.Errorf("xml: EncodeToken of invalid token type")
250 return p
.cachedWriteError()
253 // isValidDirective reports whether dir is a valid directive text,
254 // meaning angle brackets are matched, ignoring comments and strings.
255 func isValidDirective(dir Directive
) bool {
261 for i
, c
:= range dir
{
265 if n
:= 1 + i
- len(endComment
); n
>= 0 && bytes
.Equal(dir
[n
:i
+1], endComment
) {
269 // Just ignore anything in comment
274 // Just ignore anything within quotes
275 case c
== '\'' || c
== '"':
278 if i
+len(begComment
) < len(dir
) && bytes
.Equal(dir
[i
:i
+len(begComment
)], begComment
) {
290 return depth
== 0 && inquote
== 0 && !incomment
293 // Flush flushes any buffered XML to the underlying writer.
294 // See the EncodeToken documentation for details about when it is necessary.
295 func (enc
*Encoder
) Flush() error
{
299 type printer
struct {
308 attrNS
map[string]string // map prefix -> name space
309 attrPrefix
map[string]string // map name space -> prefix
314 // createAttrPrefix finds the name space prefix attribute to use for the given name space,
315 // defining a new prefix if necessary. It returns the prefix.
316 func (p
*printer
) createAttrPrefix(url
string) string {
317 if prefix
:= p
.attrPrefix
[url
]; prefix
!= "" {
321 // The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml"
322 // and must be referred to that way.
323 // (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns",
324 // but users should not be trying to use that one directly - that's our job.)
329 // Need to define a new name space.
330 if p
.attrPrefix
== nil {
331 p
.attrPrefix
= make(map[string]string)
332 p
.attrNS
= make(map[string]string)
335 // Pick a name. We try to use the final element of the path
336 // but fall back to _.
337 prefix
:= strings
.TrimRight(url
, "/")
338 if i
:= strings
.LastIndex(prefix
, "/"); i
>= 0 {
339 prefix
= prefix
[i
+1:]
341 if prefix
== "" ||
!isName([]byte(prefix
)) || strings
.Contains(prefix
, ":") {
344 if strings
.HasPrefix(prefix
, "xml") {
345 // xmlanything is reserved.
346 prefix
= "_" + prefix
348 if p
.attrNS
[prefix
] != "" {
349 // Name is taken. Find a better one.
350 for p
.seq
++; ; p
.seq
++ {
351 if id
:= prefix
+ "_" + strconv
.Itoa(p
.seq
); p
.attrNS
[id
] == "" {
358 p
.attrPrefix
[url
] = prefix
359 p
.attrNS
[prefix
] = url
361 p
.WriteString(`xmlns:`)
362 p
.WriteString(prefix
)
364 EscapeText(p
, []byte(url
))
367 p
.prefixes
= append(p
.prefixes
, prefix
)
372 // deleteAttrPrefix removes an attribute name space prefix.
373 func (p
*printer
) deleteAttrPrefix(prefix
string) {
374 delete(p
.attrPrefix
, p
.attrNS
[prefix
])
375 delete(p
.attrNS
, prefix
)
378 func (p
*printer
) markPrefix() {
379 p
.prefixes
= append(p
.prefixes
, "")
382 func (p
*printer
) popPrefix() {
383 for len(p
.prefixes
) > 0 {
384 prefix
:= p
.prefixes
[len(p
.prefixes
)-1]
385 p
.prefixes
= p
.prefixes
[:len(p
.prefixes
)-1]
389 p
.deleteAttrPrefix(prefix
)
394 marshalerType
= reflect
.TypeOf((*Marshaler
)(nil)).Elem()
395 marshalerAttrType
= reflect
.TypeOf((*MarshalerAttr
)(nil)).Elem()
396 textMarshalerType
= reflect
.TypeOf((*encoding
.TextMarshaler
)(nil)).Elem()
399 // marshalValue writes one or more XML elements representing val.
400 // If val was obtained from a struct field, finfo must have its details.
401 func (p
*printer
) marshalValue(val reflect
.Value
, finfo
*fieldInfo
, startTemplate
*StartElement
) error
{
402 if startTemplate
!= nil && startTemplate
.Name
.Local
== "" {
403 return fmt
.Errorf("xml: EncodeElement of StartElement with missing name")
409 if finfo
!= nil && finfo
.flags
&fOmitEmpty
!= 0 && isEmptyValue(val
) {
413 // Drill into interfaces and pointers.
414 // This can turn into an infinite loop given a cyclic chain,
415 // but it matches the Go 1 behavior.
416 for val
.Kind() == reflect
.Interface || val
.Kind() == reflect
.Ptr
{
426 // Check for marshaler.
427 if val
.CanInterface() && typ
.Implements(marshalerType
) {
428 return p
.marshalInterface(val
.Interface().(Marshaler
), defaultStart(typ
, finfo
, startTemplate
))
432 if pv
.CanInterface() && pv
.Type().Implements(marshalerType
) {
433 return p
.marshalInterface(pv
.Interface().(Marshaler
), defaultStart(pv
.Type(), finfo
, startTemplate
))
437 // Check for text marshaler.
438 if val
.CanInterface() && typ
.Implements(textMarshalerType
) {
439 return p
.marshalTextInterface(val
.Interface().(encoding
.TextMarshaler
), defaultStart(typ
, finfo
, startTemplate
))
443 if pv
.CanInterface() && pv
.Type().Implements(textMarshalerType
) {
444 return p
.marshalTextInterface(pv
.Interface().(encoding
.TextMarshaler
), defaultStart(pv
.Type(), finfo
, startTemplate
))
448 // Slices and arrays iterate over the elements. They do not have an enclosing tag.
449 if (kind
== reflect
.Slice || kind
== reflect
.Array
) && typ
.Elem().Kind() != reflect
.Uint8
{
450 for i
, n
:= 0, val
.Len(); i
< n
; i
++ {
451 if err
:= p
.marshalValue(val
.Index(i
), finfo
, startTemplate
); err
!= nil {
458 tinfo
, err
:= getTypeInfo(typ
)
463 // Create start element.
464 // Precedence for the XML element name is:
466 // 1. XMLName field in underlying struct;
467 // 2. field name/tag in the struct field; and
469 var start StartElement
471 if startTemplate
!= nil {
472 start
.Name
= startTemplate
.Name
473 start
.Attr
= append(start
.Attr
, startTemplate
.Attr
...)
474 } else if tinfo
.xmlname
!= nil {
475 xmlname
:= tinfo
.xmlname
476 if xmlname
.name
!= "" {
477 start
.Name
.Space
, start
.Name
.Local
= xmlname
.xmlns
, xmlname
.name
478 } else if v
, ok
:= xmlname
.value(val
).Interface().(Name
); ok
&& v
.Local
!= "" {
482 if start
.Name
.Local
== "" && finfo
!= nil {
483 start
.Name
.Space
, start
.Name
.Local
= finfo
.xmlns
, finfo
.name
485 if start
.Name
.Local
== "" {
488 return &UnsupportedTypeError
{typ
}
490 start
.Name
.Local
= name
494 for i
:= range tinfo
.fields
{
495 finfo
:= &tinfo
.fields
[i
]
496 if finfo
.flags
&fAttr
== 0 {
499 fv
:= finfo
.value(val
)
501 if finfo
.flags
&fOmitEmpty
!= 0 && isEmptyValue(fv
) {
505 if fv
.Kind() == reflect
.Interface
&& fv
.IsNil() {
509 name
:= Name
{Space
: finfo
.xmlns
, Local
: finfo
.name
}
510 if err
:= p
.marshalAttr(&start
, name
, fv
); err
!= nil {
515 if err
:= p
.writeStart(&start
); err
!= nil {
519 if val
.Kind() == reflect
.Struct
{
520 err
= p
.marshalStruct(tinfo
, val
)
522 s
, b
, err1
:= p
.marshalSimple(typ
, val
)
535 if err
:= p
.writeEnd(start
.Name
); err
!= nil {
539 return p
.cachedWriteError()
542 // marshalAttr marshals an attribute with the given name and value, adding to start.Attr.
543 func (p
*printer
) marshalAttr(start
*StartElement
, name Name
, val reflect
.Value
) error
{
544 if val
.CanInterface() && val
.Type().Implements(marshalerAttrType
) {
545 attr
, err
:= val
.Interface().(MarshalerAttr
).MarshalXMLAttr(name
)
549 if attr
.Name
.Local
!= "" {
550 start
.Attr
= append(start
.Attr
, attr
)
557 if pv
.CanInterface() && pv
.Type().Implements(marshalerAttrType
) {
558 attr
, err
:= pv
.Interface().(MarshalerAttr
).MarshalXMLAttr(name
)
562 if attr
.Name
.Local
!= "" {
563 start
.Attr
= append(start
.Attr
, attr
)
569 if val
.CanInterface() && val
.Type().Implements(textMarshalerType
) {
570 text
, err
:= val
.Interface().(encoding
.TextMarshaler
).MarshalText()
574 start
.Attr
= append(start
.Attr
, Attr
{name
, string(text
)})
580 if pv
.CanInterface() && pv
.Type().Implements(textMarshalerType
) {
581 text
, err
:= pv
.Interface().(encoding
.TextMarshaler
).MarshalText()
585 start
.Attr
= append(start
.Attr
, Attr
{name
, string(text
)})
590 // Dereference or skip nil pointer, interface values.
592 case reflect
.Ptr
, reflect
.Interface
:
600 if val
.Kind() == reflect
.Slice
&& val
.Type().Elem().Kind() != reflect
.Uint8
{
602 for i
:= 0; i
< n
; i
++ {
603 if err
:= p
.marshalAttr(start
, name
, val
.Index(i
)); err
!= nil {
610 if val
.Type() == attrType
{
611 start
.Attr
= append(start
.Attr
, val
.Interface().(Attr
))
615 s
, b
, err
:= p
.marshalSimple(val
.Type(), val
)
622 start
.Attr
= append(start
.Attr
, Attr
{name
, s
})
626 // defaultStart returns the default start element to use,
627 // given the reflect type, field info, and start template.
628 func defaultStart(typ reflect
.Type
, finfo
*fieldInfo
, startTemplate
*StartElement
) StartElement
{
629 var start StartElement
630 // Precedence for the XML element name is as above,
631 // except that we do not look inside structs for the first field.
632 if startTemplate
!= nil {
633 start
.Name
= startTemplate
.Name
634 start
.Attr
= append(start
.Attr
, startTemplate
.Attr
...)
635 } else if finfo
!= nil && finfo
.name
!= "" {
636 start
.Name
.Local
= finfo
.name
637 start
.Name
.Space
= finfo
.xmlns
638 } else if typ
.Name() != "" {
639 start
.Name
.Local
= typ
.Name()
641 // Must be a pointer to a named type,
642 // since it has the Marshaler methods.
643 start
.Name
.Local
= typ
.Elem().Name()
648 // marshalInterface marshals a Marshaler interface value.
649 func (p
*printer
) marshalInterface(val Marshaler
, start StartElement
) error
{
650 // Push a marker onto the tag stack so that MarshalXML
651 // cannot close the XML tags that it did not open.
652 p
.tags
= append(p
.tags
, Name
{})
655 err
:= val
.MarshalXML(p
.encoder
, start
)
660 // Make sure MarshalXML closed all its tags. p.tags[n-1] is the mark.
662 return fmt
.Errorf("xml: %s.MarshalXML wrote invalid XML: <%s> not closed", receiverType(val
), p
.tags
[len(p
.tags
)-1].Local
)
664 p
.tags
= p
.tags
[:n
-1]
668 // marshalTextInterface marshals a TextMarshaler interface value.
669 func (p
*printer
) marshalTextInterface(val encoding
.TextMarshaler
, start StartElement
) error
{
670 if err
:= p
.writeStart(&start
); err
!= nil {
673 text
, err
:= val
.MarshalText()
678 return p
.writeEnd(start
.Name
)
681 // writeStart writes the given start element.
682 func (p
*printer
) writeStart(start
*StartElement
) error
{
683 if start
.Name
.Local
== "" {
684 return fmt
.Errorf("xml: start tag with no name")
687 p
.tags
= append(p
.tags
, start
.Name
)
692 p
.WriteString(start
.Name
.Local
)
694 if start
.Name
.Space
!= "" {
695 p
.WriteString(` xmlns="`)
696 p
.EscapeString(start
.Name
.Space
)
701 for _
, attr
:= range start
.Attr
{
703 if name
.Local
== "" {
707 if name
.Space
!= "" {
708 p
.WriteString(p
.createAttrPrefix(name
.Space
))
711 p
.WriteString(name
.Local
)
713 p
.EscapeString(attr
.Value
)
720 func (p
*printer
) writeEnd(name Name
) error
{
721 if name
.Local
== "" {
722 return fmt
.Errorf("xml: end tag with no name")
724 if len(p
.tags
) == 0 || p
.tags
[len(p
.tags
)-1].Local
== "" {
725 return fmt
.Errorf("xml: end tag </%s> without start tag", name
.Local
)
727 if top
:= p
.tags
[len(p
.tags
)-1]; top
!= name
{
728 if top
.Local
!= name
.Local
{
729 return fmt
.Errorf("xml: end tag </%s> does not match start tag <%s>", name
.Local
, top
.Local
)
731 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
)
733 p
.tags
= p
.tags
[:len(p
.tags
)-1]
738 p
.WriteString(name
.Local
)
744 func (p
*printer
) marshalSimple(typ reflect
.Type
, val reflect
.Value
) (string, []byte, error
) {
746 case reflect
.Int
, reflect
.Int8
, reflect
.Int16
, reflect
.Int32
, reflect
.Int64
:
747 return strconv
.FormatInt(val
.Int(), 10), nil, nil
748 case reflect
.Uint
, reflect
.Uint8
, reflect
.Uint16
, reflect
.Uint32
, reflect
.Uint64
, reflect
.Uintptr
:
749 return strconv
.FormatUint(val
.Uint(), 10), nil, nil
750 case reflect
.Float32
, reflect
.Float64
:
751 return strconv
.FormatFloat(val
.Float(), 'g', -1, val
.Type().Bits()), nil, nil
753 return val
.String(), nil, nil
755 return strconv
.FormatBool(val
.Bool()), nil, nil
757 if typ
.Elem().Kind() != reflect
.Uint8
{
763 bytes
= val
.Slice(0, val
.Len()).Bytes()
765 bytes
= make([]byte, val
.Len())
766 reflect
.Copy(reflect
.ValueOf(bytes
), val
)
768 return "", bytes
, nil
770 if typ
.Elem().Kind() != reflect
.Uint8
{
774 return "", val
.Bytes(), nil
776 return "", nil, &UnsupportedTypeError
{typ
}
779 var ddBytes
= []byte("--")
781 // indirect drills into interfaces and pointers, returning the pointed-at value.
782 // If it encounters a nil interface or pointer, indirect returns that nil value.
783 // This can turn into an infinite loop given a cyclic chain,
784 // but it matches the Go 1 behavior.
785 func indirect(vf reflect
.Value
) reflect
.Value
{
786 for vf
.Kind() == reflect
.Interface || vf
.Kind() == reflect
.Ptr
{
795 func (p
*printer
) marshalStruct(tinfo
*typeInfo
, val reflect
.Value
) error
{
796 s
:= parentStack
{p
: p
}
797 for i
:= range tinfo
.fields
{
798 finfo
:= &tinfo
.fields
[i
]
799 if finfo
.flags
&fAttr
!= 0 {
802 vf
:= finfo
.value(val
)
804 switch finfo
.flags
& fMode
{
805 case fCDATA
, fCharData
:
807 if finfo
.flags
&fMode
== fCDATA
{
810 if err
:= s
.trim(finfo
.parents
); err
!= nil {
813 if vf
.CanInterface() && vf
.Type().Implements(textMarshalerType
) {
814 data
, err
:= vf
.Interface().(encoding
.TextMarshaler
).MarshalText()
818 if err
:= emit(p
, data
); err
!= nil {
825 if pv
.CanInterface() && pv
.Type().Implements(textMarshalerType
) {
826 data
, err
:= pv
.Interface().(encoding
.TextMarshaler
).MarshalText()
830 if err
:= emit(p
, data
); err
!= nil {
840 case reflect
.Int
, reflect
.Int8
, reflect
.Int16
, reflect
.Int32
, reflect
.Int64
:
841 if err
:= emit(p
, strconv
.AppendInt(scratch
[:0], vf
.Int(), 10)); err
!= nil {
844 case reflect
.Uint
, reflect
.Uint8
, reflect
.Uint16
, reflect
.Uint32
, reflect
.Uint64
, reflect
.Uintptr
:
845 if err
:= emit(p
, strconv
.AppendUint(scratch
[:0], vf
.Uint(), 10)); err
!= nil {
848 case reflect
.Float32
, reflect
.Float64
:
849 if err
:= emit(p
, strconv
.AppendFloat(scratch
[:0], vf
.Float(), 'g', -1, vf
.Type().Bits())); err
!= nil {
853 if err
:= emit(p
, strconv
.AppendBool(scratch
[:0], vf
.Bool())); err
!= nil {
857 if err
:= emit(p
, []byte(vf
.String())); err
!= nil {
861 if elem
, ok
:= vf
.Interface().([]byte); ok
{
862 if err
:= emit(p
, elem
); err
!= nil {
870 if err
:= s
.trim(finfo
.parents
); err
!= nil {
875 if !(k
== reflect
.String || k
== reflect
.Slice
&& vf
.Type().Elem().Kind() == reflect
.Uint8
) {
876 return fmt
.Errorf("xml: bad type for comment field of %s", val
.Type())
882 p
.WriteString("<!--")
888 dashDash
= strings
.Contains(s
, "--")
889 dashLast
= s
[len(s
)-1] == '-'
895 dashDash
= bytes
.Contains(b
, ddBytes
)
896 dashLast
= b
[len(b
)-1] == '-'
901 panic("can't happen")
904 return fmt
.Errorf(`xml: comments must not contain "--"`)
907 // "--->" is invalid grammar. Make it "- -->"
915 iface
:= vf
.Interface()
916 switch raw
:= iface
.(type) {
925 case fElement
, fElement | fAny
:
926 if err
:= s
.trim(finfo
.parents
); err
!= nil {
929 if len(finfo
.parents
) > len(s
.stack
) {
930 if vf
.Kind() != reflect
.Ptr
&& vf
.Kind() != reflect
.Interface ||
!vf
.IsNil() {
931 if err
:= s
.push(finfo
.parents
[len(s
.stack
):]); err
!= nil {
937 if err
:= p
.marshalValue(vf
, finfo
, nil); err
!= nil {
942 return p
.cachedWriteError()
945 // return the bufio Writer's cached write error
946 func (p
*printer
) cachedWriteError() error
{
947 _
, err
:= p
.Write(nil)
951 func (p
*printer
) writeIndent(depthDelta
int) {
952 if len(p
.prefix
) == 0 && len(p
.indent
) == 0 {
968 if len(p
.prefix
) > 0 {
969 p
.WriteString(p
.prefix
)
971 if len(p
.indent
) > 0 {
972 for i
:= 0; i
< p
.depth
; i
++ {
973 p
.WriteString(p
.indent
)
982 type parentStack
struct {
987 // trim updates the XML context to match the longest common prefix of the stack
988 // and the given parents. A closing tag will be written for every parent
989 // popped. Passing a zero slice or nil will close all the elements.
990 func (s
*parentStack
) trim(parents
[]string) error
{
992 for ; split
< len(parents
) && split
< len(s
.stack
); split
++ {
993 if parents
[split
] != s
.stack
[split
] {
997 for i
:= len(s
.stack
) - 1; i
>= split
; i
-- {
998 if err
:= s
.p
.writeEnd(Name
{Local
: s
.stack
[i
]}); err
!= nil {
1002 s
.stack
= s
.stack
[:split
]
1006 // push adds parent elements to the stack and writes open tags.
1007 func (s
*parentStack
) push(parents
[]string) error
{
1008 for i
:= 0; i
< len(parents
); i
++ {
1009 if err
:= s
.p
.writeStart(&StartElement
{Name
: Name
{Local
: parents
[i
]}}); err
!= nil {
1013 s
.stack
= append(s
.stack
, parents
...)
1017 // UnsupportedTypeError is returned when Marshal encounters a type
1018 // that cannot be converted into XML.
1019 type UnsupportedTypeError
struct {
1023 func (e
*UnsupportedTypeError
) Error() string {
1024 return "xml: unsupported type: " + e
.Type
.String()
1027 func isEmptyValue(v reflect
.Value
) bool {
1029 case reflect
.Array
, reflect
.Map
, reflect
.Slice
, reflect
.String
:
1033 case reflect
.Int
, reflect
.Int8
, reflect
.Int16
, reflect
.Int32
, reflect
.Int64
:
1035 case reflect
.Uint
, reflect
.Uint8
, reflect
.Uint16
, reflect
.Uint32
, reflect
.Uint64
, reflect
.Uintptr
:
1036 return v
.Uint() == 0
1037 case reflect
.Float32
, reflect
.Float64
:
1038 return v
.Float() == 0
1039 case reflect
.Interface
, reflect
.Ptr
: