1
- // Copyright 2012-2016 Charles Banning. All rights reserved.
1
+ // Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
2
2
// Use of this source code is governed by a BSD-style
3
3
// license that can be found in the LICENSE file
4
4
@@ -17,20 +17,23 @@ import (
17
17
"strings"
18
18
)
19
19
20
+ // MapSeq is like Map but contains seqencing indices to allow recovering the original order of
21
+ // the XML elements when the map[string]interface{} is marshaled. (Also, element attributes are
22
+ // stored as a map["#attr"][]map[<tag>]map[<attr_key>]map[string]interface{} value instead of
23
+ // denoting the keys with a prefix character.
24
+ type MapSeq map [string ]interface {}
25
+
20
26
// NoRoot is returned by NewXmlSeq, etc., when a comment, directive or procinstr element is parsed
21
27
// in the XML data stream and the element is not contained in an XML object with a root element.
22
28
var NoRoot = errors .New ("no root key" )
23
29
var NO_ROOT = NoRoot // maintain backwards compatibility
24
30
25
31
// ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... -------------------------
26
32
27
- // This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure.
28
- // The xml.Decoder.RawToken method is used to parse the XML, so there is no checking for appropriate xml.EndElement values;
29
- // thus, it is assumed that the XML is valid.
30
- //
31
- // NewMapXmlSeq - convert a XML doc into a Map with elements id'd with decoding sequence int - #seq.
33
+ // NewMapXmlSeq converts a XML doc into a MapSeq value with elements id'd with decoding sequence key represented
34
+ // as map["#seq"]<int value>.
32
35
// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
33
- // NOTE: "#seq" key/value pairs are removed on encoding with mv.XmlSeq () / mv.XmlSeqIndent ().
36
+ // NOTE: "#seq" key/value pairs are removed on encoding with msv.Xml () / msv.XmlIndent ().
34
37
// • attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":<aval>, "#seq":<num>}
35
38
// • all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well.
36
39
// • lists always decode as map["list_tag"][]map[string]interface{} where the array elements are maps that
@@ -52,7 +55,7 @@ var NO_ROOT = NoRoot // maintain backwards compatibility
52
55
// newtag :
53
56
// #seq :[int] 1
54
57
// #text :[string] value 2
55
- // It will encode in proper sequence even though the Map representation merges all "ltag" elements in an array.
58
+ // It will encode in proper sequence even though the MapSeq representation merges all "ltag" elements in an array.
56
59
// • comments - "<!--comment-->" - are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair.
57
60
// • directives - "<!text>" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair.
58
61
// • process instructions - "<?instr?>" - are decoded as map["#procinst"]interface{} where the #procinst value
@@ -70,24 +73,22 @@ var NO_ROOT = NoRoot // maintain backwards compatibility
70
73
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
71
74
//
72
75
// NAME SPACES:
73
- // 1. Keys in the Map value that are parsed from a <name space prefix>:<local name> tag preserve the
76
+ // 1. Keys in the MapSeq value that are parsed from a <name space prefix>:<local name> tag preserve the
74
77
// "<prefix>:" notation rather than stripping it as with NewMapXml().
75
78
// 2. Attribute keys for name space prefix declarations preserve "xmlns:<prefix>" notation.
76
79
//
77
80
// ERRORS:
78
81
// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
79
82
// "#directive" or #procinst" key.
80
- func NewMapXmlSeq (xmlVal []byte , cast ... bool ) (Map , error ) {
83
+ func NewMapXmlSeq (xmlVal []byte , cast ... bool ) (MapSeq , error ) {
81
84
var r bool
82
85
if len (cast ) == 1 {
83
86
r = cast [0 ]
84
87
}
85
88
return xmlSeqToMap (xmlVal , r )
86
89
}
87
90
88
- // This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure.
89
- //
90
- // Get next XML doc from an io.Reader as a Map value. Returns Map value.
91
+ // NewMpaXmlSeqReader returns next XML doc from an io.Reader as a MapSeq value.
91
92
// NOTES:
92
93
// 1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
93
94
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
@@ -98,7 +99,7 @@ func NewMapXmlSeq(xmlVal []byte, cast ...bool) (Map, error) {
98
99
// ERRORS:
99
100
// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
100
101
// "#directive" or #procinst" key.
101
- func NewMapXmlSeqReader (xmlReader io.Reader , cast ... bool ) (Map , error ) {
102
+ func NewMapXmlSeqReader (xmlReader io.Reader , cast ... bool ) (MapSeq , error ) {
102
103
var r bool
103
104
if len (cast ) == 1 {
104
105
r = cast [0 ]
@@ -115,9 +116,8 @@ func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (Map, error) {
115
116
return xmlSeqReaderToMap (xmlReader , r )
116
117
}
117
118
118
- // This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure.
119
- //
120
- // Get next XML doc from an io.Reader as a Map value. Returns Map value and slice with the raw XML.
119
+ // NewMapXmlSeqReaderRaw returns the next XML doc from an io.Reader as a MapSeq value.
120
+ // Returns MapSeq value, slice with the raw XML, and any error.
121
121
// NOTES:
122
122
// 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
123
123
// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
@@ -132,9 +132,9 @@ func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (Map, error) {
132
132
// 5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
133
133
//
134
134
// ERRORS:
135
- // 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
135
+ // 1. If a NoRoot error, "no root key," is returned, check if the initial map key is "#comment",
136
136
// "#directive" or #procinst" key.
137
- func NewMapXmlSeqReaderRaw (xmlReader io.Reader , cast ... bool ) (Map , []byte , error ) {
137
+ func NewMapXmlSeqReaderRaw (xmlReader io.Reader , cast ... bool ) (MapSeq , []byte , error ) {
138
138
var r bool
139
139
if len (cast ) == 1 {
140
140
r = cast [0 ]
@@ -394,12 +394,9 @@ func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[s
394
394
395
395
// --------------------- mv.XmlSeq & mv.XmlSeqWriter -------------------------
396
396
397
- // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
398
- //
399
- // Encode a Map as XML with elements sorted on #seq. The companion of NewMapXmlSeq().
397
+ // Xml encodes a MapSeq as XML with elements sorted on #seq. The companion of NewMapXmlSeq().
400
398
// The following rules apply.
401
- // - The key label "#text" is treated as the value for a simple element with attributes.
402
- // - The "#seq" key is used to seqence the subelements or attributes but is ignored for writing.
399
+ // - The "#seq" key value is used to seqence the subelements or attributes only.
403
400
// - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key.
404
401
// - The "#comment" map key identifies a comment in the value "#text" map entry - <!--comment-->.
405
402
// - The "#directive" map key identifies a directive in the value "#text" map entry - <!directive>.
@@ -413,7 +410,7 @@ func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[s
413
410
// - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called.
414
411
// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
415
412
// Thus, `{ "key":"value" }` encodes as "<key>value</key>".
416
- func (mv Map ) XmlSeq (rootTag ... string ) ([]byte , error ) {
413
+ func (mv MapSeq ) Xml (rootTag ... string ) ([]byte , error ) {
417
414
m := map [string ]interface {}(mv )
418
415
var err error
419
416
s := new (string )
@@ -449,12 +446,10 @@ done:
449
446
// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
450
447
// The names will also provide a key for the number of return arguments.
451
448
452
- // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
453
- //
454
- // Writes the Map as XML on the Writer.
455
- // See XmlSeq() for encoding rules.
456
- func (mv Map ) XmlSeqWriter (xmlWriter io.Writer , rootTag ... string ) error {
457
- x , err := mv .XmlSeq (rootTag ... )
449
+ // XmlWriter Writes the MapSeq value as XML on the Writer.
450
+ // See MapSeq.Xml() for encoding rules.
451
+ func (mv MapSeq ) XmlWriter (xmlWriter io.Writer , rootTag ... string ) error {
452
+ x , err := mv .Xml (rootTag ... )
458
453
if err != nil {
459
454
return err
460
455
}
@@ -463,26 +458,24 @@ func (mv Map) XmlSeqWriter(xmlWriter io.Writer, rootTag ...string) error {
463
458
return err
464
459
}
465
460
466
- // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
467
- //
468
- // Writes the Map as XML on the Writer. []byte is the raw XML that was written.
469
- // See XmlSeq() for encoding rules.
470
- func (mv Map ) XmlSeqWriterRaw (xmlWriter io.Writer , rootTag ... string ) ([]byte , error ) {
471
- x , err := mv .XmlSeq (rootTag ... )
461
+ // XmlWriteRaw writes the MapSeq value as XML on the Writer. []byte is the raw XML that was written.
462
+ // See Map.XmlSeq() for encoding rules.
463
+ /*
464
+ func (mv MapSeq) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
465
+ x, err := mv.Xml(rootTag...)
472
466
if err != nil {
473
467
return x, err
474
468
}
475
469
476
470
_, err = xmlWriter.Write(x)
477
471
return x, err
478
472
}
473
+ */
479
474
480
- // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
481
- //
482
- // Writes the Map as pretty XML on the Writer.
483
- // See Xml() for encoding rules.
484
- func (mv Map ) XmlSeqIndentWriter (xmlWriter io.Writer , prefix , indent string , rootTag ... string ) error {
485
- x , err := mv .XmlSeqIndent (prefix , indent , rootTag ... )
475
+ // XmlIndentWriter writes the MapSeq value as pretty XML on the Writer.
476
+ // See MapSeq.Xml() for encoding rules.
477
+ func (mv MapSeq ) XmlIndentWriter (xmlWriter io.Writer , prefix , indent string , rootTag ... string ) error {
478
+ x , err := mv .XmlIndent (prefix , indent , rootTag ... )
486
479
if err != nil {
487
480
return err
488
481
}
@@ -491,11 +484,10 @@ func (mv Map) XmlSeqIndentWriter(xmlWriter io.Writer, prefix, indent string, roo
491
484
return err
492
485
}
493
486
494
- // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
495
- //
496
- // Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
497
- // See XmlSeq() for encoding rules.
498
- func (mv Map ) XmlSeqIndentWriterRaw (xmlWriter io.Writer , prefix , indent string , rootTag ... string ) ([]byte , error ) {
487
+ // XmlIndentWriterRaw writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
488
+ // See Map.XmlSeq() for encoding rules.
489
+ /*
490
+ func (mv MapSeq) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
499
491
x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
500
492
if err != nil {
501
493
return x, err
@@ -504,16 +496,15 @@ func (mv Map) XmlSeqIndentWriterRaw(xmlWriter io.Writer, prefix, indent string,
504
496
_, err = xmlWriter.Write(x)
505
497
return x, err
506
498
}
499
+ */
507
500
508
501
// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
509
502
510
503
// ---------------------- XmlSeqIndent ----------------------------
511
504
512
- // This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
513
- //
514
- // Encode a map[string]interface{} as a pretty XML string.
515
- // See mv.XmlSeq() for encoding rules.
516
- func (mv Map ) XmlSeqIndent (prefix , indent string , rootTag ... string ) ([]byte , error ) {
505
+ // XmlIndent encodes a map[string]interface{} as a pretty XML string.
506
+ // See MapSeq.XmlSeq() for encoding rules.
507
+ func (mv MapSeq ) XmlIndent (prefix , indent string , rootTag ... string ) ([]byte , error ) {
517
508
m := map [string ]interface {}(mv )
518
509
519
510
var err error
@@ -842,10 +833,11 @@ func (e elemListSeq) Less(i, j int) bool {
842
833
// =============== https://groups.google.com/forum/#!topic/golang-nuts/lHPOHD-8qio
843
834
844
835
// BeautifyXml (re)formats an XML doc similar to Map.XmlIndent().
836
+ // It preserves comments, directives and process instructions,
845
837
func BeautifyXml (b []byte , prefix , indent string ) ([]byte , error ) {
846
838
x , err := NewMapXmlSeq (b )
847
839
if err != nil {
848
840
return nil , err
849
841
}
850
- return x .XmlSeqIndent (prefix , indent )
842
+ return x .XmlIndent (prefix , indent )
851
843
}
0 commit comments