xml.go raw

   1  // Copyright 2012-2016, 2018-2019 Charles Banning. 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  // xml.go - basically the core of X2j for map[string]interface{} values.
   6  //          NewMapXml, NewMapXmlReader, mv.Xml, mv.XmlWriter
   7  // see x2j and j2x for wrappers to provide end-to-end transformation of XML and JSON messages.
   8  
   9  package mxj
  10  
  11  import (
  12  	"bytes"
  13  	"encoding/json"
  14  	"encoding/xml"
  15  	"errors"
  16  	"fmt"
  17  	"io"
  18  	"reflect"
  19  	"sort"
  20  	"strconv"
  21  	"strings"
  22  	"time"
  23  )
  24  
  25  var (
  26  	textK      = "#text"
  27  	seqK       = "#seq"
  28  	commentK   = "#comment"
  29  	attrK      = "#attr"
  30  	directiveK = "#directive"
  31  	procinstK  = "#procinst"
  32  	targetK    = "#target"
  33  	instK      = "#inst"
  34  )
  35  
  36  // Support overriding default Map keys prefix
  37  
  38  func SetGlobalKeyMapPrefix(s string) {
  39  	textK = strings.ReplaceAll(textK, textK[0:1], s)
  40  	seqK = strings.ReplaceAll(seqK, seqK[0:1], s)
  41  	commentK = strings.ReplaceAll(commentK, commentK[0:1], s)
  42  	directiveK = strings.ReplaceAll(directiveK, directiveK[0:1], s)
  43  	procinstK = strings.ReplaceAll(procinstK, procinstK[0:1], s)
  44  	targetK = strings.ReplaceAll(targetK, targetK[0:1], s)
  45  	instK = strings.ReplaceAll(instK, instK[0:1], s)
  46  	attrK = strings.ReplaceAll(attrK, attrK[0:1], s)
  47  }
  48  
  49  // ------------------- NewMapXml & NewMapXmlReader ... -------------------------
  50  
  51  // If XmlCharsetReader != nil, it will be used to decode the XML, if required.
  52  // Note: if CustomDecoder != nil, then XmlCharsetReader is ignored;
  53  // set the CustomDecoder attribute instead.
  54  //   import (
  55  //	     charset "code.google.com/p/go-charset/charset"
  56  //	     github.com/clbanning/mxj
  57  //	 )
  58  //   ...
  59  //   mxj.XmlCharsetReader = charset.NewReader
  60  //   m, merr := mxj.NewMapXml(xmlValue)
  61  var XmlCharsetReader func(charset string, input io.Reader) (io.Reader, error)
  62  
  63  // NewMapXml - convert a XML doc into a Map
  64  // (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)
  65  //	If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
  66  //
  67  //	Converting XML to JSON is a simple as:
  68  //		...
  69  //		mapVal, merr := mxj.NewMapXml(xmlVal)
  70  //		if merr != nil {
  71  //			// handle error
  72  //		}
  73  //		jsonVal, jerr := mapVal.Json()
  74  //		if jerr != nil {
  75  //			// handle error
  76  //		}
  77  //
  78  //	NOTES:
  79  //	   1. Declarations, directives, process instructions and comments are NOT parsed.
  80  //	   2. The 'xmlVal' will be parsed looking for an xml.StartElement, so BOM and other
  81  //	      extraneous xml.CharData will be ignored unless io.EOF is reached first.
  82  //	   3. If CoerceKeysToLower() has been called, then all key values will be lower case.
  83  //	   4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
  84  //	   5. If DisableTrimWhiteSpace(b bool) has been called, then all values will be trimmed or not. 'true' by default.
  85  func NewMapXml(xmlVal []byte, cast ...bool) (Map, error) {
  86  	var r bool
  87  	if len(cast) == 1 {
  88  		r = cast[0]
  89  	}
  90  	return xmlToMap(xmlVal, r)
  91  }
  92  
  93  // Get next XML doc from an io.Reader as a Map value.  Returns Map value.
  94  //	NOTES:
  95  //	   1. Declarations, directives, process instructions and comments are NOT parsed.
  96  //	   2. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
  97  //	      extraneous xml.CharData will be ignored unless io.EOF is reached first.
  98  //	   3. If CoerceKeysToLower() has been called, then all key values will be lower case.
  99  //	   4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
 100  func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error) {
 101  	var r bool
 102  	if len(cast) == 1 {
 103  		r = cast[0]
 104  	}
 105  
 106  	// We need to put an *os.File reader in a ByteReader or the xml.NewDecoder
 107  	// will wrap it in a bufio.Reader and seek on the file beyond where the
 108  	// xml.Decoder parses!
 109  	if _, ok := xmlReader.(io.ByteReader); !ok {
 110  		xmlReader = myByteReader(xmlReader) // see code at EOF
 111  	}
 112  
 113  	// build the map
 114  	return xmlReaderToMap(xmlReader, r)
 115  }
 116  
 117  // Get next XML doc from an io.Reader as a Map value.  Returns Map value and slice with the raw XML.
 118  //	NOTES:
 119  //	   1. Declarations, directives, process instructions and comments are NOT parsed.
 120  //	   2. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
 121  //	      using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
 122  //	      See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
 123  //	      data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
 124  //	      you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
 125  //	   3. The 'raw' return value may be larger than the XML text value.
 126  //	   4. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
 127  //	      extraneous xml.CharData will be ignored unless io.EOF is reached first.
 128  //	   5. If CoerceKeysToLower() has been called, then all key values will be lower case.
 129  //	   6. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
 130  func NewMapXmlReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
 131  	var r bool
 132  	if len(cast) == 1 {
 133  		r = cast[0]
 134  	}
 135  	// create TeeReader so we can retrieve raw XML
 136  	buf := make([]byte, 0)
 137  	wb := bytes.NewBuffer(buf)
 138  	trdr := myTeeReader(xmlReader, wb) // see code at EOF
 139  
 140  	m, err := xmlReaderToMap(trdr, r)
 141  
 142  	// retrieve the raw XML that was decoded
 143  	b := wb.Bytes()
 144  
 145  	if err != nil {
 146  		return nil, b, err
 147  	}
 148  
 149  	return m, b, nil
 150  }
 151  
 152  // xmlReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
 153  func xmlReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
 154  	// parse the Reader
 155  	p := xml.NewDecoder(rdr)
 156  	if CustomDecoder != nil {
 157  		useCustomDecoder(p)
 158  	} else {
 159  		p.CharsetReader = XmlCharsetReader
 160  	}
 161  	return xmlToMapParser("", nil, p, r)
 162  }
 163  
 164  // xmlToMap - convert a XML doc into map[string]interface{} value
 165  func xmlToMap(doc []byte, r bool) (map[string]interface{}, error) {
 166  	b := bytes.NewReader(doc)
 167  	p := xml.NewDecoder(b)
 168  	if CustomDecoder != nil {
 169  		useCustomDecoder(p)
 170  	} else {
 171  		p.CharsetReader = XmlCharsetReader
 172  	}
 173  	return xmlToMapParser("", nil, p, r)
 174  }
 175  
 176  // ===================================== where the work happens =============================
 177  
 178  // PrependAttrWithHyphen. Prepend attribute tags with a hyphen.
 179  // Default is 'true'. (Not applicable to NewMapXmlSeq(), mv.XmlSeq(), etc.)
 180  //	Note:
 181  //		If 'false', unmarshaling and marshaling is not symmetric. Attributes will be
 182  //		marshal'd as <attr_tag>attr</attr_tag> and may be part of a list.
 183  func PrependAttrWithHyphen(v bool) {
 184  	if v {
 185  		attrPrefix = "-"
 186  		lenAttrPrefix = len(attrPrefix)
 187  		return
 188  	}
 189  	attrPrefix = ""
 190  	lenAttrPrefix = len(attrPrefix)
 191  }
 192  
 193  // Include sequence id with inner tags. - per Sean Murphy, murphysean84@gmail.com.
 194  var includeTagSeqNum bool
 195  
 196  // IncludeTagSeqNum - include a "_seq":N key:value pair with each inner tag, denoting
 197  // its position when parsed. This is of limited usefulness, since list values cannot
 198  // be tagged with "_seq" without changing their depth in the Map.
 199  // So THIS SHOULD BE USED WITH CAUTION - see the test cases. Here's a sample of what
 200  // you get.
 201  /*
 202  		<Obj c="la" x="dee" h="da">
 203  			<IntObj id="3"/>
 204  			<IntObj1 id="1"/>
 205  			<IntObj id="2"/>
 206  			<StrObj>hello</StrObj>
 207  		</Obj>
 208  
 209  	parses as:
 210  
 211  		{
 212  		Obj:{
 213  			"-c":"la",
 214  			"-h":"da",
 215  			"-x":"dee",
 216  			"intObj":[
 217  				{
 218  					"-id"="3",
 219  					"_seq":"0" // if mxj.Cast is passed, then: "_seq":0
 220  				},
 221  				{
 222  					"-id"="2",
 223  					"_seq":"2"
 224  				}],
 225  			"intObj1":{
 226  				"-id":"1",
 227  				"_seq":"1"
 228  				},
 229  			"StrObj":{
 230  				"#text":"hello", // simple element value gets "#text" tag
 231  				"_seq":"3"
 232  				}
 233  			}
 234  		}
 235  */
 236  func IncludeTagSeqNum(b ...bool) {
 237  	if len(b) == 0 {
 238  		includeTagSeqNum = !includeTagSeqNum
 239  	} else if len(b) == 1 {
 240  		includeTagSeqNum = b[0]
 241  	}
 242  }
 243  
 244  // all keys will be "lower case"
 245  var lowerCase bool
 246  
 247  // Coerce all tag values to keys in lower case.  This is useful if you've got sources with variable
 248  // tag capitalization, and you want to use m.ValuesForKeys(), etc., with the key or path spec
 249  // in lower case.
 250  //	CoerceKeysToLower() will toggle the coercion flag true|false - on|off
 251  //	CoerceKeysToLower(true|false) will set the coercion flag on|off
 252  //
 253  //	NOTE: only recognized by NewMapXml, NewMapXmlReader, and NewMapXmlReaderRaw functions as well as
 254  //	      the associated HandleXmlReader and HandleXmlReaderRaw.
 255  func CoerceKeysToLower(b ...bool) {
 256  	if len(b) == 0 {
 257  		lowerCase = !lowerCase
 258  	} else if len(b) == 1 {
 259  		lowerCase = b[0]
 260  	}
 261  }
 262  
 263  // disableTrimWhiteSpace sets if the white space should be removed or not
 264  var disableTrimWhiteSpace bool
 265  var trimRunes = "\t\r\b\n "
 266  
 267  // DisableTrimWhiteSpace set if the white space should be trimmed or not. By default white space is always trimmed. If
 268  // no argument is provided, trim white space will be disabled.
 269  func DisableTrimWhiteSpace(b ...bool) {
 270  	if len(b) == 0 {
 271  		disableTrimWhiteSpace = true
 272  	} else {
 273  		disableTrimWhiteSpace = b[0]
 274  	}
 275  
 276  	if disableTrimWhiteSpace {
 277  		trimRunes = "\t\r\b\n"
 278  	} else {
 279  		trimRunes = "\t\r\b\n "
 280  	}
 281  }
 282  
 283  // 25jun16: Allow user to specify the "prefix" character for XML attribute key labels.
 284  // We do this by replacing '`' constant with attrPrefix var, replacing useHyphen with attrPrefix = "",
 285  // and adding a SetAttrPrefix(s string) function.
 286  
 287  var attrPrefix string = `-` // the default
 288  var lenAttrPrefix int = 1   // the default
 289  
 290  // SetAttrPrefix changes the default, "-", to the specified value, s.
 291  // SetAttrPrefix("") is the same as PrependAttrWithHyphen(false).
 292  // (Not applicable for NewMapXmlSeq(), mv.XmlSeq(), etc.)
 293  func SetAttrPrefix(s string) {
 294  	attrPrefix = s
 295  	lenAttrPrefix = len(attrPrefix)
 296  }
 297  
 298  // 18jan17: Allows user to specify if the map keys should be in snake case instead
 299  // of the default hyphenated notation.
 300  var snakeCaseKeys bool
 301  
 302  // CoerceKeysToSnakeCase changes the default, false, to the specified value, b.
 303  // Note: the attribute prefix will be a hyphen, '-', or what ever string value has
 304  // been specified using SetAttrPrefix.
 305  func CoerceKeysToSnakeCase(b ...bool) {
 306  	if len(b) == 0 {
 307  		snakeCaseKeys = !snakeCaseKeys
 308  	} else if len(b) == 1 {
 309  		snakeCaseKeys = b[0]
 310  	}
 311  }
 312  
 313  // 10jan19: use of pull request #57 should be conditional - legacy code assumes
 314  // numeric values are float64.
 315  var castToInt bool
 316  
 317  // CastValuesToInt tries to coerce numeric valus to int64 or uint64 instead of the
 318  // default float64. Repeated calls with no argument will toggle this on/off, or this
 319  // handling will be set with the value of 'b'.
 320  func CastValuesToInt(b ...bool) {
 321  	if len(b) == 0 {
 322  		castToInt = !castToInt
 323  	} else if len(b) == 1 {
 324  		castToInt = b[0]
 325  	}
 326  }
 327  
 328  // 05feb17: support processing XMPP streams (issue #36)
 329  var handleXMPPStreamTag bool
 330  
 331  // HandleXMPPStreamTag causes decoder to parse XMPP <stream:stream> elements.
 332  // If called with no argument, XMPP stream element handling is toggled on/off.
 333  // (See xmppStream_test.go for example.)
 334  //	If called with NewMapXml, NewMapXmlReader, New MapXmlReaderRaw the "stream"
 335  //	element will be  returned as:
 336  //		map["stream"]interface{}{map[-<attrs>]interface{}}.
 337  //	If called with NewMapSeq, NewMapSeqReader, NewMapSeqReaderRaw the "stream"
 338  //	element will be returned as:
 339  //		map["stream:stream"]interface{}{map["#attr"]interface{}{map[string]interface{}}}
 340  //		where the "#attr" values have "#text" and "#seq" keys. (See NewMapXmlSeq.)
 341  func HandleXMPPStreamTag(b ...bool) {
 342  	if len(b) == 0 {
 343  		handleXMPPStreamTag = !handleXMPPStreamTag
 344  	} else if len(b) == 1 {
 345  		handleXMPPStreamTag = b[0]
 346  	}
 347  }
 348  
 349  // 21jan18 - decode all values as map["#text":value] (issue #56)
 350  var decodeSimpleValuesAsMap bool
 351  
 352  // DecodeSimpleValuesAsMap forces all values to be decoded as map["#text":<value>].
 353  // If called with no argument, the decoding is toggled on/off.
 354  //
 355  // By default the NewMapXml functions decode simple values without attributes as
 356  // map[<tag>:<value>]. This function causes simple values without attributes to be
 357  // decoded the same as simple values with attributes - map[<tag>:map["#text":<value>]].
 358  func DecodeSimpleValuesAsMap(b ...bool) {
 359  	if len(b) == 0 {
 360  		decodeSimpleValuesAsMap = !decodeSimpleValuesAsMap
 361  	} else if len(b) == 1 {
 362  		decodeSimpleValuesAsMap = b[0]
 363  	}
 364  }
 365  
 366  // xmlToMapParser (2015.11.12) - load a 'clean' XML doc into a map[string]interface{} directly.
 367  // A refactoring of xmlToTreeParser(), markDuplicate() and treeToMap() - here, all-in-one.
 368  // We've removed the intermediate *node tree with the allocation and subsequent rescanning.
 369  func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
 370  	if lowerCase {
 371  		skey = strings.ToLower(skey)
 372  	}
 373  	if snakeCaseKeys {
 374  		skey = strings.Replace(skey, "-", "_", -1)
 375  	}
 376  
 377  	// NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'.
 378  	// Unless 'skey' is a simple element w/o attributes, in which case the xml.CharData value is the value.
 379  	var n, na map[string]interface{}
 380  	var seq int // for includeTagSeqNum
 381  
 382  	// Allocate maps and load attributes, if any.
 383  	// NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through
 384  	//       to get StartElement then recurse with skey==xml.StartElement.Name.Local
 385  	//       where we begin allocating map[string]interface{} values 'n' and 'na'.
 386  	if skey != "" {
 387  		n = make(map[string]interface{})  // old n
 388  		na = make(map[string]interface{}) // old n.nodes
 389  		if len(a) > 0 {
 390  			for _, v := range a {
 391  				if snakeCaseKeys {
 392  					v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
 393  				}
 394  				var key string
 395  				key = attrPrefix + v.Name.Local
 396  				if lowerCase {
 397  					key = strings.ToLower(key)
 398  				}
 399  				if xmlEscapeCharsDecoder { // per issue#84
 400  					v.Value = escapeChars(v.Value)
 401  				}
 402  				na[key] = cast(v.Value, r, key)
 403  			}
 404  		}
 405  	}
 406  	// Return XMPP <stream:stream> message.
 407  	if handleXMPPStreamTag && skey == "stream" {
 408  		n[skey] = na
 409  		return n, nil
 410  	}
 411  
 412  	for {
 413  		t, err := p.Token()
 414  		if err != nil {
 415  			if err != io.EOF {
 416  				return nil, errors.New("xml.Decoder.Token() - " + err.Error())
 417  			}
 418  			return nil, err
 419  		}
 420  		switch t.(type) {
 421  		case xml.StartElement:
 422  			tt := t.(xml.StartElement)
 423  
 424  			// First call to xmlToMapParser() doesn't pass xml.StartElement - the map key.
 425  			// So when the loop is first entered, the first token is the root tag along
 426  			// with any attributes, which we process here.
 427  			//
 428  			// Subsequent calls to xmlToMapParser() will pass in tag+attributes for
 429  			// processing before getting the next token which is the element value,
 430  			// which is done above.
 431  			if skey == "" {
 432  				return xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
 433  			}
 434  
 435  			// If not initializing the map, parse the element.
 436  			// len(nn) == 1, necessarily - it is just an 'n'.
 437  			nn, err := xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
 438  			if err != nil {
 439  				return nil, err
 440  			}
 441  
 442  			// The nn map[string]interface{} value is a na[nn_key] value.
 443  			// We need to see if nn_key already exists - means we're parsing a list.
 444  			// This may require converting na[nn_key] value into []interface{} type.
 445  			// First, extract the key:val for the map - it's a singleton.
 446  			// Note:
 447  			// * if CoerceKeysToLower() called, then key will be lower case.
 448  			// * if CoerceKeysToSnakeCase() called, then key will be converted to snake case.
 449  			var key string
 450  			var val interface{}
 451  			for key, val = range nn {
 452  				break
 453  			}
 454  
 455  			// IncludeTagSeqNum requests that the element be augmented with a "_seq" sub-element.
 456  			// In theory, we don't need this if len(na) == 1. But, we don't know what might
 457  			// come next - we're only parsing forward.  So if you ask for 'includeTagSeqNum' you
 458  			// get it on every element. (Personally, I never liked this, but I added it on request
 459  			// and did get a $50 Amazon gift card in return - now we support it for backwards compatibility!)
 460  			if includeTagSeqNum {
 461  				switch val.(type) {
 462  				case []interface{}:
 463  					// noop - There's no clean way to handle this w/o changing message structure.
 464  				case map[string]interface{}:
 465  					val.(map[string]interface{})["_seq"] = seq // will overwrite an "_seq" XML tag
 466  					seq++
 467  				case interface{}: // a non-nil simple element: string, float64, bool
 468  					v := map[string]interface{}{textK: val}
 469  					v["_seq"] = seq
 470  					seq++
 471  					val = v
 472  				}
 473  			}
 474  
 475  			// 'na' holding sub-elements of n.
 476  			// See if 'key' already exists.
 477  			// If 'key' exists, then this is a list, if not just add key:val to na.
 478  			if v, ok := na[key]; ok {
 479  				var a []interface{}
 480  				switch v.(type) {
 481  				case []interface{}:
 482  					a = v.([]interface{})
 483  				default: // anything else - note: v.(type) != nil
 484  					a = []interface{}{v}
 485  				}
 486  				a = append(a, val)
 487  				na[key] = a
 488  			} else {
 489  				na[key] = val // save it as a singleton
 490  			}
 491  		case xml.EndElement:
 492  			// len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
 493  			if len(n) == 0 {
 494  				// If len(na)==0 we have an empty element == "";
 495  				// it has no xml.Attr nor xml.CharData.
 496  				// Note: in original node-tree parser, val defaulted to "";
 497  				// so we always had the default if len(node.nodes) == 0.
 498  				if len(na) > 0 {
 499  					n[skey] = na
 500  				} else {
 501  					n[skey] = "" // empty element
 502  				}
 503  			} else if len(n) == 1 && len(na) > 0 {
 504  				// it's a simple element w/ no attributes w/ subelements
 505  				for _, v := range n {
 506  					na[textK] = v
 507  				}
 508  				n[skey] = na
 509  			}
 510  			return n, nil
 511  		case xml.CharData:
 512  			// clean up possible noise
 513  			tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
 514  			if xmlEscapeCharsDecoder { // issue#84
 515  				tt = escapeChars(tt)
 516  			}
 517  			if len(tt) > 0 {
 518  				if len(na) > 0 || decodeSimpleValuesAsMap {
 519  					na[textK] = cast(tt, r, textK)
 520  				} else if skey != "" {
 521  					n[skey] = cast(tt, r, skey)
 522  				} else {
 523  					// per Adrian (http://www.adrianlungu.com/) catch stray text
 524  					// in decoder stream -
 525  					// https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
 526  					// NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
 527  					// a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
 528  					continue
 529  				}
 530  			}
 531  		default:
 532  			// noop
 533  		}
 534  	}
 535  }
 536  
 537  var castNanInf bool
 538  
 539  // Cast "Nan", "Inf", "-Inf" XML values to 'float64'.
 540  // By default, these values will be decoded as 'string'.
 541  func CastNanInf(b ...bool) {
 542  	if len(b) == 0 {
 543  		castNanInf = !castNanInf
 544  	} else if len(b) == 1 {
 545  		castNanInf = b[0]
 546  	}
 547  }
 548  
 549  // cast - try to cast string values to bool or float64
 550  // 't' is the tag key that can be checked for 'not-casting'
 551  func cast(s string, r bool, t string) interface{} {
 552  	if checkTagToSkip != nil && t != "" && checkTagToSkip(t) {
 553  		// call the check-function here with 't[0]'
 554  		// if 'true' return s
 555  		return s
 556  	}
 557  
 558  	if r {
 559  		// handle nan and inf
 560  		if !castNanInf {
 561  			switch strings.ToLower(s) {
 562  			case "nan", "inf", "-inf":
 563  				return s
 564  			}
 565  		}
 566  
 567  		// handle numeric strings ahead of boolean
 568  		if castToInt {
 569  			if f, err := strconv.ParseInt(s, 10, 64); err == nil {
 570  				return f
 571  			}
 572  			if f, err := strconv.ParseUint(s, 10, 64); err == nil {
 573  				return f
 574  			}
 575  		}
 576  
 577  		if castToFloat {
 578  			if f, err := strconv.ParseFloat(s, 64); err == nil {
 579  				return f
 580  			}
 581  		}
 582  
 583  		// ParseBool treats "1"==true & "0"==false, we've already scanned those
 584  		// values as float64. See if value has 't' or 'f' as initial screen to
 585  		// minimize calls to ParseBool; also, see if len(s) < 6.
 586  		if castToBool {
 587  			if len(s) > 0 && len(s) < 6 {
 588  				switch s[:1] {
 589  				case "t", "T", "f", "F":
 590  					if b, err := strconv.ParseBool(s); err == nil {
 591  						return b
 592  					}
 593  				}
 594  			}
 595  		}
 596  	}
 597  	return s
 598  }
 599  
 600  // pull request, #59
 601  var castToFloat = true
 602  
 603  // CastValuesToFloat can be used to skip casting to float64 when
 604  // "cast" argument is 'true' in NewMapXml, etc.
 605  // Default is true.
 606  func CastValuesToFloat(b ...bool) {
 607  	if len(b) == 0 {
 608  		castToFloat = !castToFloat
 609  	} else if len(b) == 1 {
 610  		castToFloat = b[0]
 611  	}
 612  }
 613  
 614  var castToBool = true
 615  
 616  // CastValuesToBool can be used to skip casting to bool when
 617  // "cast" argument is 'true' in NewMapXml, etc.
 618  // Default is true.
 619  func CastValuesToBool(b ...bool) {
 620  	if len(b) == 0 {
 621  		castToBool = !castToBool
 622  	} else if len(b) == 1 {
 623  		castToBool = b[0]
 624  	}
 625  }
 626  
 627  // checkTagToSkip - switch to address Issue #58
 628  
 629  var checkTagToSkip func(string) bool
 630  
 631  // SetCheckTagToSkipFunc registers function to test whether the value
 632  // for a tag should be cast to bool or float64 when "cast" argument is 'true'.
 633  // (Dot tag path notation is not supported.)
 634  // NOTE: key may be "#text" if it's a simple element with attributes
 635  //       or "decodeSimpleValuesAsMap == true".
 636  // NOTE: does not apply to NewMapXmlSeq... functions.
 637  func SetCheckTagToSkipFunc(fn func(string) bool) {
 638  	checkTagToSkip = fn
 639  }
 640  
 641  // ------------------ END: NewMapXml & NewMapXmlReader -------------------------
 642  
 643  // ------------------ mv.Xml & mv.XmlWriter - from j2x ------------------------
 644  
 645  const (
 646  	DefaultRootTag = "doc"
 647  )
 648  
 649  var useGoXmlEmptyElemSyntax bool
 650  
 651  // XmlGoEmptyElemSyntax() - <tag ...></tag> rather than <tag .../>.
 652  //	Go's encoding/xml package marshals empty XML elements as <tag ...></tag>.  By default this package
 653  //	encodes empty elements as <tag .../>.  If you're marshaling Map values that include structures
 654  //	(which are passed to xml.Marshal for encoding), this will let you conform to the standard package.
 655  func XmlGoEmptyElemSyntax() {
 656  	useGoXmlEmptyElemSyntax = true
 657  }
 658  
 659  // XmlDefaultEmptyElemSyntax() - <tag .../> rather than <tag ...></tag>.
 660  // Return XML encoding for empty elements to the default package setting.
 661  // Reverses effect of XmlGoEmptyElemSyntax().
 662  func XmlDefaultEmptyElemSyntax() {
 663  	useGoXmlEmptyElemSyntax = false
 664  }
 665  
 666  // ------- issue #88 ----------
 667  // xmlCheckIsValid set switch to force decoding the encoded XML to
 668  // see if it is valid XML.
 669  var xmlCheckIsValid bool
 670  
 671  // XmlCheckIsValid forces the encoded XML to be checked for validity.
 672  func XmlCheckIsValid(b ...bool) {
 673  	if len(b) == 1 {
 674  		xmlCheckIsValid = b[0]
 675  		return
 676  	}
 677  	xmlCheckIsValid = !xmlCheckIsValid
 678  }
 679  
 680  // Encode a Map as XML.  The companion of NewMapXml().
 681  // The following rules apply.
 682  //    - The key label "#text" is treated as the value for a simple element with attributes.
 683  //    - Map keys that begin with a hyphen, '-', are interpreted as attributes.
 684  //      It is an error if the attribute doesn't have a []byte, string, number, or boolean value.
 685  //    - Map value type encoding:
 686  //          > string, bool, float64, int, int32, int64, float32: per "%v" formating
 687  //          > []bool, []uint8: by casting to string
 688  //          > structures, etc.: handed to xml.Marshal() - if there is an error, the element
 689  //            value is "UNKNOWN"
 690  //    - Elements with only attribute values or are null are terminated using "/>".
 691  //    - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
 692  //      Thus, `{ "key":"value" }` encodes as "<key>value</key>".
 693  //    - To encode empty elements in a syntax consistent with encoding/xml call UseGoXmlEmptyElementSyntax().
 694  // The attributes tag=value pairs are alphabetized by "tag".  Also, when encoding map[string]interface{} values -
 695  // complex elements, etc. - the key:value pairs are alphabetized by key so the resulting tags will appear sorted.
 696  func (mv Map) Xml(rootTag ...string) ([]byte, error) {
 697  	m := map[string]interface{}(mv)
 698  	var err error
 699  	b := new(bytes.Buffer)
 700  	p := new(pretty) // just a stub
 701  
 702  	if len(m) == 1 && len(rootTag) == 0 {
 703  		for key, value := range m {
 704  			// if it an array, see if all values are map[string]interface{}
 705  			// we force a new root tag if we'll end up with no key:value in the list
 706  			// so: key:[string_val, bool:true] --> <doc><key>string_val</key><bool>true</bool></doc>
 707  			switch value.(type) {
 708  			case []interface{}:
 709  				for _, v := range value.([]interface{}) {
 710  					switch v.(type) {
 711  					case map[string]interface{}: // noop
 712  					default: // anything else
 713  						err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
 714  						goto done
 715  					}
 716  				}
 717  			}
 718  			err = marshalMapToXmlIndent(false, b, key, value, p)
 719  		}
 720  	} else if len(rootTag) == 1 {
 721  		err = marshalMapToXmlIndent(false, b, rootTag[0], m, p)
 722  	} else {
 723  		err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
 724  	}
 725  done:
 726  	if xmlCheckIsValid {
 727  		d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
 728  		for {
 729  			_, err = d.Token()
 730  			if err == io.EOF {
 731  				err = nil
 732  				break
 733  			} else if err != nil {
 734  				return nil, err
 735  			}
 736  		}
 737  	}
 738  	return b.Bytes(), err
 739  }
 740  
 741  // The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
 742  // The names will also provide a key for the number of return arguments.
 743  
 744  // Writes the Map as  XML on the Writer.
 745  // See Xml() for encoding rules.
 746  func (mv Map) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
 747  	x, err := mv.Xml(rootTag...)
 748  	if err != nil {
 749  		return err
 750  	}
 751  
 752  	_, err = xmlWriter.Write(x)
 753  	return err
 754  }
 755  
 756  // Writes the Map as  XML on the Writer. []byte is the raw XML that was written.
 757  // See Xml() for encoding rules.
 758  /*
 759  func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
 760  	x, err := mv.Xml(rootTag...)
 761  	if err != nil {
 762  		return x, err
 763  	}
 764  
 765  	_, err = xmlWriter.Write(x)
 766  	return x, err
 767  }
 768  */
 769  
 770  // Writes the Map as pretty XML on the Writer.
 771  // See Xml() for encoding rules.
 772  func (mv Map) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
 773  	x, err := mv.XmlIndent(prefix, indent, rootTag...)
 774  	if err != nil {
 775  		return err
 776  	}
 777  
 778  	_, err = xmlWriter.Write(x)
 779  	return err
 780  }
 781  
 782  // Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
 783  // See Xml() for encoding rules.
 784  /*
 785  func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
 786  	x, err := mv.XmlIndent(prefix, indent, rootTag...)
 787  	if err != nil {
 788  		return x, err
 789  	}
 790  
 791  	_, err = xmlWriter.Write(x)
 792  	return x, err
 793  }
 794  */
 795  
 796  // -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
 797  
 798  // --------------  Handle XML stream by processing Map value --------------------
 799  
 800  // Default poll delay to keep Handler from spinning on an open stream
 801  // like sitting on os.Stdin waiting for imput.
 802  var xhandlerPollInterval = time.Millisecond
 803  
 804  // Bulk process XML using handlers that process a Map value.
 805  //	'rdr' is an io.Reader for XML (stream)
 806  //	'mapHandler' is the Map processor. Return of 'false' stops io.Reader processing.
 807  //	'errHandler' is the error processor. Return of 'false' stops io.Reader processing and returns the error.
 808  //	Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
 809  //	      This means that you can stop reading the file on error or after processing a particular message.
 810  //	      To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
 811  func HandleXmlReader(xmlReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
 812  	var n int
 813  	for {
 814  		m, merr := NewMapXmlReader(xmlReader)
 815  		n++
 816  
 817  		// handle error condition with errhandler
 818  		if merr != nil && merr != io.EOF {
 819  			merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
 820  			if ok := errHandler(merr); !ok {
 821  				// caused reader termination
 822  				return merr
 823  			}
 824  			continue
 825  		}
 826  
 827  		// pass to maphandler
 828  		if len(m) != 0 {
 829  			if ok := mapHandler(m); !ok {
 830  				break
 831  			}
 832  		} else if merr != io.EOF {
 833  			time.Sleep(xhandlerPollInterval)
 834  		}
 835  
 836  		if merr == io.EOF {
 837  			break
 838  		}
 839  	}
 840  	return nil
 841  }
 842  
 843  // Bulk process XML using handlers that process a Map value and the raw XML.
 844  //	'rdr' is an io.Reader for XML (stream)
 845  //	'mapHandler' is the Map and raw XML - []byte - processor. Return of 'false' stops io.Reader processing.
 846  //	'errHandler' is the error and raw XML processor. Return of 'false' stops io.Reader processing and returns the error.
 847  //	Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
 848  //	      This means that you can stop reading the file on error or after processing a particular message.
 849  //	      To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
 850  //	See NewMapXmlReaderRaw for comment on performance associated with retrieving raw XML from a Reader.
 851  func HandleXmlReaderRaw(xmlReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
 852  	var n int
 853  	for {
 854  		m, raw, merr := NewMapXmlReaderRaw(xmlReader)
 855  		n++
 856  
 857  		// handle error condition with errhandler
 858  		if merr != nil && merr != io.EOF {
 859  			merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
 860  			if ok := errHandler(merr, raw); !ok {
 861  				// caused reader termination
 862  				return merr
 863  			}
 864  			continue
 865  		}
 866  
 867  		// pass to maphandler
 868  		if len(m) != 0 {
 869  			if ok := mapHandler(m, raw); !ok {
 870  				break
 871  			}
 872  		} else if merr != io.EOF {
 873  			time.Sleep(xhandlerPollInterval)
 874  		}
 875  
 876  		if merr == io.EOF {
 877  			break
 878  		}
 879  	}
 880  	return nil
 881  }
 882  
 883  // ----------------- END: Handle XML stream by processing Map value --------------
 884  
 885  // --------  a hack of io.TeeReader ... need one that's an io.ByteReader for xml.NewDecoder() ----------
 886  
 887  // This is a clone of io.TeeReader with the additional method t.ReadByte().
 888  // Thus, this TeeReader is also an io.ByteReader.
 889  // This is necessary because xml.NewDecoder uses a ByteReader not a Reader. It appears to have been written
 890  // with bufio.Reader or bytes.Reader in mind ... not a generic io.Reader, which doesn't have to have ReadByte()..
 891  // If NewDecoder is passed a Reader that does not satisfy ByteReader() it wraps the Reader with
 892  // bufio.NewReader and uses ReadByte rather than Read that runs the TeeReader pipe logic.
 893  
 894  type teeReader struct {
 895  	r io.Reader
 896  	w io.Writer
 897  	b []byte
 898  }
 899  
 900  func myTeeReader(r io.Reader, w io.Writer) io.Reader {
 901  	b := make([]byte, 1)
 902  	return &teeReader{r, w, b}
 903  }
 904  
 905  // need for io.Reader - but we don't use it ...
 906  func (t *teeReader) Read(p []byte) (int, error) {
 907  	return 0, nil
 908  }
 909  
 910  func (t *teeReader) ReadByte() (byte, error) {
 911  	n, err := t.r.Read(t.b)
 912  	if n > 0 {
 913  		if _, err := t.w.Write(t.b[:1]); err != nil {
 914  			return t.b[0], err
 915  		}
 916  	}
 917  	return t.b[0], err
 918  }
 919  
 920  // For use with NewMapXmlReader & NewMapXmlSeqReader.
 921  type byteReader struct {
 922  	r io.Reader
 923  	b []byte
 924  }
 925  
 926  func myByteReader(r io.Reader) io.Reader {
 927  	b := make([]byte, 1)
 928  	return &byteReader{r, b}
 929  }
 930  
 931  // Need for io.Reader interface ...
 932  // Needed if reading a malformed http.Request.Body - issue #38.
 933  func (b *byteReader) Read(p []byte) (int, error) {
 934  	return b.r.Read(p)
 935  }
 936  
 937  func (b *byteReader) ReadByte() (byte, error) {
 938  	_, err := b.r.Read(b.b)
 939  	if len(b.b) > 0 {
 940  		// issue #38
 941  		return b.b[0], err
 942  	}
 943  	var c byte
 944  	return c, err
 945  }
 946  
 947  // ----------------------- END: io.TeeReader hack -----------------------------------
 948  
 949  // ---------------------- XmlIndent - from j2x package ----------------------------
 950  
 951  // Encode a map[string]interface{} as a pretty XML string.
 952  // See Xml for encoding rules.
 953  func (mv Map) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
 954  	m := map[string]interface{}(mv)
 955  
 956  	var err error
 957  	b := new(bytes.Buffer)
 958  	p := new(pretty)
 959  	p.indent = indent
 960  	p.padding = prefix
 961  
 962  	if len(m) == 1 && len(rootTag) == 0 {
 963  		// this can extract the key for the single map element
 964  		// use it if it isn't a key for a list
 965  		for key, value := range m {
 966  			if _, ok := value.([]interface{}); ok {
 967  				err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
 968  			} else {
 969  				err = marshalMapToXmlIndent(true, b, key, value, p)
 970  			}
 971  		}
 972  	} else if len(rootTag) == 1 {
 973  		err = marshalMapToXmlIndent(true, b, rootTag[0], m, p)
 974  	} else {
 975  		err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
 976  	}
 977  	if xmlCheckIsValid {
 978  		d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
 979  		for {
 980  			_, err = d.Token()
 981  			if err == io.EOF {
 982  				err = nil
 983  				break
 984  			} else if err != nil {
 985  				return nil, err
 986  			}
 987  		}
 988  	}
 989  	return b.Bytes(), err
 990  }
 991  
 992  type pretty struct {
 993  	indent   string
 994  	cnt      int
 995  	padding  string
 996  	mapDepth int
 997  	start    int
 998  }
 999  
1000  func (p *pretty) Indent() {
1001  	p.padding += p.indent
1002  	p.cnt++
1003  }
1004  
1005  func (p *pretty) Outdent() {
1006  	if p.cnt > 0 {
1007  		p.padding = p.padding[:len(p.padding)-len(p.indent)]
1008  		p.cnt--
1009  	}
1010  }
1011  
1012  // where the work actually happens
1013  // returns an error if an attribute is not atomic
1014  // NOTE: 01may20 - replaces mapToXmlIndent(); uses bytes.Buffer instead for string appends.
1015  func marshalMapToXmlIndent(doIndent bool, b *bytes.Buffer, key string, value interface{}, pp *pretty) error {
1016  	var err error
1017  	var endTag bool
1018  	var isSimple bool
1019  	var elen int
1020  	p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
1021  
1022  	// per issue #48, 18apr18 - try and coerce maps to map[string]interface{}
1023  	// Don't need for mapToXmlSeqIndent, since maps there are decoded by NewMapXmlSeq().
1024  	if reflect.ValueOf(value).Kind() == reflect.Map {
1025  		switch value.(type) {
1026  		case map[string]interface{}:
1027  		default:
1028  			val := make(map[string]interface{})
1029  			vv := reflect.ValueOf(value)
1030  			keys := vv.MapKeys()
1031  			for _, k := range keys {
1032  				val[fmt.Sprint(k)] = vv.MapIndex(k).Interface()
1033  			}
1034  			value = val
1035  		}
1036  	}
1037  
1038  	// 14jul20.  The following block of code has become something of a catch all for odd stuff
1039  	// that might be passed in as a result of casting an arbitrary map[<T>]<T> to an mxj.Map
1040  	// value and then call m.Xml or m.XmlIndent. See issue #71 (and #73) for such edge cases.
1041  	switch value.(type) {
1042  	// these types are handled during encoding
1043  	case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32, json.Number:
1044  	case []map[string]interface{}, []string, []float64, []bool, []int, []int32, []int64, []float32, []json.Number:
1045  	case []interface{}:
1046  	case nil:
1047  		value = ""
1048  	default:
1049  		// see if value is a struct, if so marshal using encoding/xml package
1050  		if reflect.ValueOf(value).Kind() == reflect.Struct {
1051  			if v, err := xml.Marshal(value); err != nil {
1052  				return err
1053  			} else {
1054  				value = string(v)
1055  			}
1056  		} else {
1057  			// coerce eveything else into a string value
1058  			value = fmt.Sprint(value)
1059  		}
1060  	}
1061  
1062  	// start the XML tag with required indentaton and padding
1063  	if doIndent {
1064  		switch value.(type) {
1065  		case []interface{}, []string:
1066  			// list processing handles indentation for all elements
1067  		default:
1068  			if _, err = b.WriteString(p.padding); err != nil {
1069  				return err
1070  			}
1071  		}
1072  	}
1073  	switch value.(type) {
1074  	case []interface{}:
1075  	default:
1076  		if _, err = b.WriteString(`<` + key); err != nil {
1077  			return err
1078  		}
1079  	}
1080  
1081  	switch value.(type) {
1082  	case map[string]interface{}:
1083  		vv := value.(map[string]interface{})
1084  		lenvv := len(vv)
1085  		// scan out attributes - attribute keys have prepended attrPrefix
1086  		attrlist := make([][2]string, len(vv))
1087  		var n int
1088  		var ss string
1089  		for k, v := range vv {
1090  			if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
1091  				switch v.(type) {
1092  				case string:
1093  					if xmlEscapeChars {
1094  						ss = escapeChars(v.(string))
1095  					} else {
1096  						ss = v.(string)
1097  					}
1098  					attrlist[n][0] = k[lenAttrPrefix:]
1099  					attrlist[n][1] = ss
1100  				case float64, bool, int, int32, int64, float32, json.Number:
1101  					attrlist[n][0] = k[lenAttrPrefix:]
1102  					attrlist[n][1] = fmt.Sprintf("%v", v)
1103  				case []byte:
1104  					if xmlEscapeChars {
1105  						ss = escapeChars(string(v.([]byte)))
1106  					} else {
1107  						ss = string(v.([]byte))
1108  					}
1109  					attrlist[n][0] = k[lenAttrPrefix:]
1110  					attrlist[n][1] = ss
1111  				default:
1112  					return fmt.Errorf("invalid attribute value for: %s:<%T>", k, v)
1113  				}
1114  				n++
1115  			}
1116  		}
1117  		if n > 0 {
1118  			attrlist = attrlist[:n]
1119  			sort.Sort(attrList(attrlist))
1120  			for _, v := range attrlist {
1121  				if _, err = b.WriteString(` ` + v[0] + `="` + v[1] + `"`); err != nil {
1122  					return err
1123  				}
1124  			}
1125  		}
1126  		// only attributes?
1127  		if n == lenvv {
1128  			if useGoXmlEmptyElemSyntax {
1129  				if _, err = b.WriteString(`</` + key + ">"); err != nil {
1130  					return err
1131  				}
1132  			} else {
1133  				if _, err = b.WriteString(`/>`); err != nil {
1134  					return err
1135  				}
1136  			}
1137  			break
1138  		}
1139  
1140  		// simple element? Note: '#text" is an invalid XML tag.
1141  		isComplex := false
1142  		if v, ok := vv[textK]; ok && n+1 == lenvv {
1143  			// just the value and attributes
1144  			switch v.(type) {
1145  			case string:
1146  				if xmlEscapeChars {
1147  					v = escapeChars(v.(string))
1148  				} else {
1149  					v = v.(string)
1150  				}
1151  			case []byte:
1152  				if xmlEscapeChars {
1153  					v = escapeChars(string(v.([]byte)))
1154  				} else {
1155  					v = string(v.([]byte))
1156  				}
1157  			}
1158  			if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
1159  				return err
1160  			}
1161  			endTag = true
1162  			elen = 1
1163  			isSimple = true
1164  			break
1165  		} else if ok {
1166  			// need to handle when there are subelements in addition to the simple element value
1167  			// issue #90
1168  			switch v.(type) {
1169  			case string:
1170  				if xmlEscapeChars {
1171  					v = escapeChars(v.(string))
1172  				} else {
1173  					v = v.(string)
1174  				}
1175  			case []byte:
1176  				if xmlEscapeChars {
1177  					v = escapeChars(string(v.([]byte)))
1178  				} else {
1179  					v = string(v.([]byte))
1180  				}
1181  			}
1182  			if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
1183  				return err
1184  			}
1185  			isComplex = true
1186  		}
1187  
1188  		// close tag with possible attributes
1189  		if !isComplex {
1190  			if _, err = b.WriteString(">"); err != nil {
1191  				return err
1192  			}
1193  		}
1194  		if doIndent {
1195  			// *s += "\n"
1196  			if _, err = b.WriteString("\n"); err != nil {
1197  				return err
1198  			}
1199  		}
1200  		// something more complex
1201  		p.mapDepth++
1202  		// extract the map k:v pairs and sort on key
1203  		elemlist := make([][2]interface{}, len(vv))
1204  		n = 0
1205  		for k, v := range vv {
1206  			if k == textK {
1207  				// simple element handled above
1208  				continue
1209  			}
1210  			if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
1211  				continue
1212  			}
1213  			elemlist[n][0] = k
1214  			elemlist[n][1] = v
1215  			n++
1216  		}
1217  		elemlist = elemlist[:n]
1218  		sort.Sort(elemList(elemlist))
1219  		var i int
1220  		for _, v := range elemlist {
1221  			switch v[1].(type) {
1222  			case []interface{}:
1223  			default:
1224  				if i == 0 && doIndent {
1225  					p.Indent()
1226  				}
1227  			}
1228  			i++
1229  			if err := marshalMapToXmlIndent(doIndent, b, v[0].(string), v[1], p); err != nil {
1230  				return err
1231  			}
1232  			switch v[1].(type) {
1233  			case []interface{}: // handled in []interface{} case
1234  			default:
1235  				if doIndent {
1236  					p.Outdent()
1237  				}
1238  			}
1239  			i--
1240  		}
1241  		p.mapDepth--
1242  		endTag = true
1243  		elen = 1 // we do have some content ...
1244  	case []interface{}:
1245  		// special case - found during implementing Issue #23
1246  		if len(value.([]interface{})) == 0 {
1247  			if doIndent {
1248  				if _, err = b.WriteString(p.padding + p.indent); err != nil {
1249  					return err
1250  				}
1251  			}
1252  			if _, err = b.WriteString("<" + key); err != nil {
1253  				return err
1254  			}
1255  			elen = 0
1256  			endTag = true
1257  			break
1258  		}
1259  		for _, v := range value.([]interface{}) {
1260  			if doIndent {
1261  				p.Indent()
1262  			}
1263  			if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
1264  				return err
1265  			}
1266  			if doIndent {
1267  				p.Outdent()
1268  			}
1269  		}
1270  		return nil
1271  	case []string:
1272  		// This was added by https://github.com/slotix ... not a type that
1273  		// would be encountered if mv generated from NewMapXml, NewMapJson.
1274  		// Could be encountered in AnyXml(), so we'll let it stay, though
1275  		// it should be merged with case []interface{}, above.
1276  		//quick fix for []string type
1277  		//[]string should be treated exaclty as []interface{}
1278  		if len(value.([]string)) == 0 {
1279  			if doIndent {
1280  				if _, err = b.WriteString(p.padding + p.indent); err != nil {
1281  					return err
1282  				}
1283  			}
1284  			if _, err = b.WriteString("<" + key); err != nil {
1285  				return err
1286  			}
1287  			elen = 0
1288  			endTag = true
1289  			break
1290  		}
1291  		for _, v := range value.([]string) {
1292  			if doIndent {
1293  				p.Indent()
1294  			}
1295  			if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
1296  				return err
1297  			}
1298  			if doIndent {
1299  				p.Outdent()
1300  			}
1301  		}
1302  		return nil
1303  	case nil:
1304  		// terminate the tag
1305  		if doIndent {
1306  			// *s += p.padding
1307  			if _, err = b.WriteString(p.padding); err != nil {
1308  				return err
1309  			}
1310  		}
1311  		if _, err = b.WriteString("<" + key); err != nil {
1312  			return err
1313  		}
1314  		endTag, isSimple = true, true
1315  		break
1316  	default: // handle anything - even goofy stuff
1317  		elen = 0
1318  		switch value.(type) {
1319  		case string:
1320  			v := value.(string)
1321  			if xmlEscapeChars {
1322  				v = escapeChars(v)
1323  			}
1324  			elen = len(v)
1325  			if elen > 0 {
1326  				// *s += ">" + v
1327  				if _, err = b.WriteString(">" + v); err != nil {
1328  					return err
1329  				}
1330  			}
1331  		case float64, bool, int, int32, int64, float32, json.Number:
1332  			v := fmt.Sprintf("%v", value)
1333  			elen = len(v) // always > 0
1334  			if _, err = b.WriteString(">" + v); err != nil {
1335  				return err
1336  			}
1337  		case []byte: // NOTE: byte is just an alias for uint8
1338  			// similar to how xml.Marshal handles []byte structure members
1339  			v := string(value.([]byte))
1340  			if xmlEscapeChars {
1341  				v = escapeChars(v)
1342  			}
1343  			elen = len(v)
1344  			if elen > 0 {
1345  				// *s += ">" + v
1346  				if _, err = b.WriteString(">" + v); err != nil {
1347  					return err
1348  				}
1349  			}
1350  		default:
1351  			if _, err = b.WriteString(">"); err != nil {
1352  				return err
1353  			}
1354  			var v []byte
1355  			var err error
1356  			if doIndent {
1357  				v, err = xml.MarshalIndent(value, p.padding, p.indent)
1358  			} else {
1359  				v, err = xml.Marshal(value)
1360  			}
1361  			if err != nil {
1362  				if _, err = b.WriteString(">UNKNOWN"); err != nil {
1363  					return err
1364  				}
1365  			} else {
1366  				elen = len(v)
1367  				if elen > 0 {
1368  					if _, err = b.Write(v); err != nil {
1369  						return err
1370  					}
1371  				}
1372  			}
1373  		}
1374  		isSimple = true
1375  		endTag = true
1376  	}
1377  	if endTag {
1378  		if doIndent {
1379  			if !isSimple {
1380  				if _, err = b.WriteString(p.padding); err != nil {
1381  					return err
1382  				}
1383  			}
1384  		}
1385  		if elen > 0 || useGoXmlEmptyElemSyntax {
1386  			if elen == 0 {
1387  				if _, err = b.WriteString(">"); err != nil {
1388  					return err
1389  				}
1390  			}
1391  			if _, err = b.WriteString(`</` + key + ">"); err != nil {
1392  				return err
1393  			}
1394  		} else {
1395  			if _, err = b.WriteString(`/>`); err != nil {
1396  				return err
1397  			}
1398  		}
1399  	}
1400  	if doIndent {
1401  		if p.cnt > p.start {
1402  			if _, err = b.WriteString("\n"); err != nil {
1403  				return err
1404  			}
1405  		}
1406  		p.Outdent()
1407  	}
1408  
1409  	return nil
1410  }
1411  
1412  // ============================ sort interface implementation =================
1413  
1414  type attrList [][2]string
1415  
1416  func (a attrList) Len() int {
1417  	return len(a)
1418  }
1419  
1420  func (a attrList) Swap(i, j int) {
1421  	a[i], a[j] = a[j], a[i]
1422  }
1423  
1424  func (a attrList) Less(i, j int) bool {
1425  	return a[i][0] <= a[j][0]
1426  }
1427  
1428  type elemList [][2]interface{}
1429  
1430  func (e elemList) Len() int {
1431  	return len(e)
1432  }
1433  
1434  func (e elemList) Swap(i, j int) {
1435  	e[i], e[j] = e[j], e[i]
1436  }
1437  
1438  func (e elemList) Less(i, j int) bool {
1439  	return e[i][0].(string) <= e[j][0].(string)
1440  }
1441