src/pkg/encoding/xml/marshal.go - The Go Programming Language

Golang

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	}