xml.go 41 KB

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