Skip to content

Commit 9bb810d

Browse files
committed
v2.0
Add XmlSeq type and move functions and methods from Map type. Also, remove probably unused XmlWriter methods that return 'raw' value; a potentially breaking change.
1 parent 4638d1b commit 9bb810d

12 files changed

+88
-79
lines changed

Diff for: badxml_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func TestBadXmlSeq(t *testing.T) {
3939
t.Fatalf("err: didn't find xmlStartElement")
4040
}
4141
fmt.Printf("m: %v\n", m)
42-
j, _ := m.XmlSeq()
42+
j, _ := m.Xml()
4343
fmt.Println("m:", string(j))
4444
}
4545

@@ -63,6 +63,6 @@ func TestBadXmlSeqReader(t *testing.T) {
6363
t.Fatalf("err: didn't find xmlStartElement")
6464
}
6565
fmt.Printf("m: %v\n", m)
66-
j, _ := m.XmlSeq()
66+
j, _ := m.Xml()
6767
fmt.Println("m:", string(j))
6868
}

Diff for: bom_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func TestBomDataSeq(t *testing.T) {
5959
t.Fatalf("err: didn't find xml.StartElement")
6060
}
6161
fmt.Printf("m: %v\n", m)
62-
j, _ := m.XmlSeq()
62+
j, _ := m.Xml()
6363
fmt.Println("m:", string(j))
6464
}
6565

@@ -83,6 +83,6 @@ func TestBomDataSeqReader(t *testing.T) {
8383
t.Fatalf("err: didn't find xml.StartElement")
8484
}
8585
fmt.Printf("m: %v\n", m)
86-
j, _ := m.XmlSeq()
86+
j, _ := m.Xml()
8787
fmt.Println("m:", string(j))
8888
}

Diff for: doc.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ Related Packages:
1414
checkxml: github.com/clbanning/checkxml provides functions for validating XML data.
1515
1616
Notes:
17-
2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>]
17+
2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
18+
2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
19+
2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>].
1820
2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
1921
2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
2022
2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.

Diff for: escapechars_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func TestXMLSeqEscapeChars(t *testing.T) {
8080
XMLEscapeChars(true)
8181
defer XMLEscapeChars(false)
8282

83-
x, err := m.XmlSeqIndent("", " ")
83+
x, err := m.XmlIndent("", " ")
8484
if err != nil {
8585
t.Fatal(err)
8686
}
@@ -105,7 +105,7 @@ func TestXMLSeqEscapeChars2(t *testing.T) {
105105
XMLEscapeChars(true)
106106
defer XMLEscapeChars(false)
107107

108-
x, err := m.XmlSeqIndent("", " ")
108+
x, err := m.XmlIndent("", " ")
109109
if err != nil {
110110
t.Fatal(err)
111111
}

Diff for: readme.md

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ For over a year I've wanted to refactor the XML-to-map[string]interface{} decode
2121

2222
<h4>Notices</h4>
2323

24+
2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
25+
2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
2426
2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>]
2527
2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
2628
2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.

Diff for: snakecase_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ func TestStakeCase(t *testing.T) {
5353
</software_information>
5454
</rpc_reply>`
5555

56-
m, err = NewMapXmlSeq([]byte(data1))
56+
ms, err := NewMapXmlSeq([]byte(data1))
5757
if err != nil {
5858
t.Fatal(err)
5959
}
6060

61-
x, err = m.XmlSeqIndent("", "")
61+
x, err = ms.XmlIndent("", "")
6262
if err != nil {
6363
t.Fatal(err)
6464
}

Diff for: xml.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012-2016, 2018 Charles Banning. All rights reserved.
1+
// Copyright 2012-2016, 2018-2019 Charles Banning. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file
44

@@ -653,6 +653,7 @@ func (mv Map) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
653653

654654
// Writes the Map as XML on the Writer. []byte is the raw XML that was written.
655655
// See Xml() for encoding rules.
656+
/*
656657
func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
657658
x, err := mv.Xml(rootTag...)
658659
if err != nil {
@@ -662,6 +663,7 @@ func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, erro
662663
_, err = xmlWriter.Write(x)
663664
return x, err
664665
}
666+
*/
665667

666668
// Writes the Map as pretty XML on the Writer.
667669
// See Xml() for encoding rules.
@@ -677,6 +679,7 @@ func (mv Map) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTa
677679

678680
// Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
679681
// See Xml() for encoding rules.
682+
/*
680683
func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
681684
x, err := mv.XmlIndent(prefix, indent, rootTag...)
682685
if err != nil {
@@ -686,6 +689,7 @@ func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, roo
686689
_, err = xmlWriter.Write(x)
687690
return x, err
688691
}
692+
*/
689693

690694
// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
691695

Diff for: xml3_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func TestOnlyAttributesSeq(t *testing.T) {
3939
if err != nil {
4040
t.Fatal(err)
4141
}
42-
xml, err := dom.XmlSeqIndent("", " ")
42+
xml, err := dom.XmlIndent("", " ")
4343
if err != nil {
4444
t.Fatal(err)
4545
}

Diff for: xml_test.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func TestXmlWriter(t *testing.T) {
179179
mv := Map{"tag1": "some data", "tag2": "more data", "boolean": true, "float": 3.14159625}
180180
w := new(bytes.Buffer)
181181

182-
raw, err := mv.XmlWriterRaw(w, "myRootTag")
182+
err := mv.XmlWriter(w, "myRootTag")
183183
if err != nil {
184184
t.Fatal("err:", err.Error())
185185
}
@@ -190,7 +190,6 @@ func TestXmlWriter(t *testing.T) {
190190
t.Fatal("err:", err.Error())
191191
}
192192

193-
fmt.Println("XmlWriter, raw:", string(raw))
194193
fmt.Println("XmlWriter, b :", string(b))
195194
}
196195

Diff for: xmlseq.go

+46-54
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012-2016 Charles Banning. All rights reserved.
1+
// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file
44

@@ -17,20 +17,23 @@ import (
1717
"strings"
1818
)
1919

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+
2026
// NoRoot is returned by NewXmlSeq, etc., when a comment, directive or procinstr element is parsed
2127
// in the XML data stream and the element is not contained in an XML object with a root element.
2228
var NoRoot = errors.New("no root key")
2329
var NO_ROOT = NoRoot // maintain backwards compatibility
2430

2531
// ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... -------------------------
2632

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>.
3235
// 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().
3437
// • attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":<aval>, "#seq":<num>}
3538
// • all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well.
3639
// • 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
5255
// newtag :
5356
// #seq :[int] 1
5457
// #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.
5659
// • comments - "<!--comment-->" - are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair.
5760
// • directives - "<!text>" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair.
5861
// • process instructions - "<?instr?>" - are decoded as map["#procinst"]interface{} where the #procinst value
@@ -70,24 +73,22 @@ var NO_ROOT = NoRoot // maintain backwards compatibility
7073
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
7174
//
7275
// 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
7477
// "<prefix>:" notation rather than stripping it as with NewMapXml().
7578
// 2. Attribute keys for name space prefix declarations preserve "xmlns:<prefix>" notation.
7679
//
7780
// ERRORS:
7881
// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
7982
// "#directive" or #procinst" key.
80-
func NewMapXmlSeq(xmlVal []byte, cast ...bool) (Map, error) {
83+
func NewMapXmlSeq(xmlVal []byte, cast ...bool) (MapSeq, error) {
8184
var r bool
8285
if len(cast) == 1 {
8386
r = cast[0]
8487
}
8588
return xmlSeqToMap(xmlVal, r)
8689
}
8790

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.
9192
// NOTES:
9293
// 1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
9394
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
@@ -98,7 +99,7 @@ func NewMapXmlSeq(xmlVal []byte, cast ...bool) (Map, error) {
9899
// ERRORS:
99100
// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
100101
// "#directive" or #procinst" key.
101-
func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (Map, error) {
102+
func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (MapSeq, error) {
102103
var r bool
103104
if len(cast) == 1 {
104105
r = cast[0]
@@ -115,9 +116,8 @@ func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (Map, error) {
115116
return xmlSeqReaderToMap(xmlReader, r)
116117
}
117118

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.
121121
// NOTES:
122122
// 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
123123
// 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) {
132132
// 5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
133133
//
134134
// 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",
136136
// "#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) {
138138
var r bool
139139
if len(cast) == 1 {
140140
r = cast[0]
@@ -394,12 +394,9 @@ func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[s
394394

395395
// --------------------- mv.XmlSeq & mv.XmlSeqWriter -------------------------
396396

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().
400398
// 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.
403400
// - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key.
404401
// - The "#comment" map key identifies a comment in the value "#text" map entry - <!--comment-->.
405402
// - 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
413410
// - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called.
414411
// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
415412
// 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) {
417414
m := map[string]interface{}(mv)
418415
var err error
419416
s := new(string)
@@ -449,12 +446,10 @@ done:
449446
// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
450447
// The names will also provide a key for the number of return arguments.
451448

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...)
458453
if err != nil {
459454
return err
460455
}
@@ -463,26 +458,24 @@ func (mv Map) XmlSeqWriter(xmlWriter io.Writer, rootTag ...string) error {
463458
return err
464459
}
465460

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...)
472466
if err != nil {
473467
return x, err
474468
}
475469
476470
_, err = xmlWriter.Write(x)
477471
return x, err
478472
}
473+
*/
479474

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...)
486479
if err != nil {
487480
return err
488481
}
@@ -491,11 +484,10 @@ func (mv Map) XmlSeqIndentWriter(xmlWriter io.Writer, prefix, indent string, roo
491484
return err
492485
}
493486

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) {
499491
x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
500492
if err != nil {
501493
return x, err
@@ -504,16 +496,15 @@ func (mv Map) XmlSeqIndentWriterRaw(xmlWriter io.Writer, prefix, indent string,
504496
_, err = xmlWriter.Write(x)
505497
return x, err
506498
}
499+
*/
507500

508501
// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
509502

510503
// ---------------------- XmlSeqIndent ----------------------------
511504

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) {
517508
m := map[string]interface{}(mv)
518509

519510
var err error
@@ -842,10 +833,11 @@ func (e elemListSeq) Less(i, j int) bool {
842833
// =============== https://groups.google.com/forum/#!topic/golang-nuts/lHPOHD-8qio
843834

844835
// BeautifyXml (re)formats an XML doc similar to Map.XmlIndent().
836+
// It preserves comments, directives and process instructions,
845837
func BeautifyXml(b []byte, prefix, indent string) ([]byte, error) {
846838
x, err := NewMapXmlSeq(b)
847839
if err != nil {
848840
return nil, err
849841
}
850-
return x.XmlSeqIndent(prefix, indent)
842+
return x.XmlIndent(prefix, indent)
851843
}

0 commit comments

Comments
 (0)