func marshalMapToXmlIndent(doIndent bool, b *bytes.Buffer, key string, value interface{}, pp *pretty) error {
	var err error
	var endTag bool
	var isSimple bool
	var elen int
	p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
	if reflect.ValueOf(value).Kind() == reflect.Map {
		switch value.(type) {
		case map[string]interface{}:
		default:
			val := make(map[string]interface{})
			vv := reflect.ValueOf(value)
			keys := vv.MapKeys()
			for _, k := range keys {
				val[fmt.Sprint(k)] = vv.MapIndex(k).Interface()
			}
			value = val
		}
	}
	switch value.(type) {
	case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32, json.Number:
	case []map[string]interface{}, []string, []float64, []bool, []int, []int32, []int64, []float32, []json.Number:
	case []interface{}:
	case nil:
		value = ""
	default:
		if reflect.ValueOf(value).Kind() == reflect.Struct {
			if v, err := xml.Marshal(value); err != nil {
				return err
			} else {
				value = string(v)
			}
		} else {
			value = fmt.Sprint(value)
		}
	}
	if doIndent {
		if _, err = b.WriteString(p.padding); err != nil {
			return err
		}
	}
	switch value.(type) {
	case []interface{}:
	default:
		if _, err = b.WriteString(`<` + key); err != nil {
			return err
		}
	}
	switch value.(type) {
	case map[string]interface{}:
		vv := value.(map[string]interface{})
		lenvv := len(vv)
		attrlist := make([][2]string, len(vv))
		var n int
		var ss string
		for k, v := range vv {
			if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
				switch v.(type) {
				case string:
					if xmlEscapeChars {
						ss = escapeChars(v.(string))
					} else {
						ss = v.(string)
					}
					attrlist[n][0] = k[lenAttrPrefix:]
					attrlist[n][1] = ss
				case float64, bool, int, int32, int64, float32, json.Number:
					attrlist[n][0] = k[lenAttrPrefix:]
					attrlist[n][1] = fmt.Sprintf("%v", v)
				case []byte:
					if xmlEscapeChars {
						ss = escapeChars(string(v.([]byte)))
					} else {
						ss = string(v.([]byte))
					}
					attrlist[n][0] = k[lenAttrPrefix:]
					attrlist[n][1] = ss
				default:
					return fmt.Errorf("invalid attribute value for: %s:<%T>", k, v)
				}
				n++
			}
		}
		if n > 0 {
			attrlist = attrlist[:n]
			sort.Sort(attrList(attrlist))
			for _, v := range attrlist {
				if _, err = b.WriteString(` ` + v[0] + `="` + v[1] + `"`); err != nil {
					return err
				}
			}
		}
		if n == lenvv {
			if useGoXmlEmptyElemSyntax {
				if _, err = b.WriteString(`</` + key + ">"); err != nil {
					return err
				}
			} else {
				if _, err = b.WriteString(`/>`); err != nil {
					return err
				}
			}
			break
		}
		isComplex := false
		if v, ok := vv["#text"]; ok && n+1 == lenvv {
			switch v.(type) {
			case string:
				if xmlEscapeChars {
					v = escapeChars(v.(string))
				} else {
					v = v.(string)
				}
			case []byte:
				if xmlEscapeChars {
					v = escapeChars(string(v.([]byte)))
				} else {
					v = string(v.([]byte))
				}
			}
			if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
				return err
			}
			endTag = true
			elen = 1
			isSimple = true
			break
		} else if ok {
			switch v.(type) {
			case string:
				if xmlEscapeChars {
					v = escapeChars(v.(string))
				} else {
					v = v.(string)
				}
			case []byte:
				if xmlEscapeChars {
					v = escapeChars(string(v.([]byte)))
				} else {
					v = string(v.([]byte))
				}
			}
			if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
				return err
			}
			isComplex = true
		}
		if !isComplex {
			if _, err = b.WriteString(">"); err != nil {
				return err
			}
		}
		if doIndent {
			if _, err = b.WriteString("\n"); err != nil {
				return err
			}
		}
		p.mapDepth++
		elemlist := make([][2]interface{}, len(vv))
		n = 0
		for k, v := range vv {
			if k == "#text" {
				continue
			}
			if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
				continue
			}
			elemlist[n][0] = k
			elemlist[n][1] = v
			n++
		}
		elemlist = elemlist[:n]
		sort.Sort(elemList(elemlist))
		var i int
		for _, v := range elemlist {
			switch v[1].(type) {
			case []interface{}:
			default:
				if i == 0 && doIndent {
					p.Indent()
				}
			}
			i++
			if err := marshalMapToXmlIndent(doIndent, b, v[0].(string), v[1], p); err != nil {
				return err
			}
			switch v[1].(type) {
			case []interface{}:  
			default:
				if doIndent {
					p.Outdent()
				}
			}
			i--
		}
		p.mapDepth--
		endTag = true
		elen = 1  
	case []interface{}:
		if len(value.([]interface{})) == 0 {
			if doIndent {
				if _, err = b.WriteString(p.padding + p.indent); err != nil {
					return err
				}
			}
			if _, err = b.WriteString("<" + key); err != nil {
				return err
			}
			elen = 0
			endTag = true
			break
		}
		for _, v := range value.([]interface{}) {
			if doIndent {
				p.Indent()
			}
			if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
				return err
			}
			if doIndent {
				p.Outdent()
			}
		}
		return nil
	case []string:
		if len(value.([]string)) == 0 {
			if doIndent {
				if _, err = b.WriteString(p.padding + p.indent); err != nil {
					return err
				}
			}
			if _, err = b.WriteString("<" + key); err != nil {
				return err
			}
			elen = 0
			endTag = true
			break
		}
		for _, v := range value.([]string) {
			if doIndent {
				p.Indent()
			}
			if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
				return err
			}
			if doIndent {
				p.Outdent()
			}
		}
		return nil
	case nil:
		if doIndent {
			if _, err = b.WriteString(p.padding); err != nil {
				return err
			}
		}
		if _, err = b.WriteString("<" + key); err != nil {
			return err
		}
		endTag, isSimple = true, true
		break
	default:  
		elen = 0
		switch value.(type) {
		case string:
			v := value.(string)
			if xmlEscapeChars {
				v = escapeChars(v)
			}
			elen = len(v)
			if elen > 0 {
				if _, err = b.WriteString(">" + v); err != nil {
					return err
				}
			}
		case float64, bool, int, int32, int64, float32, json.Number:
			v := fmt.Sprintf("%v", value)
			elen = len(v)  
			if _, err = b.WriteString(">" + v); err != nil {
				return err
			}
		case []byte:  
			v := string(value.([]byte))
			if xmlEscapeChars {
				v = escapeChars(v)
			}
			elen = len(v)
			if elen > 0 {
				if _, err = b.WriteString(">" + v); err != nil {
					return err
				}
			}
		default:
			if _, err = b.WriteString(">"); err != nil {
				return err
			}
			var v []byte
			var err error
			if doIndent {
				v, err = xml.MarshalIndent(value, p.padding, p.indent)
			} else {
				v, err = xml.Marshal(value)
			}
			if err != nil {
				if _, err = b.WriteString(">UNKNOWN"); err != nil {
					return err
				}
			} else {
				elen = len(v)
				if elen > 0 {
					if _, err = b.Write(v); err != nil {
						return err
					}
				}
			}
		}
		isSimple = true
		endTag = true
	}
	if endTag {
		if doIndent {
			if !isSimple {
				if _, err = b.WriteString(p.padding); err != nil {
					return err
				}
			}
		}
		if elen > 0 || useGoXmlEmptyElemSyntax {
			if elen == 0 {
				if _, err = b.WriteString(">"); err != nil {
					return err
				}
			}
			if _, err = b.WriteString(`</` + key + ">"); err != nil {
				return err
			}
		} else {
			if _, err = b.WriteString(`/>`); err != nil {
				return err
			}
		}
	}
	if doIndent {
		if p.cnt > p.start {
			if _, err = b.WriteString("\n"); err != nil {
				return err
			}
		}
		p.Outdent()
	}
	return nil
}
where the work actually happens
returns an error if an attribute is not atomic
NOTE: 01may20 - replaces mapToXmlIndent(); uses bytes.Buffer instead for string appends.
package mxj
import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"
)
var XmlCharsetReader func(charset string, input io.Reader) (io.Reader, error)
func NewMapXml(xmlVal []byte, cast ...bool) (Map, error) {
	var r bool
	if len(cast) == 1 {
		r = cast[0]
	}
	return xmlToMap(xmlVal, r)
}
func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error) {
	var r bool
	if len(cast) == 1 {
		r = cast[0]
	}
	if _, ok := xmlReader.(io.ByteReader); !ok {
		xmlReader = myByteReader(xmlReader)  
	}
	return xmlReaderToMap(xmlReader, r)
}
func NewMapXmlReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
	var r bool
	if len(cast) == 1 {
		r = cast[0]
	}
	buf := make([]byte, 0)
	wb := bytes.NewBuffer(buf)
	trdr := myTeeReader(xmlReader, wb)  
	m, err := xmlReaderToMap(trdr, r)
	b := wb.Bytes()
	if err != nil {
		return nil, b, err
	}
	return m, b, nil
}
func xmlReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
	p := xml.NewDecoder(rdr)
	if CustomDecoder != nil {
		useCustomDecoder(p)
	} else {
		p.CharsetReader = XmlCharsetReader
	}
	return xmlToMapParser("", nil, p, r)
}
func xmlToMap(doc []byte, r bool) (map[string]interface{}, error) {
	b := bytes.NewReader(doc)
	p := xml.NewDecoder(b)
	if CustomDecoder != nil {
		useCustomDecoder(p)
	} else {
		p.CharsetReader = XmlCharsetReader
	}
	return xmlToMapParser("", nil, p, r)
}
func PrependAttrWithHyphen(v bool) {
	if v {
		attrPrefix = "-"
		lenAttrPrefix = len(attrPrefix)
		return
	}
	attrPrefix = ""
	lenAttrPrefix = len(attrPrefix)
}
var includeTagSeqNum bool
func IncludeTagSeqNum(b ...bool) {
	if len(b) == 0 {
		includeTagSeqNum = !includeTagSeqNum
	} else if len(b) == 1 {
		includeTagSeqNum = b[0]
	}
}
var lowerCase bool
func CoerceKeysToLower(b ...bool) {
	if len(b) == 0 {
		lowerCase = !lowerCase
	} else if len(b) == 1 {
		lowerCase = b[0]
	}
}
var disableTrimWhiteSpace bool
var trimRunes = "\t\r\b\n "
func DisableTrimWhiteSpace(b ...bool) {
	if len(b) == 0 {
		disableTrimWhiteSpace = true
	} else {
		disableTrimWhiteSpace = b[0]
	}
	if disableTrimWhiteSpace {
		trimRunes = "\t\r\b\n"
	} else {
		trimRunes = "\t\r\b\n "
	}
}
var attrPrefix string = `-`  
var lenAttrPrefix int = 1    
func SetAttrPrefix(s string) {
	attrPrefix = s
	lenAttrPrefix = len(attrPrefix)
}
var snakeCaseKeys bool
func CoerceKeysToSnakeCase(b ...bool) {
	if len(b) == 0 {
		snakeCaseKeys = !snakeCaseKeys
	} else if len(b) == 1 {
		snakeCaseKeys = b[0]
	}
}
var castToInt bool
func CastValuesToInt(b ...bool) {
	if len(b) == 0 {
		castToInt = !castToInt
	} else if len(b) == 1 {
		castToInt = b[0]
	}
}
var handleXMPPStreamTag bool
func HandleXMPPStreamTag(b ...bool) {
	if len(b) == 0 {
		handleXMPPStreamTag = !handleXMPPStreamTag
	} else if len(b) == 1 {
		handleXMPPStreamTag = b[0]
	}
}
var decodeSimpleValuesAsMap bool
func DecodeSimpleValuesAsMap(b ...bool) {
	if len(b) == 0 {
		decodeSimpleValuesAsMap = !decodeSimpleValuesAsMap
	} else if len(b) == 1 {
		decodeSimpleValuesAsMap = b[0]
	}
}
func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
	if lowerCase {
		skey = strings.ToLower(skey)
	}
	if snakeCaseKeys {
		skey = strings.Replace(skey, "-", "_", -1)
	}
	var n, na map[string]interface{}
	var seq int  
	if skey != "" {
		n = make(map[string]interface{})   
		na = make(map[string]interface{})  
		if len(a) > 0 {
			for _, v := range a {
				if snakeCaseKeys {
					v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
				}
				var key string
				key = attrPrefix + v.Name.Local
				if lowerCase {
					key = strings.ToLower(key)
				}
				if xmlEscapeCharsDecoder {  
					v.Value = escapeChars(v.Value)
				}
				na[key] = cast(v.Value, r, key)
			}
		}
	}
	if handleXMPPStreamTag && skey == "stream" {
		n[skey] = na
		return n, nil
	}
	for {
		t, err := p.Token()
		if err != nil {
			if err != io.EOF {
				return nil, errors.New("xml.Decoder.Token() - " + err.Error())
			}
			return nil, err
		}
		switch t.(type) {
		case xml.StartElement:
			tt := t.(xml.StartElement)
			if skey == "" {
				return xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
			}
			nn, err := xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
			if err != nil {
				return nil, err
			}
			var key string
			var val interface{}
			for key, val = range nn {
				break
			}
			if includeTagSeqNum {
				switch val.(type) {
				case []interface{}:
				case map[string]interface{}:
					val.(map[string]interface{})["_seq"] = seq  
					seq++
				case interface{}:  
					v := map[string]interface{}{"#text": val}
					v["_seq"] = seq
					seq++
					val = v
				}
			}
			if v, ok := na[key]; ok {
				var a []interface{}
				switch v.(type) {
				case []interface{}:
					a = v.([]interface{})
				default:  
					a = []interface{}{v}
				}
				a = append(a, val)
				na[key] = a
			} else {
				na[key] = val  
			}
		case xml.EndElement:
			if len(n) == 0 {
				if len(na) > 0 {
					n[skey] = na
				} else {
					n[skey] = ""  
				}
			} else if len(n) == 1 && len(na) > 0 {
				for _, v := range n {
					na["#text"] = v
				}
				n[skey] = na
			}
			return n, nil
		case xml.CharData:
			tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
			if xmlEscapeCharsDecoder {  
				tt = escapeChars(tt)
			}
			if len(tt) > 0 {
				if len(na) > 0 || decodeSimpleValuesAsMap {
					na["#text"] = cast(tt, r, "#text")
				} else if skey != "" {
					n[skey] = cast(tt, r, skey)
				} else {
					continue
				}
			}
		default:
		}
	}
}
var castNanInf bool
func CastNanInf(b ...bool) {
	if len(b) == 0 {
		castNanInf = !castNanInf
	} else if len(b) == 1 {
		castNanInf = b[0]
	}
}
func cast(s string, r bool, t string) interface{} {
	if checkTagToSkip != nil && t != "" && checkTagToSkip(t) {
		return s
	}
	if r {
		if !castNanInf {
			switch strings.ToLower(s) {
			case "nan", "inf", "-inf":
				return s
			}
		}
		if castToInt {
			if f, err := strconv.ParseInt(s, 10, 64); err == nil {
				return f
			}
			if f, err := strconv.ParseUint(s, 10, 64); err == nil {
				return f
			}
		}
		if castToFloat {
			if f, err := strconv.ParseFloat(s, 64); err == nil {
				return f
			}
		}
		if castToBool {
			if len(s) > 0 && len(s) < 6 {
				switch s[:1] {
				case "t", "T", "f", "F":
					if b, err := strconv.ParseBool(s); err == nil {
						return b
					}
				}
			}
		}
	}
	return s
}
var castToFloat = true
func CastValuesToFloat(b ...bool) {
	if len(b) == 0 {
		castToFloat = !castToFloat
	} else if len(b) == 1 {
		castToFloat = b[0]
	}
}
var castToBool = true
func CastValuesToBool(b ...bool) {
	if len(b) == 0 {
		castToBool = !castToBool
	} else if len(b) == 1 {
		castToBool = b[0]
	}
}
var checkTagToSkip func(string) bool
func SetCheckTagToSkipFunc(fn func(string) bool) {
	checkTagToSkip = fn
}
const (
	DefaultRootTag = "doc"
)
var useGoXmlEmptyElemSyntax bool
func XmlGoEmptyElemSyntax() {
	useGoXmlEmptyElemSyntax = true
}
func XmlDefaultEmptyElemSyntax() {
	useGoXmlEmptyElemSyntax = false
}
var xmlCheckIsValid bool
func XmlCheckIsValid(b ...bool) {
	if len(b) == 1 {
		xmlCheckIsValid = b[0]
		return
	}
	xmlCheckIsValid = !xmlCheckIsValid
}
func (mv Map) Xml(rootTag ...string) ([]byte, error) {
	m := map[string]interface{}(mv)
	var err error
	b := new(bytes.Buffer)
	p := new(pretty)  
	if len(m) == 1 && len(rootTag) == 0 {
		for key, value := range m {
			switch value.(type) {
			case []interface{}:
				for _, v := range value.([]interface{}) {
					switch v.(type) {
					case map[string]interface{}:  
					default:  
						err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
						goto done
					}
				}
			}
			err = marshalMapToXmlIndent(false, b, key, value, p)
		}
	} else if len(rootTag) == 1 {
		err = marshalMapToXmlIndent(false, b, rootTag[0], m, p)
	} else {
		err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
	}
done:
	if xmlCheckIsValid {
		d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
		for {
			_, err = d.Token()
			if err == io.EOF {
				err = nil
				break
			} else if err != nil {
				return nil, err
			}
		}
	}
	return b.Bytes(), err
}
func (mv Map) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
	x, err := mv.Xml(rootTag...)
	if err != nil {
		return err
	}
	_, err = xmlWriter.Write(x)
	return err
}
func (mv Map) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
	x, err := mv.XmlIndent(prefix, indent, rootTag...)
	if err != nil {
		return err
	}
	_, err = xmlWriter.Write(x)
	return err
}
var xhandlerPollInterval = time.Millisecond
func HandleXmlReader(xmlReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
	var n int
	for {
		m, merr := NewMapXmlReader(xmlReader)
		n++
		if merr != nil && merr != io.EOF {
			merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
			if ok := errHandler(merr); !ok {
				return merr
			}
			continue
		}
		if len(m) != 0 {
			if ok := mapHandler(m); !ok {
				break
			}
		} else if merr != io.EOF {
			time.Sleep(xhandlerPollInterval)
		}
		if merr == io.EOF {
			break
		}
	}
	return nil
}
func HandleXmlReaderRaw(xmlReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
	var n int
	for {
		m, raw, merr := NewMapXmlReaderRaw(xmlReader)
		n++
		if merr != nil && merr != io.EOF {
			merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
			if ok := errHandler(merr, raw); !ok {
				return merr
			}
			continue
		}
		if len(m) != 0 {
			if ok := mapHandler(m, raw); !ok {
				break
			}
		} else if merr != io.EOF {
			time.Sleep(xhandlerPollInterval)
		}
		if merr == io.EOF {
			break
		}
	}
	return nil
}
type teeReader struct {
	r io.Reader
	w io.Writer
	b []byte
}
func myTeeReader(r io.Reader, w io.Writer) io.Reader {
	b := make([]byte, 1)
	return &teeReader{r, w, b}
}
func (t *teeReader) Read(p []byte) (int, error) {
	return 0, nil
}
func (t *teeReader) ReadByte() (byte, error) {
	n, err := t.r.Read(t.b)
	if n > 0 {
		if _, err := t.w.Write(t.b[:1]); err != nil {
			return t.b[0], err
		}
	}
	return t.b[0], err
}
type byteReader struct {
	r io.Reader
	b []byte
}
func myByteReader(r io.Reader) io.Reader {
	b := make([]byte, 1)
	return &byteReader{r, b}
}
func (b *byteReader) Read(p []byte) (int, error) {
	return b.r.Read(p)
}
func (b *byteReader) ReadByte() (byte, error) {
	_, err := b.r.Read(b.b)
	if len(b.b) > 0 {
		return b.b[0], nil
	}
	var c byte
	return c, err
}
func (mv Map) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
	m := map[string]interface{}(mv)
	var err error
	b := new(bytes.Buffer)
	p := new(pretty)
	p.indent = indent
	p.padding = prefix
	if len(m) == 1 && len(rootTag) == 0 {
		for key, value := range m {
			if _, ok := value.([]interface{}); ok {
				err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
			} else {
				err = marshalMapToXmlIndent(true, b, key, value, p)
			}
		}
	} else if len(rootTag) == 1 {
		err = marshalMapToXmlIndent(true, b, rootTag[0], m, p)
	} else {
		err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
	}
	if xmlCheckIsValid {
		d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
		for {
			_, err = d.Token()
			if err == io.EOF {
				err = nil
				break
			} else if err != nil {
				return nil, err
			}
		}
	}
	return b.Bytes(), err
}
type pretty struct {
	indent   string
	cnt      int
	padding  string
	mapDepth int
	start    int
}
func (p *pretty) Indent() {
	p.padding += p.indent
	p.cnt++
}
func (p *pretty) Outdent() {
	if p.cnt > 0 {
		p.padding = p.padding[:len(p.padding)-len(p.indent)]
		p.cnt--
	}
}