xml.go 39 KB

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