Run Format

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 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.
    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.Indent(prefix, indent)
    85		if err := enc.Encode(v); err != nil {
    86			return nil, err
    87		}
    88		return b.Bytes(), nil
    89	}
    90	
    91	// An Encoder writes XML data to an output stream.
    92	type Encoder struct {
    93		printer
    94	}
    95	
    96	// NewEncoder returns a new encoder that writes to w.
    97	func NewEncoder(w io.Writer) *Encoder {
    98		return &Encoder{printer{Writer: bufio.NewWriter(w)}}
    99	}
   100	
   101	// Indent sets the encoder to generate XML in which each element
   102	// begins on a new indented line that starts with prefix and is followed by
   103	// one or more copies of indent according to the nesting depth.
   104	func (enc *Encoder) Indent(prefix, indent string) {
   105		enc.prefix = prefix
   106		enc.indent = indent
   107	}
   108	
   109	// Encode writes the XML encoding of v to the stream.
   110	//
   111	// See the documentation for Marshal for details about the conversion
   112	// of Go values to XML.
   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 // map prefix -> name space
   130		attrPrefix map[string]string // map name space -> prefix
   131	}
   132	
   133	// createAttrPrefix finds the name space prefix attribute to use for the given name space,
   134	// defining a new prefix if necessary. It returns the prefix and whether it is new.
   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		// The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml"
   141		// and must be referred to that way.
   142		// (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns",
   143		// but users should not be trying to use that one directly - that's our job.)
   144		if url == xmlURL {
   145			return "xml", false
   146		}
   147	
   148		// Need to define a new name space.
   149		if p.attrPrefix == nil {
   150			p.attrPrefix = make(map[string]string)
   151			p.attrNS = make(map[string]string)
   152		}
   153	
   154		// Pick a name. We try to use the final element of the path
   155		// but fall back to _.
   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			// xmlanything is reserved.
   165			prefix = "_" + prefix
   166		}
   167		if p.attrNS[prefix] != "" {
   168			// Name is taken. Find a better one.
   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	// deleteAttrPrefix removes an attribute name space prefix.
   190	func (p *printer) deleteAttrPrefix(prefix string) {
   191		delete(p.attrPrefix, p.attrNS[prefix])
   192		delete(p.attrNS, prefix)
   193	}
   194	
   195	// marshalValue writes one or more XML elements representing val.
   196	// If val was obtained from a struct field, finfo must have its details.
   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		// Drill into pointers/interfaces
   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		// Slices and arrays iterate over the elements. They do not have an enclosing tag.
   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		// Precedence for the XML element name is:
   232		// 1. XMLName field in underlying struct;
   233		// 2. field name/tag in the struct field; and
   234		// 3. type name
   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			// TODO: EscapeString, to avoid the allocation.
   261			if err := EscapeText(p, []byte(xmlns)); err != nil {
   262				return err
   263			}
   264			p.WriteByte('"')
   265		}
   266	
   267		// Attributes
   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		// Normally we don't see structs, but this can happen for an attribute.
   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			// TODO: Add EscapeString.
   330			EscapeText(p, []byte(val.String()))
   331		case reflect.Bool:
   332			p.WriteString(strconv.FormatBool(val.Bool()))
   333		case reflect.Array:
   334			// will be [...]byte
   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			// will be []byte
   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					// "--->" is invalid grammar. Make it "- -->"
   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	// return the bufio Writer's cached write error
   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	// trim updates the XML context to match the longest common prefix of the stack
   505	// and the given parents.  A closing tag will be written for every parent
   506	// popped.  Passing a zero slice or nil will close all the elements.
   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	// push adds parent elements to the stack and writes open tags.
   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	// A MarshalXMLError is returned when Marshal encounters a type
   535	// that cannot be converted into XML.
   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