xmlseq.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. // Copyright 2012-2016 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. // xmlseq.go - version of xml.go with sequence # injection on Decoding and sorting on Encoding.
  5. // Also, handles comments, directives and process instructions.
  6. package mxj
  7. import (
  8. "bytes"
  9. "encoding/xml"
  10. "errors"
  11. "fmt"
  12. "io"
  13. "sort"
  14. "strings"
  15. )
  16. var NoRoot = errors.New("no root key")
  17. var NO_ROOT = NoRoot // maintain backwards compatibility
  18. // ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... -------------------------
  19. // This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure.
  20. //
  21. // NewMapXmlSeq - convert a XML doc into a Map with elements id'd with decoding sequence int - #seq.
  22. // If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
  23. // NOTE: "#seq" key/value pairs are removed on encoding with mv.XmlSeq() / mv.XmlSeqIndent().
  24. // • attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":<aval>, "#seq":<num>}
  25. // • all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well.
  26. // • lists always decode as map["list_tag"][]map[string]interface{} where the array elements are maps that
  27. // include a "#seq" k:v pair based on sequence they are decoded. Thus, XML like:
  28. // <doc>
  29. // <ltag>value 1</ltag>
  30. // <newtag>value 2</newtag>
  31. // <ltag>value 3</ltag>
  32. // </doc>
  33. // is decoded as:
  34. // doc :
  35. // ltag :[[]interface{}]
  36. // [item: 0]
  37. // #seq :[int] 0
  38. // #text :[string] value 1
  39. // [item: 1]
  40. // #seq :[int] 2
  41. // #text :[string] value 3
  42. // newtag :
  43. // #seq :[int] 1
  44. // #text :[string] value 2
  45. // It will encode in proper sequence even though the Map representation merges all "ltag" elements in an array.
  46. // • comments - "<!--comment-->" - are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair.
  47. // • directives - "<!text>" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair.
  48. // • process instructions - "<?instr?>" - are decoded as map["#procinst"]interface{} where the #procinst value
  49. // is of map[string]interface{} type with the following keys: #target, #inst, and #seq.
  50. // • comments, directives, and procinsts that are NOT part of a document with a root key will be returned as
  51. // map[string]interface{} and the error value 'NoRoot'.
  52. // • note: "<![CDATA[" syntax is lost in xml.Decode parser - and is not handled here, either.
  53. // and: "\r\n" is converted to "\n"
  54. //
  55. // NOTES:
  56. // 1. The 'xmlVal' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
  57. // extraneous xml.CharData will be ignored unless io.EOF is reached first.
  58. // 2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
  59. // re-encode the message in its original structure.
  60. func NewMapXmlSeq(xmlVal []byte, cast ...bool) (Map, error) {
  61. var r bool
  62. if len(cast) == 1 {
  63. r = cast[0]
  64. }
  65. return xmlSeqToMap(xmlVal, r)
  66. }
  67. // This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure.
  68. //
  69. // Get next XML doc from an io.Reader as a Map value. Returns Map value.
  70. // NOTES:
  71. // 1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
  72. // extraneous xml.CharData will be ignored unless io.EOF is reached first.
  73. // 2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
  74. // re-encode the message in its original structure.
  75. func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (Map, error) {
  76. var r bool
  77. if len(cast) == 1 {
  78. r = cast[0]
  79. }
  80. // build the node tree
  81. return xmlSeqReaderToMap(xmlReader, r)
  82. }
  83. // This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure.
  84. //
  85. // Get next XML doc from an io.Reader as a Map value. Returns Map value and slice with the raw XML.
  86. // NOTES:
  87. // 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
  88. // using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
  89. // See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
  90. // data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
  91. // you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
  92. // 2. The 'raw' return value may be larger than the XML text value.
  93. // 3. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
  94. // extraneous xml.CharData will be ignored unless io.EOF is reached first.
  95. // 4. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
  96. // re-encode the message in its original structure.
  97. func NewMapXmlSeqReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
  98. var r bool
  99. if len(cast) == 1 {
  100. r = cast[0]
  101. }
  102. // create TeeReader so we can retrieve raw XML
  103. buf := make([]byte, XmlWriterBufSize)
  104. wb := bytes.NewBuffer(buf)
  105. trdr := myTeeReader(xmlReader, wb)
  106. // build the node tree
  107. m, err := xmlSeqReaderToMap(trdr, r)
  108. // retrieve the raw XML that was decoded
  109. b := make([]byte, wb.Len())
  110. _, _ = wb.Read(b)
  111. if err != nil {
  112. return nil, b, err
  113. }
  114. return m, b, nil
  115. }
  116. // xmlSeqReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
  117. func xmlSeqReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
  118. // parse the Reader
  119. p := xml.NewDecoder(rdr)
  120. p.CharsetReader = XmlCharsetReader
  121. return xmlSeqToMapParser("", nil, p, r)
  122. }
  123. // xmlSeqToMap - convert a XML doc into map[string]interface{} value
  124. func xmlSeqToMap(doc []byte, r bool) (map[string]interface{}, error) {
  125. b := bytes.NewReader(doc)
  126. p := xml.NewDecoder(b)
  127. p.CharsetReader = XmlCharsetReader
  128. return xmlSeqToMapParser("", nil, p, r)
  129. }
  130. // ===================================== where the work happens =============================
  131. // xmlSeqToMapParser - load a 'clean' XML doc into a map[string]interface{} directly.
  132. // Add #seq tag value for each element decoded - to be used for Encoding later.
  133. func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
  134. // NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey'
  135. var n, na map[string]interface{}
  136. var seq int // for including seq num when decoding
  137. // Allocate maps and load attributes, if any.
  138. if skey != "" {
  139. // 'n' only needs one slot - save call to runtime•hashGrow()
  140. // 'na' we don't know
  141. n = make(map[string]interface{}, 1)
  142. na = make(map[string]interface{})
  143. if len(a) > 0 {
  144. // xml.Attr is decoded into: map["#attr"]map[<attr_label>]interface{}
  145. // where interface{} is map[string]interface{}{"#text":<attr_val>, "#seq":<attr_seq>}
  146. aa := make(map[string]interface{}, len(a))
  147. for i, v := range a {
  148. aa[v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r), "#seq": i}
  149. }
  150. na["#attr"] = aa
  151. }
  152. }
  153. for {
  154. t, err := p.Token()
  155. if err != nil {
  156. if err != io.EOF {
  157. return nil, errors.New("xml.Decoder.Token() - " + err.Error())
  158. }
  159. return nil, err
  160. }
  161. switch t.(type) {
  162. case xml.StartElement:
  163. tt := t.(xml.StartElement)
  164. // First call to xmlSeqToMapParser() doesn't pass xml.StartElement - the map key.
  165. // So when the loop is first entered, the first token is the root tag along
  166. // with any attributes, which we process here.
  167. //
  168. // Subsequent calls to xmlSeqToMapParser() will pass in tag+attributes for
  169. // processing before getting the next token which is the element value,
  170. // which is done above.
  171. if skey == "" {
  172. return xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
  173. }
  174. // If not initializing the map, parse the element.
  175. // len(nn) == 1, necessarily - it is just an 'n'.
  176. nn, err := xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
  177. if err != nil {
  178. return nil, err
  179. }
  180. // The nn map[string]interface{} value is a na[nn_key] value.
  181. // We need to see if nn_key already exists - means we're parsing a list.
  182. // This may require converting na[nn_key] value into []interface{} type.
  183. // First, extract the key:val for the map - it's a singleton.
  184. var key string
  185. var val interface{}
  186. for key, val = range nn {
  187. break
  188. }
  189. // add "#seq" k:v pair -
  190. // Sequence number included even in list elements - this should allow us
  191. // to properly resequence even something goofy like:
  192. // <list>item 1</list>
  193. // <subelement>item 2</subelement>
  194. // <list>item 3</list>
  195. // where all the "list" subelements are decoded into an array.
  196. switch val.(type) {
  197. case map[string]interface{}:
  198. val.(map[string]interface{})["#seq"] = seq
  199. seq++
  200. case interface{}: // a non-nil simple element: string, float64, bool
  201. v := map[string]interface{}{"#text": val, "#seq": seq}
  202. seq++
  203. val = v
  204. }
  205. // 'na' holding sub-elements of n.
  206. // See if 'key' already exists.
  207. // If 'key' exists, then this is a list, if not just add key:val to na.
  208. if v, ok := na[key]; ok {
  209. var a []interface{}
  210. switch v.(type) {
  211. case []interface{}:
  212. a = v.([]interface{})
  213. default: // anything else - note: v.(type) != nil
  214. a = []interface{}{v}
  215. }
  216. a = append(a, val)
  217. na[key] = a
  218. } else {
  219. na[key] = val // save it as a singleton
  220. }
  221. case xml.EndElement:
  222. // len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
  223. if len(n) == 0 {
  224. // If len(na)==0 we have an empty element == "";
  225. // it has no xml.Attr nor xml.CharData.
  226. // Empty element content will be map["etag"]map["#text"]""
  227. // after #seq injection - map["etag"]map["#seq"]seq - after return.
  228. if len(na) > 0 {
  229. n[skey] = na
  230. } else {
  231. n[skey] = "" // empty element
  232. }
  233. }
  234. return n, nil
  235. case xml.CharData:
  236. // clean up possible noise
  237. tt := strings.Trim(string(t.(xml.CharData)), "\t\r\b\n ")
  238. if skey == "" {
  239. // per Adrian (http://www.adrianlungu.com/) catch stray text
  240. // in decoder stream -
  241. // https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
  242. // NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
  243. // a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
  244. continue
  245. }
  246. if len(tt) > 0 {
  247. // every simple element is a #text and has #seq associated with it
  248. na["#text"] = cast(tt, r)
  249. na["#seq"] = seq
  250. seq++
  251. }
  252. case xml.Comment:
  253. if n == nil { // no root 'key'
  254. n = map[string]interface{}{"#comment": string(t.(xml.Comment))}
  255. return n, NoRoot
  256. }
  257. cm := make(map[string]interface{}, 2)
  258. cm["#text"] = string(t.(xml.Comment))
  259. cm["#seq"] = seq
  260. seq++
  261. na["#comment"] = cm
  262. case xml.Directive:
  263. if n == nil { // no root 'key'
  264. n = map[string]interface{}{"#directive": string(t.(xml.Directive))}
  265. return n, NoRoot
  266. }
  267. dm := make(map[string]interface{}, 2)
  268. dm["#text"] = string(t.(xml.Directive))
  269. dm["#seq"] = seq
  270. seq++
  271. na["#directive"] = dm
  272. case xml.ProcInst:
  273. if n == nil {
  274. na = map[string]interface{}{"#target": t.(xml.ProcInst).Target, "#inst": string(t.(xml.ProcInst).Inst)}
  275. n = map[string]interface{}{"#procinst": na}
  276. return n, NoRoot
  277. }
  278. pm := make(map[string]interface{}, 3)
  279. pm["#target"] = t.(xml.ProcInst).Target
  280. pm["#inst"] = string(t.(xml.ProcInst).Inst)
  281. pm["#seq"] = seq
  282. seq++
  283. na["#procinst"] = pm
  284. default:
  285. // noop - shouldn't ever get here, now, since we handle all token types
  286. }
  287. }
  288. }
  289. // ------------------ END: NewMapXml & NewMapXmlReader -------------------------
  290. // ------------------ mv.Xml & mv.XmlWriter - from j2x ------------------------
  291. // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
  292. //
  293. // Encode a Map as XML with elements sorted on #seq. The companion of NewMapXmlSeq().
  294. // The following rules apply.
  295. // - The key label "#text" is treated as the value for a simple element with attributes.
  296. // - The "#seq" key is used to seqence the subelements or attributes but is ignored for writing.
  297. // - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key.
  298. // - The "#comment" map key identifies a comment in the value "#text" map entry - <!--comment-->.
  299. // - The "#directive" map key identifies a directive in the value "#text" map entry - <!directive>.
  300. // - The "#procinst" map key identifies a process instruction in the value "#target" and "#inst"
  301. // map entries - <?target inst?>.
  302. // - Value type encoding:
  303. // > string, bool, float64, int, int32, int64, float32: per "%v" formating
  304. // > []bool, []uint8: by casting to string
  305. // > structures, etc.: handed to xml.Marshal() - if there is an error, the element
  306. // value is "UNKNOWN"
  307. // - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called.
  308. // - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
  309. // Thus, `{ "key":"value" }` encodes as "<key>value</key>".
  310. func (mv Map) XmlSeq(rootTag ...string) ([]byte, error) {
  311. m := map[string]interface{}(mv)
  312. var err error
  313. s := new(string)
  314. p := new(pretty) // just a stub
  315. if len(m) == 1 && len(rootTag) == 0 {
  316. for key, value := range m {
  317. // if it an array, see if all values are map[string]interface{}
  318. // we force a new root tag if we'll end up with no key:value in the list
  319. // so: key:[string_val, bool:true] --> <doc><key>string_val</key><bool>true</bool></doc>
  320. switch value.(type) {
  321. case []interface{}:
  322. for _, v := range value.([]interface{}) {
  323. switch v.(type) {
  324. case map[string]interface{}: // noop
  325. default: // anything else
  326. err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
  327. goto done
  328. }
  329. }
  330. }
  331. err = mapToXmlSeqIndent(false, s, key, value, p)
  332. }
  333. } else if len(rootTag) == 1 {
  334. err = mapToXmlSeqIndent(false, s, rootTag[0], m, p)
  335. } else {
  336. err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
  337. }
  338. done:
  339. return []byte(*s), err
  340. }
  341. // The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
  342. // The names will also provide a key for the number of return arguments.
  343. // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
  344. //
  345. // Writes the Map as XML on the Writer.
  346. // See Xml() for encoding rules.
  347. func (mv Map) XmlSeqWriter(xmlWriter io.Writer, rootTag ...string) error {
  348. x, err := mv.XmlSeq(rootTag...)
  349. if err != nil {
  350. return err
  351. }
  352. _, err = xmlWriter.Write(x)
  353. return err
  354. }
  355. // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
  356. //
  357. // Writes the Map as XML on the Writer. []byte is the raw XML that was written.
  358. // See Xml() for encoding rules.
  359. func (mv Map) XmlSeqWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
  360. x, err := mv.XmlSeq(rootTag...)
  361. if err != nil {
  362. return x, err
  363. }
  364. _, err = xmlWriter.Write(x)
  365. return x, err
  366. }
  367. // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
  368. //
  369. // Writes the Map as pretty XML on the Writer.
  370. // See Xml() for encoding rules.
  371. func (mv Map) XmlSeqIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
  372. x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
  373. if err != nil {
  374. return err
  375. }
  376. _, err = xmlWriter.Write(x)
  377. return err
  378. }
  379. // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
  380. //
  381. // Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
  382. // See Xml() for encoding rules.
  383. func (mv Map) XmlSeqIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
  384. x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
  385. if err != nil {
  386. return x, err
  387. }
  388. _, err = xmlWriter.Write(x)
  389. return x, err
  390. }
  391. // -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
  392. // ---------------------- XmlSeqIndent ----------------------------
  393. // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
  394. //
  395. // Encode a map[string]interface{} as a pretty XML string.
  396. // See mv.XmlSeq() for encoding rules.
  397. func (mv Map) XmlSeqIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
  398. m := map[string]interface{}(mv)
  399. var err error
  400. s := new(string)
  401. p := new(pretty)
  402. p.indent = indent
  403. p.padding = prefix
  404. if len(m) == 1 && len(rootTag) == 0 {
  405. // this can extract the key for the single map element
  406. // use it if it isn't a key for a list
  407. for key, value := range m {
  408. if _, ok := value.([]interface{}); ok {
  409. err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
  410. } else {
  411. err = mapToXmlSeqIndent(true, s, key, value, p)
  412. }
  413. }
  414. } else if len(rootTag) == 1 {
  415. err = mapToXmlSeqIndent(true, s, rootTag[0], m, p)
  416. } else {
  417. err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
  418. }
  419. return []byte(*s), err
  420. }
  421. // where the work actually happens
  422. // returns an error if an attribute is not atomic
  423. func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{}, pp *pretty) error {
  424. var endTag bool
  425. var isSimple bool
  426. var noEndTag bool
  427. var elen int
  428. p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
  429. switch value.(type) {
  430. case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
  431. if doIndent {
  432. *s += p.padding
  433. }
  434. if key != "#comment" && key != "#directive" && key != "#procinst" {
  435. *s += `<` + key
  436. }
  437. }
  438. switch value.(type) {
  439. case map[string]interface{}:
  440. val := value.(map[string]interface{})
  441. if key == "#comment" {
  442. *s += `<!--` + val["#text"].(string) + `-->`
  443. noEndTag = true
  444. break
  445. }
  446. if key == "#directive" {
  447. *s += `<!` + val["#text"].(string) + `>`
  448. noEndTag = true
  449. break
  450. }
  451. if key == "#procinst" {
  452. *s += `<?` + val["#target"].(string) + ` ` + val["#inst"].(string) + `?>`
  453. noEndTag = true
  454. break
  455. }
  456. haveAttrs := false
  457. // process attributes first
  458. if v, ok := val["#attr"].(map[string]interface{}); ok {
  459. // First, unroll the map[string]interface{} into a []keyval array.
  460. // Then sequence it.
  461. kv := make([]keyval, len(v))
  462. n := 0
  463. for ak, av := range v {
  464. kv[n] = keyval{ak, av}
  465. n++
  466. }
  467. sort.Sort(elemListSeq(kv))
  468. // Now encode the attributes in original decoding sequence, using keyval array.
  469. for _, a := range kv {
  470. vv := a.v.(map[string]interface{})
  471. switch vv["#text"].(type) {
  472. case string, float64, bool, int, int32, int64, float32:
  473. *s += ` ` + a.k + `="` + fmt.Sprintf("%v", vv["#text"]) + `"`
  474. case []byte:
  475. *s += ` ` + a.k + `="` + fmt.Sprintf("%v", string(vv["#text"].([]byte))) + `"`
  476. default:
  477. return fmt.Errorf("invalid attribute value for: %s", a.k)
  478. }
  479. }
  480. haveAttrs = true
  481. }
  482. // simple element?
  483. // every map value has, at least, "#seq" and, perhaps, "#text" and/or "#attr"
  484. _, seqOK := val["#seq"] // have key
  485. if v, ok := val["#text"]; ok && ((len(val) == 3 && haveAttrs) || (len(val) == 2 && !haveAttrs)) && seqOK {
  486. if stmp, ok := v.(string); ok && stmp != "" {
  487. *s += ">" + fmt.Sprintf("%v", v)
  488. endTag = true
  489. elen = 1
  490. }
  491. isSimple = true
  492. break
  493. } else if !ok && ((len(val) == 2 && haveAttrs) || (len(val) == 1 && !haveAttrs)) && seqOK {
  494. // here no #text but have #seq or #seq+#attr
  495. endTag = false
  496. break
  497. }
  498. // we now need to sequence everything except attributes
  499. // 'kv' will hold everything that needs to be written
  500. kv := make([]keyval, 0)
  501. for k, v := range val {
  502. if k == "#attr" { // already processed
  503. continue
  504. }
  505. if k == "#seq" { // ignore - just for sorting
  506. continue
  507. }
  508. switch v.(type) {
  509. case []interface{}:
  510. // unwind the array as separate entries
  511. for _, vv := range v.([]interface{}) {
  512. kv = append(kv, keyval{k, vv})
  513. }
  514. default:
  515. kv = append(kv, keyval{k, v})
  516. }
  517. }
  518. // close tag with possible attributes
  519. *s += ">"
  520. if doIndent {
  521. *s += "\n"
  522. }
  523. // something more complex
  524. p.mapDepth++
  525. // PrintElemListSeq(elemListSeq(kv))
  526. sort.Sort(elemListSeq(kv))
  527. // PrintElemListSeq(elemListSeq(kv))
  528. i := 0
  529. for _, v := range kv {
  530. switch v.v.(type) {
  531. case []interface{}:
  532. default:
  533. if i == 0 && doIndent {
  534. p.Indent()
  535. }
  536. }
  537. i++
  538. mapToXmlSeqIndent(doIndent, s, v.k, v.v, p)
  539. switch v.v.(type) {
  540. case []interface{}: // handled in []interface{} case
  541. default:
  542. if doIndent {
  543. p.Outdent()
  544. }
  545. }
  546. i--
  547. }
  548. p.mapDepth--
  549. endTag = true
  550. elen = 1 // we do have some content other than attrs
  551. case []interface{}:
  552. for _, v := range value.([]interface{}) {
  553. if doIndent {
  554. p.Indent()
  555. }
  556. mapToXmlSeqIndent(doIndent, s, key, v, p)
  557. if doIndent {
  558. p.Outdent()
  559. }
  560. }
  561. return nil
  562. case nil:
  563. // terminate the tag
  564. *s += "<" + key
  565. break
  566. default: // handle anything - even goofy stuff
  567. elen = 0
  568. switch value.(type) {
  569. case string, float64, bool, int, int32, int64, float32:
  570. v := fmt.Sprintf("%v", value)
  571. elen = len(v)
  572. if elen > 0 {
  573. *s += ">" + v
  574. }
  575. case []byte: // NOTE: byte is just an alias for uint8
  576. // similar to how xml.Marshal handles []byte structure members
  577. v := string(value.([]byte))
  578. elen = len(v)
  579. if elen > 0 {
  580. *s += ">" + v
  581. }
  582. default:
  583. var v []byte
  584. var err error
  585. if doIndent {
  586. v, err = xml.MarshalIndent(value, p.padding, p.indent)
  587. } else {
  588. v, err = xml.Marshal(value)
  589. }
  590. if err != nil {
  591. *s += ">UNKNOWN"
  592. } else {
  593. elen = len(v)
  594. if elen > 0 {
  595. *s += string(v)
  596. }
  597. }
  598. }
  599. isSimple = true
  600. endTag = true
  601. }
  602. if endTag && !noEndTag {
  603. if doIndent {
  604. if !isSimple {
  605. *s += p.padding
  606. }
  607. }
  608. switch value.(type) {
  609. case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
  610. if elen > 0 || useGoXmlEmptyElemSyntax {
  611. if elen == 0 {
  612. *s += ">"
  613. }
  614. *s += `</` + key + ">"
  615. } else {
  616. *s += `/>`
  617. }
  618. }
  619. } else if !noEndTag {
  620. if useGoXmlEmptyElemSyntax {
  621. *s += "></" + key + ">"
  622. } else {
  623. *s += "/>"
  624. }
  625. }
  626. if doIndent {
  627. if p.cnt > p.start {
  628. *s += "\n"
  629. }
  630. p.Outdent()
  631. }
  632. return nil
  633. }
  634. // the element sort implementation
  635. type keyval struct {
  636. k string
  637. v interface{}
  638. }
  639. type elemListSeq []keyval
  640. func (e elemListSeq) Len() int {
  641. return len(e)
  642. }
  643. func (e elemListSeq) Swap(i, j int) {
  644. e[i], e[j] = e[j], e[i]
  645. }
  646. func (e elemListSeq) Less(i, j int) bool {
  647. var iseq, jseq int
  648. var ok bool
  649. if iseq, ok = e[i].v.(map[string]interface{})["#seq"].(int); !ok {
  650. iseq = 9999999
  651. }
  652. if jseq, ok = e[j].v.(map[string]interface{})["#seq"].(int); !ok {
  653. jseq = 9999999
  654. }
  655. if iseq > jseq {
  656. return false
  657. }
  658. return true
  659. }
  660. func PrintElemListSeq(e elemListSeq) {
  661. for n, v := range e {
  662. fmt.Printf("%d: %v\n", n, v)
  663. }
  664. }