mirror of
https://github.com/adnanh/webhook.git
synced 2025-05-12 08:34:43 +00:00
Update mxj dependency to v2.7.0
This commit is contained in:
parent
c6e6a2ac17
commit
843ee65d8c
43 changed files with 811 additions and 341 deletions
3
go.mod
3
go.mod
|
@ -3,12 +3,13 @@ module github.com/adnanh/webhook
|
||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/clbanning/mxj v1.8.4
|
github.com/clbanning/mxj/v2 v2.7.0
|
||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/fsnotify/fsnotify v1.4.7 // indirect
|
github.com/fsnotify/fsnotify v1.4.7 // indirect
|
||||||
github.com/ghodss/yaml v1.0.0
|
github.com/ghodss/yaml v1.0.0
|
||||||
github.com/go-chi/chi/v5 v5.0.12
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
github.com/gofrs/uuid/v5 v5.0.0
|
github.com/gofrs/uuid/v5 v5.0.0
|
||||||
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -1,5 +1,5 @@
|
||||||
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
|
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||||
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
|
@ -10,6 +10,8 @@ github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||||
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/clbanning/mxj"
|
"github.com/clbanning/mxj/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Request represents a webhook request.
|
// Request represents a webhook request.
|
||||||
|
|
55
vendor/github.com/clbanning/mxj/LICENSE
generated
vendored
55
vendor/github.com/clbanning/mxj/LICENSE
generated
vendored
|
@ -1,55 +0,0 @@
|
||||||
Copyright (c) 2012-2016 Charles Banning <clbanning@gmail.com>. All rights reserved.
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
===============================================================================
|
|
||||||
|
|
||||||
Go Language Copyright & License -
|
|
||||||
|
|
||||||
Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
Use of this source code is governed by a BSD-style
|
|
||||||
license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
54
vendor/github.com/clbanning/mxj/escapechars.go
generated
vendored
54
vendor/github.com/clbanning/mxj/escapechars.go
generated
vendored
|
@ -1,54 +0,0 @@
|
||||||
// Copyright 2016 Charles Banning. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file
|
|
||||||
|
|
||||||
package mxj
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
)
|
|
||||||
|
|
||||||
var xmlEscapeChars bool
|
|
||||||
|
|
||||||
// XMLEscapeChars(true) forces escaping invalid characters in attribute and element values.
|
|
||||||
// NOTE: this is brute force with NO interrogation of '&' being escaped already; if it is
|
|
||||||
// then '&' will be re-escaped as '&amp;'.
|
|
||||||
//
|
|
||||||
/*
|
|
||||||
The values are:
|
|
||||||
" "
|
|
||||||
' '
|
|
||||||
< <
|
|
||||||
> >
|
|
||||||
& &
|
|
||||||
*/
|
|
||||||
func XMLEscapeChars(b bool) {
|
|
||||||
xmlEscapeChars = b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan for '&' first, since 's' may contain "&" that is parsed to "&amp;"
|
|
||||||
// - or "<" that is parsed to "&lt;".
|
|
||||||
var escapechars = [][2][]byte{
|
|
||||||
{[]byte(`&`), []byte(`&`)},
|
|
||||||
{[]byte(`<`), []byte(`<`)},
|
|
||||||
{[]byte(`>`), []byte(`>`)},
|
|
||||||
{[]byte(`"`), []byte(`"`)},
|
|
||||||
{[]byte(`'`), []byte(`'`)},
|
|
||||||
}
|
|
||||||
|
|
||||||
func escapeChars(s string) string {
|
|
||||||
if len(s) == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
b := []byte(s)
|
|
||||||
for _, v := range escapechars {
|
|
||||||
n := bytes.Count(b, v[0])
|
|
||||||
if n == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
b = bytes.Replace(b, v[0], v[1], n)
|
|
||||||
}
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
7
vendor/github.com/clbanning/mxj/exists.go
generated
vendored
7
vendor/github.com/clbanning/mxj/exists.go
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
package mxj
|
|
||||||
|
|
||||||
// Checks whether the path exists
|
|
||||||
func (mv Map) Exists(path string, subkeys ...string) bool {
|
|
||||||
v, err := mv.ValuesForPath(path, subkeys...)
|
|
||||||
return err == nil && len(v) > 0
|
|
||||||
}
|
|
4
vendor/github.com/clbanning/mxj/v2/.travis.yml
generated
vendored
Normal file
4
vendor/github.com/clbanning/mxj/v2/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.x
|
22
vendor/github.com/clbanning/mxj/v2/LICENSE
generated
vendored
Normal file
22
vendor/github.com/clbanning/mxj/v2/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright (c) 2012-2021 Charles Banning <clbanning@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
54
vendor/github.com/clbanning/mxj/anyxml.go → vendor/github.com/clbanning/mxj/v2/anyxml.go
generated
vendored
54
vendor/github.com/clbanning/mxj/anyxml.go → vendor/github.com/clbanning/mxj/v2/anyxml.go
generated
vendored
|
@ -1,6 +1,7 @@
|
||||||
package mxj
|
package mxj
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
@ -50,6 +51,8 @@ const (
|
||||||
<element>3.14159265</element>
|
<element>3.14159265</element>
|
||||||
<element>true</element>
|
<element>true</element>
|
||||||
</mydoc>
|
</mydoc>
|
||||||
|
|
||||||
|
An extreme example is available in examples/goofy_map.go.
|
||||||
*/
|
*/
|
||||||
// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
|
// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
|
||||||
// AnyXml( v, myRootTag, myElementTag).
|
// AnyXml( v, myRootTag, myElementTag).
|
||||||
|
@ -77,40 +80,43 @@ func AnyXml(v interface{}, tags ...string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s := new(string)
|
s := new(bytes.Buffer)
|
||||||
p := new(pretty)
|
p := new(pretty)
|
||||||
|
|
||||||
var ss string
|
|
||||||
var b []byte
|
var b []byte
|
||||||
switch v.(type) {
|
switch v.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
ss = "<" + rt + ">"
|
if _, err = s.WriteString("<" + rt + ">"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
for _, vv := range v.([]interface{}) {
|
for _, vv := range v.([]interface{}) {
|
||||||
switch vv.(type) {
|
switch vv.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
m := vv.(map[string]interface{})
|
m := vv.(map[string]interface{})
|
||||||
if len(m) == 1 {
|
if len(m) == 1 {
|
||||||
for tag, val := range m {
|
for tag, val := range m {
|
||||||
err = mapToXmlIndent(false, s, tag, val, p)
|
err = marshalMapToXmlIndent(false, s, tag, val, p)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = mapToXmlIndent(false, s, et, vv, p)
|
err = marshalMapToXmlIndent(false, s, et, vv, p)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = mapToXmlIndent(false, s, et, vv, p)
|
err = marshalMapToXmlIndent(false, s, et, vv, p)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ss += *s + "</" + rt + ">"
|
if _, err = s.WriteString("</" + rt + ">"); err != nil {
|
||||||
b = []byte(ss)
|
return nil, err
|
||||||
|
}
|
||||||
|
b = s.Bytes()
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
m := Map(v.(map[string]interface{}))
|
m := Map(v.(map[string]interface{}))
|
||||||
b, err = m.Xml(rt)
|
b, err = m.Xml(rt)
|
||||||
default:
|
default:
|
||||||
err = mapToXmlIndent(false, s, rt, v, p)
|
err = marshalMapToXmlIndent(false, s, rt, v, p)
|
||||||
b = []byte(*s)
|
b = s.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
return b, err
|
return b, err
|
||||||
|
@ -143,16 +149,17 @@ func AnyXmlIndent(v interface{}, prefix, indent string, tags ...string) ([]byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s := new(string)
|
s := new(bytes.Buffer)
|
||||||
p := new(pretty)
|
p := new(pretty)
|
||||||
p.indent = indent
|
p.indent = indent
|
||||||
p.padding = prefix
|
p.padding = prefix
|
||||||
|
|
||||||
var ss string
|
|
||||||
var b []byte
|
var b []byte
|
||||||
switch v.(type) {
|
switch v.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
ss = "<" + rt + ">\n"
|
if _, err = s.WriteString("<" + rt + ">\n"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
p.Indent()
|
p.Indent()
|
||||||
for _, vv := range v.([]interface{}) {
|
for _, vv := range v.([]interface{}) {
|
||||||
switch vv.(type) {
|
switch vv.(type) {
|
||||||
|
@ -160,29 +167,34 @@ func AnyXmlIndent(v interface{}, prefix, indent string, tags ...string) ([]byte,
|
||||||
m := vv.(map[string]interface{})
|
m := vv.(map[string]interface{})
|
||||||
if len(m) == 1 {
|
if len(m) == 1 {
|
||||||
for tag, val := range m {
|
for tag, val := range m {
|
||||||
err = mapToXmlIndent(true, s, tag, val, p)
|
err = marshalMapToXmlIndent(true, s, tag, val, p)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p.start = 1 // we 1 tag in
|
p.start = 1 // we 1 tag in
|
||||||
err = mapToXmlIndent(true, s, et, vv, p)
|
err = marshalMapToXmlIndent(true, s, et, vv, p)
|
||||||
*s += "\n"
|
// *s += "\n"
|
||||||
|
if _, err = s.WriteString("\n"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
p.start = 0 // in case trailing p.start = 1
|
p.start = 0 // in case trailing p.start = 1
|
||||||
err = mapToXmlIndent(true, s, et, vv, p)
|
err = marshalMapToXmlIndent(true, s, et, vv, p)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ss += *s + "</" + rt + ">"
|
if _, err = s.WriteString(`</` + rt + `>`); err != nil {
|
||||||
b = []byte(ss)
|
return nil, err
|
||||||
|
}
|
||||||
|
b = s.Bytes()
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
m := Map(v.(map[string]interface{}))
|
m := Map(v.(map[string]interface{}))
|
||||||
b, err = m.XmlIndent(prefix, indent, rt)
|
b, err = m.XmlIndent(prefix, indent, rt)
|
||||||
default:
|
default:
|
||||||
err = mapToXmlIndent(true, s, rt, v, p)
|
err = marshalMapToXmlIndent(true, s, rt, v, p)
|
||||||
b = []byte(*s)
|
b = s.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
return b, err
|
return b, err
|
13
vendor/github.com/clbanning/mxj/doc.go → vendor/github.com/clbanning/mxj/v2/doc.go
generated
vendored
13
vendor/github.com/clbanning/mxj/doc.go → vendor/github.com/clbanning/mxj/v2/doc.go
generated
vendored
|
@ -1,6 +1,6 @@
|
||||||
// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
|
// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
|
||||||
// Copyright 2012-2015, 2018 Charles Banning. All rights reserved.
|
// Copyright 2012-2019, Charles Banning. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file
|
// license that can be found in the LICENSE file
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -14,6 +14,15 @@ Related Packages:
|
||||||
checkxml: github.com/clbanning/checkxml provides functions for validating XML data.
|
checkxml: github.com/clbanning/checkxml provides functions for validating XML data.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
2022.11.28: v2.7 - add SetGlobalKeyMapPrefix to change default prefix, '#', for default keys
|
||||||
|
2022.11.20: v2.6 - add NewMapForattedXmlSeq for XML docs formatted with whitespace character
|
||||||
|
2021.02.02: v2.5 - add XmlCheckIsValid toggle to force checking that the encoded XML is valid
|
||||||
|
2020.12.14: v2.4 - add XMLEscapeCharsDecoder to preserve XML escaped characters in Map values
|
||||||
|
2020.10.28: v2.3 - add TrimWhiteSpace option
|
||||||
|
2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
|
||||||
|
2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
|
||||||
|
2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
|
||||||
|
2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>].
|
||||||
2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
|
2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
|
||||||
2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
|
2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
|
||||||
2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
|
2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
|
93
vendor/github.com/clbanning/mxj/v2/escapechars.go
generated
vendored
Normal file
93
vendor/github.com/clbanning/mxj/v2/escapechars.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
// Copyright 2016 Charles Banning. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file
|
||||||
|
|
||||||
|
package mxj
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var xmlEscapeChars bool
|
||||||
|
|
||||||
|
// XMLEscapeChars(true) forces escaping invalid characters in attribute and element values.
|
||||||
|
// NOTE: this is brute force with NO interrogation of '&' being escaped already; if it is
|
||||||
|
// then '&' will be re-escaped as '&amp;'.
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
The values are:
|
||||||
|
" "
|
||||||
|
' '
|
||||||
|
< <
|
||||||
|
> >
|
||||||
|
& &
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Note: if XMLEscapeCharsDecoder(true) has been called - or the default, 'false,' value
|
||||||
|
// has been toggled to 'true' - then XMLEscapeChars(true) is ignored. If XMLEscapeChars(true)
|
||||||
|
// has already been called before XMLEscapeCharsDecoder(true), XMLEscapeChars(false) is called
|
||||||
|
// to turn escape encoding on mv.Xml, etc., to prevent double escaping ampersands, '&'.
|
||||||
|
func XMLEscapeChars(b ...bool) {
|
||||||
|
var bb bool
|
||||||
|
if len(b) == 0 {
|
||||||
|
bb = !xmlEscapeChars
|
||||||
|
} else {
|
||||||
|
bb = b[0]
|
||||||
|
}
|
||||||
|
if bb == true && xmlEscapeCharsDecoder == false {
|
||||||
|
xmlEscapeChars = true
|
||||||
|
} else {
|
||||||
|
xmlEscapeChars = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan for '&' first, since 's' may contain "&" that is parsed to "&amp;"
|
||||||
|
// - or "<" that is parsed to "&lt;".
|
||||||
|
var escapechars = [][2][]byte{
|
||||||
|
{[]byte(`&`), []byte(`&`)},
|
||||||
|
{[]byte(`<`), []byte(`<`)},
|
||||||
|
{[]byte(`>`), []byte(`>`)},
|
||||||
|
{[]byte(`"`), []byte(`"`)},
|
||||||
|
{[]byte(`'`), []byte(`'`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeChars(s string) string {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
b := []byte(s)
|
||||||
|
for _, v := range escapechars {
|
||||||
|
n := bytes.Count(b, v[0])
|
||||||
|
if n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b = bytes.Replace(b, v[0], v[1], n)
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// per issue #84, escape CharData values from xml.Decoder
|
||||||
|
|
||||||
|
var xmlEscapeCharsDecoder bool
|
||||||
|
|
||||||
|
// XMLEscapeCharsDecoder(b ...bool) escapes XML characters in xml.CharData values
|
||||||
|
// returned by Decoder.Token. Thus, the internal Map values will contain escaped
|
||||||
|
// values, and you do not need to set XMLEscapeChars for proper encoding.
|
||||||
|
//
|
||||||
|
// By default, the Map values have the non-escaped values returned by Decoder.Token.
|
||||||
|
// XMLEscapeCharsDecoder(true) - or, XMLEscapeCharsDecoder() - will toggle escape
|
||||||
|
// encoding 'on.'
|
||||||
|
//
|
||||||
|
// Note: if XMLEscapeCharDecoder(true) is call then XMLEscapeChars(false) is
|
||||||
|
// called to prevent re-escaping the values on encoding using mv.Xml, etc.
|
||||||
|
func XMLEscapeCharsDecoder(b ...bool) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
xmlEscapeCharsDecoder = !xmlEscapeCharsDecoder
|
||||||
|
} else {
|
||||||
|
xmlEscapeCharsDecoder = b[0]
|
||||||
|
}
|
||||||
|
if xmlEscapeCharsDecoder == true && xmlEscapeChars == true {
|
||||||
|
xmlEscapeChars = false
|
||||||
|
}
|
||||||
|
}
|
9
vendor/github.com/clbanning/mxj/v2/exists.go
generated
vendored
Normal file
9
vendor/github.com/clbanning/mxj/v2/exists.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package mxj
|
||||||
|
|
||||||
|
// Checks whether the path exists. If err != nil then 'false' is returned
|
||||||
|
// along with the error encountered parsing either the "path" or "subkeys"
|
||||||
|
// argument.
|
||||||
|
func (mv Map) Exists(path string, subkeys ...string) (bool, error) {
|
||||||
|
v, err := mv.ValuesForPath(path, subkeys...)
|
||||||
|
return (err == nil && len(v) > 0), err
|
||||||
|
}
|
3
vendor/github.com/clbanning/mxj/v2/go.mod
generated
vendored
Normal file
3
vendor/github.com/clbanning/mxj/v2/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/clbanning/mxj/v2
|
||||||
|
|
||||||
|
go 1.15
|
0
vendor/github.com/clbanning/mxj/gob.go → vendor/github.com/clbanning/mxj/v2/gob.go
generated
vendored
0
vendor/github.com/clbanning/mxj/gob.go → vendor/github.com/clbanning/mxj/v2/gob.go
generated
vendored
0
vendor/github.com/clbanning/mxj/json.go → vendor/github.com/clbanning/mxj/v2/json.go
generated
vendored
0
vendor/github.com/clbanning/mxj/json.go → vendor/github.com/clbanning/mxj/v2/json.go
generated
vendored
|
@ -21,7 +21,7 @@ const (
|
||||||
|
|
||||||
var defaultArraySize int = minArraySize
|
var defaultArraySize int = minArraySize
|
||||||
|
|
||||||
// Adjust the buffers for expected number of values to return from ValuesForKey() and ValuesForPath().
|
// SetArraySize adjust the buffers for expected number of values to return from ValuesForKey() and ValuesForPath().
|
||||||
// This can have the effect of significantly reducing memory allocation-copy functions for large data sets.
|
// This can have the effect of significantly reducing memory allocation-copy functions for large data sets.
|
||||||
// Returns the initial buffer size.
|
// Returns the initial buffer size.
|
||||||
func SetArraySize(size int) int {
|
func SetArraySize(size int) int {
|
||||||
|
@ -33,11 +33,12 @@ func SetArraySize(size int) int {
|
||||||
return defaultArraySize
|
return defaultArraySize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return all values in Map, 'mv', associated with a 'key'. If len(returned_values) == 0, then no match.
|
// ValuesForKey return all values in Map, 'mv', associated with a 'key'. If len(returned_values) == 0, then no match.
|
||||||
// On error, the returned slice is 'nil'. NOTE: 'key' can be wildcard, "*".
|
// On error, the returned slice is 'nil'. NOTE: 'key' can be wildcard, "*".
|
||||||
// 'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
|
// 'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
|
||||||
// - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
|
// - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
|
||||||
// - For attributes prefix the label with a hyphen, '-', e.g., "-seq:3".
|
// - For attributes prefix the label with the attribute prefix character, by default a
|
||||||
|
// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
|
||||||
// - If the 'key' refers to a list, then "key:value" could select a list member of the list.
|
// - If the 'key' refers to a list, then "key:value" could select a list member of the list.
|
||||||
// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
|
// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
|
||||||
// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
|
// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
|
||||||
|
@ -149,7 +150,7 @@ func hasKey(iv interface{}, key string, ret *[]interface{}, cnt *int, subkeys ma
|
||||||
// of wildcards and unindexed arrays. Embedding such logic into valuesForKeyPath() would have made the
|
// of wildcards and unindexed arrays. Embedding such logic into valuesForKeyPath() would have made the
|
||||||
// code much more complicated; this wrapper is straightforward, easy to debug, and doesn't add significant overhead.
|
// code much more complicated; this wrapper is straightforward, easy to debug, and doesn't add significant overhead.
|
||||||
|
|
||||||
// Retrieve all values for a path from the Map. If len(returned_values) == 0, then no match.
|
// ValuesForPatb retrieves all values for a path from the Map. If len(returned_values) == 0, then no match.
|
||||||
// On error, the returned array is 'nil'.
|
// On error, the returned array is 'nil'.
|
||||||
// 'path' is a dot-separated path of key values.
|
// 'path' is a dot-separated path of key values.
|
||||||
// - If a node in the path is '*', then everything beyond is walked.
|
// - If a node in the path is '*', then everything beyond is walked.
|
||||||
|
@ -157,7 +158,8 @@ func hasKey(iv interface{}, key string, ret *[]interface{}, cnt *int, subkeys ma
|
||||||
// even "*[2].*[0].field".
|
// even "*[2].*[0].field".
|
||||||
// 'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
|
// 'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
|
||||||
// - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
|
// - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
|
||||||
// - For attributes prefix the label with a hyphen, '-', e.g., "-seq:3".
|
// - For attributes prefix the label with the attribute prefix character, by default a
|
||||||
|
// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
|
||||||
// - If the 'path' refers to a list, then "tag:value" would return member of the list.
|
// - If the 'path' refers to a list, then "tag:value" would return member of the list.
|
||||||
// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
|
// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
|
||||||
// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
|
// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
|
||||||
|
@ -545,7 +547,7 @@ func getSubKeyMap(kv ...string) (map[string]interface{}, error) {
|
||||||
|
|
||||||
//----------------------------- find all paths to a key --------------------------------
|
//----------------------------- find all paths to a key --------------------------------
|
||||||
|
|
||||||
// Get all paths through Map, 'mv', (in dot-notation) that terminate with the specified key.
|
// PathsForKey returns all paths through Map, 'mv', (in dot-notation) that terminate with the specified key.
|
||||||
// Results can be used with ValuesForPath.
|
// Results can be used with ValuesForPath.
|
||||||
func (mv Map) PathsForKey(key string) []string {
|
func (mv Map) PathsForKey(key string) []string {
|
||||||
m := map[string]interface{}(mv)
|
m := map[string]interface{}(mv)
|
||||||
|
@ -568,7 +570,7 @@ func (mv Map) PathsForKey(key string) []string {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the shortest path from all possible paths - from PathsForKey() - in Map, 'mv'..
|
// PathForKeyShortest extracts the shortest path from all possible paths - from PathsForKey() - in Map, 'mv'..
|
||||||
// Paths are strings using dot-notation.
|
// Paths are strings using dot-notation.
|
||||||
func (mv Map) PathForKeyShortest(key string) string {
|
func (mv Map) PathForKeyShortest(key string) string {
|
||||||
paths := mv.PathsForKey(key)
|
paths := mv.PathsForKey(key)
|
||||||
|
@ -632,7 +634,7 @@ func hasKeyPath(crumbs string, iv interface{}, key string, basket map[string]boo
|
||||||
|
|
||||||
var PathNotExistError = errors.New("Path does not exist")
|
var PathNotExistError = errors.New("Path does not exist")
|
||||||
|
|
||||||
// ValueForPath wrap ValuesFor Path and returns the first value returned.
|
// ValueForPath wraps ValuesFor Path and returns the first value returned.
|
||||||
// If no value is found it returns 'nil' and PathNotExistError.
|
// If no value is found it returns 'nil' and PathNotExistError.
|
||||||
func (mv Map) ValueForPath(path string) (interface{}, error) {
|
func (mv Map) ValueForPath(path string) (interface{}, error) {
|
||||||
vals, err := mv.ValuesForPath(path)
|
vals, err := mv.ValuesForPath(path)
|
||||||
|
@ -645,7 +647,7 @@ func (mv Map) ValueForPath(path string) (interface{}, error) {
|
||||||
return vals[0], nil
|
return vals[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the first found value for the path as a string.
|
// ValuesForPathString returns the first found value for the path as a string.
|
||||||
func (mv Map) ValueForPathString(path string) (string, error) {
|
func (mv Map) ValueForPathString(path string) (string, error) {
|
||||||
vals, err := mv.ValuesForPath(path)
|
vals, err := mv.ValuesForPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -655,15 +657,10 @@ func (mv Map) ValueForPathString(path string) (string, error) {
|
||||||
return "", errors.New("ValueForPath: path not found")
|
return "", errors.New("ValueForPath: path not found")
|
||||||
}
|
}
|
||||||
val := vals[0]
|
val := vals[0]
|
||||||
switch str := val.(type) {
|
return fmt.Sprintf("%v", val), nil
|
||||||
case string:
|
|
||||||
return str, nil
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("ValueForPath: unsupported type: %T", str)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the first found value for the path as a string.
|
// ValueOrEmptyForPathString returns the first found value for the path as a string.
|
||||||
// If the path is not found then it returns an empty string.
|
// If the path is not found then it returns an empty string.
|
||||||
func (mv Map) ValueOrEmptyForPathString(path string) string {
|
func (mv Map) ValueOrEmptyForPathString(path string) string {
|
||||||
str, _ := mv.ValueForPathString(path)
|
str, _ := mv.ValueForPathString(path)
|
|
@ -44,7 +44,7 @@ func (mv Map) LeafNodes(no_attr ...bool) []LeafNode {
|
||||||
|
|
||||||
func getLeafNodes(path, node string, mv interface{}, l *[]LeafNode, noattr bool) {
|
func getLeafNodes(path, node string, mv interface{}, l *[]LeafNode, noattr bool) {
|
||||||
// if stripping attributes, then also strip "#text" key
|
// if stripping attributes, then also strip "#text" key
|
||||||
if !noattr || node != "#text" {
|
if !noattr || node != textK {
|
||||||
if path != "" && node[:1] != "[" {
|
if path != "" && node[:1] != "[" {
|
||||||
path += "."
|
path += "."
|
||||||
}
|
}
|
0
vendor/github.com/clbanning/mxj/misc.go → vendor/github.com/clbanning/mxj/v2/misc.go
generated
vendored
0
vendor/github.com/clbanning/mxj/misc.go → vendor/github.com/clbanning/mxj/v2/misc.go
generated
vendored
0
vendor/github.com/clbanning/mxj/mxj.go → vendor/github.com/clbanning/mxj/v2/mxj.go
generated
vendored
0
vendor/github.com/clbanning/mxj/mxj.go → vendor/github.com/clbanning/mxj/v2/mxj.go
generated
vendored
30
vendor/github.com/clbanning/mxj/readme.md → vendor/github.com/clbanning/mxj/v2/readme.md
generated
vendored
30
vendor/github.com/clbanning/mxj/readme.md → vendor/github.com/clbanning/mxj/v2/readme.md
generated
vendored
|
@ -3,10 +3,31 @@ Decode/encode XML to/from map[string]interface{} (or JSON) values, and extract/m
|
||||||
|
|
||||||
mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j and mxj/j2x packages.
|
mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j and mxj/j2x packages.
|
||||||
|
|
||||||
|
<h4>Installation</h4>
|
||||||
|
Using go.mod:
|
||||||
|
<pre>
|
||||||
|
go get github.com/clbanning/mxj/v2@v2.7
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
import "github.com/clbanning/mxj/v2"
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
... or just vendor the package.
|
||||||
|
|
||||||
<h4>Related Packages</h4>
|
<h4>Related Packages</h4>
|
||||||
|
|
||||||
https://github.com/clbanning/checkxml provides functions for validating XML data.
|
https://github.com/clbanning/checkxml provides functions for validating XML data.
|
||||||
|
|
||||||
|
<h4>Refactor Encoder - 2020.05.01</h4>
|
||||||
|
Issue #70 highlighted that encoding large maps does not scale well, since the original logic used string appends operations. Using bytes.Buffer results in linear scaling for very large XML docs. (Metrics based on MacBook Pro i7 w/ 16 GB.)
|
||||||
|
|
||||||
|
Nodes m.XML() time
|
||||||
|
54809 12.53708ms
|
||||||
|
109780 32.403183ms
|
||||||
|
164678 59.826412ms
|
||||||
|
482598 109.358007ms
|
||||||
|
|
||||||
<h4>Refactor Decoder - 2015.11.15</h4>
|
<h4>Refactor Decoder - 2015.11.15</h4>
|
||||||
For over a year I've wanted to refactor the XML-to-map[string]interface{} decoder to make it more performant. I recently took the time to do that, since we were using github.com/clbanning/mxj in a production system that could be deployed on a Raspberry Pi. Now the decoder is comparable to the stdlib JSON-to-map[string]interface{} decoder in terms of its additional processing overhead relative to decoding to a structure value. As shown by:
|
For over a year I've wanted to refactor the XML-to-map[string]interface{} decoder to make it more performant. I recently took the time to do that, since we were using github.com/clbanning/mxj in a production system that could be deployed on a Raspberry Pi. Now the decoder is comparable to the stdlib JSON-to-map[string]interface{} decoder in terms of its additional processing overhead relative to decoding to a structure value. As shown by:
|
||||||
|
|
||||||
|
@ -21,6 +42,15 @@ For over a year I've wanted to refactor the XML-to-map[string]interface{} decode
|
||||||
|
|
||||||
<h4>Notices</h4>
|
<h4>Notices</h4>
|
||||||
|
|
||||||
|
2022.11.28: v2.7 - add SetGlobalKeyMapPrefix to change default prefix, '#', for default keys
|
||||||
|
2022.11.20: v2.6 - add NewMapForattedXmlSeq for XML docs formatted with whitespace character
|
||||||
|
2021.02.02: v2.5 - add XmlCheckIsValid toggle to force checking that the encoded XML is valid
|
||||||
|
2020.12.14: v2.4 - add XMLEscapeCharsDecoder to preserve XML escaped characters in Map values
|
||||||
|
2020.10.28: v2.3 - add TrimWhiteSpace option
|
||||||
|
2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
|
||||||
|
2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
|
||||||
|
2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
|
||||||
|
2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>]
|
||||||
2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
|
2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
|
||||||
2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
|
2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
|
||||||
2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
|
2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
|
13
vendor/github.com/clbanning/mxj/rename.go → vendor/github.com/clbanning/mxj/v2/rename.go
generated
vendored
13
vendor/github.com/clbanning/mxj/rename.go → vendor/github.com/clbanning/mxj/v2/rename.go
generated
vendored
|
@ -6,13 +6,20 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// RenameKey renames a key in a Map.
|
// RenameKey renames a key in a Map.
|
||||||
// It works only for nested maps. It doesn't work for cases when it buried in a list.
|
// It works only for nested maps.
|
||||||
|
// It doesn't work for cases when the key is in a list.
|
||||||
func (mv Map) RenameKey(path string, newName string) error {
|
func (mv Map) RenameKey(path string, newName string) error {
|
||||||
if !mv.Exists(path) {
|
var v bool
|
||||||
|
var err error
|
||||||
|
if v, err = mv.Exists(path); err == nil && !v {
|
||||||
return errors.New("RenameKey: path not found: " + path)
|
return errors.New("RenameKey: path not found: " + path)
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if mv.Exists(parentPath(path) + "." + newName) {
|
if v, err = mv.Exists(parentPath(path) + "." + newName); err == nil && v {
|
||||||
return errors.New("RenameKey: key already exists: " + newName)
|
return errors.New("RenameKey: key already exists: " + newName)
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m := map[string]interface{}(mv)
|
m := map[string]interface{}(mv)
|
0
vendor/github.com/clbanning/mxj/set.go → vendor/github.com/clbanning/mxj/v2/set.go
generated
vendored
0
vendor/github.com/clbanning/mxj/set.go → vendor/github.com/clbanning/mxj/v2/set.go
generated
vendored
|
@ -21,9 +21,11 @@ import (
|
||||||
// 'path' is dot-notation list of keys to traverse; last key in path can be newVal key
|
// 'path' is dot-notation list of keys to traverse; last key in path can be newVal key
|
||||||
// NOTE: 'path' spec does not currently support indexed array references.
|
// NOTE: 'path' spec does not currently support indexed array references.
|
||||||
// 'subkeys' are "key:value[:type]" entries that must match for path node
|
// 'subkeys' are "key:value[:type]" entries that must match for path node
|
||||||
// The subkey can be wildcarded - "key:*" - to require that it's there with some value.
|
// - For attributes prefix the label with the attribute prefix character, by default a
|
||||||
// If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
|
// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
|
||||||
// exclusion critera - e.g., "!author:William T. Gaddis".
|
// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
|
||||||
|
// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
|
||||||
|
// exclusion critera - e.g., "!author:William T. Gaddis".
|
||||||
//
|
//
|
||||||
// NOTES:
|
// NOTES:
|
||||||
// 1. Simple elements with attributes need a path terminated as ".#text" to modify the actual value.
|
// 1. Simple elements with attributes need a path terminated as ".#text" to modify the actual value.
|
455
vendor/github.com/clbanning/mxj/xml.go → vendor/github.com/clbanning/mxj/v2/xml.go
generated
vendored
455
vendor/github.com/clbanning/mxj/xml.go → vendor/github.com/clbanning/mxj/v2/xml.go
generated
vendored
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2012-2016 Charles Banning. All rights reserved.
|
// Copyright 2012-2016, 2018-2019 Charles Banning. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file
|
// license that can be found in the LICENSE file
|
||||||
|
|
||||||
|
@ -22,6 +22,30 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
textK = "#text"
|
||||||
|
seqK = "#seq"
|
||||||
|
commentK = "#comment"
|
||||||
|
attrK = "#attr"
|
||||||
|
directiveK = "#directive"
|
||||||
|
procinstK = "#procinst"
|
||||||
|
targetK = "#target"
|
||||||
|
instK = "#inst"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Support overriding default Map keys prefix
|
||||||
|
|
||||||
|
func SetGlobalKeyMapPrefix(s string) {
|
||||||
|
textK = strings.ReplaceAll(textK, textK[0:1], s)
|
||||||
|
seqK = strings.ReplaceAll(seqK, seqK[0:1], s)
|
||||||
|
commentK = strings.ReplaceAll(commentK, commentK[0:1], s)
|
||||||
|
directiveK = strings.ReplaceAll(directiveK, directiveK[0:1], s)
|
||||||
|
procinstK = strings.ReplaceAll(procinstK, procinstK[0:1], s)
|
||||||
|
targetK = strings.ReplaceAll(targetK, targetK[0:1], s)
|
||||||
|
instK = strings.ReplaceAll(instK, instK[0:1], s)
|
||||||
|
attrK = strings.ReplaceAll(attrK, attrK[0:1], s)
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------- NewMapXml & NewMapXmlReader ... -------------------------
|
// ------------------- NewMapXml & NewMapXmlReader ... -------------------------
|
||||||
|
|
||||||
// If XmlCharsetReader != nil, it will be used to decode the XML, if required.
|
// If XmlCharsetReader != nil, it will be used to decode the XML, if required.
|
||||||
|
@ -52,10 +76,12 @@ var XmlCharsetReader func(charset string, input io.Reader) (io.Reader, error)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// NOTES:
|
// NOTES:
|
||||||
// 1. The 'xmlVal' will be parsed looking for an xml.StartElement, so BOM and other
|
// 1. Declarations, directives, process instructions and comments are NOT parsed.
|
||||||
|
// 2. The 'xmlVal' will be parsed looking for an xml.StartElement, so BOM and other
|
||||||
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
|
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
|
||||||
// 2. If CoerceKeysToLower() has been called, then all key values will be lower case.
|
// 3. If CoerceKeysToLower() has been called, then all key values will be lower case.
|
||||||
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
// 4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
||||||
|
// 5. If DisableTrimWhiteSpace(b bool) has been called, then all values will be trimmed or not. 'true' by default.
|
||||||
func NewMapXml(xmlVal []byte, cast ...bool) (Map, error) {
|
func NewMapXml(xmlVal []byte, cast ...bool) (Map, error) {
|
||||||
var r bool
|
var r bool
|
||||||
if len(cast) == 1 {
|
if len(cast) == 1 {
|
||||||
|
@ -66,10 +92,11 @@ func NewMapXml(xmlVal []byte, cast ...bool) (Map, error) {
|
||||||
|
|
||||||
// Get next XML doc from an io.Reader as a Map value. Returns Map value.
|
// Get next XML doc from an io.Reader as a Map value. Returns Map value.
|
||||||
// NOTES:
|
// NOTES:
|
||||||
// 1. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
|
// 1. Declarations, directives, process instructions and comments are NOT parsed.
|
||||||
|
// 2. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
|
||||||
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
|
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
|
||||||
// 2. If CoerceKeysToLower() has been called, then all key values will be lower case.
|
// 3. If CoerceKeysToLower() has been called, then all key values will be lower case.
|
||||||
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
// 4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
||||||
func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error) {
|
func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error) {
|
||||||
var r bool
|
var r bool
|
||||||
if len(cast) == 1 {
|
if len(cast) == 1 {
|
||||||
|
@ -89,16 +116,17 @@ func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error) {
|
||||||
|
|
||||||
// Get next XML doc from an io.Reader as a Map value. Returns Map value and slice with the raw XML.
|
// Get next XML doc from an io.Reader as a Map value. Returns Map value and slice with the raw XML.
|
||||||
// NOTES:
|
// NOTES:
|
||||||
// 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
|
// 1. Declarations, directives, process instructions and comments are NOT parsed.
|
||||||
|
// 2. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
|
||||||
// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
|
// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
|
||||||
// See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
|
// See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
|
||||||
// data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
|
// data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
|
||||||
// you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
|
// you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
|
||||||
// 2. The 'raw' return value may be larger than the XML text value.
|
// 3. The 'raw' return value may be larger than the XML text value.
|
||||||
// 3. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
|
// 4. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
|
||||||
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
|
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
|
||||||
// 4. If CoerceKeysToLower() has been called, then all key values will be lower case.
|
// 5. If CoerceKeysToLower() has been called, then all key values will be lower case.
|
||||||
// 5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
// 6. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
||||||
func NewMapXmlReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
|
func NewMapXmlReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
|
||||||
var r bool
|
var r bool
|
||||||
if len(cast) == 1 {
|
if len(cast) == 1 {
|
||||||
|
@ -205,8 +233,12 @@ var includeTagSeqNum bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
func IncludeTagSeqNum(b bool) {
|
func IncludeTagSeqNum(b ...bool) {
|
||||||
includeTagSeqNum = b
|
if len(b) == 0 {
|
||||||
|
includeTagSeqNum = !includeTagSeqNum
|
||||||
|
} else if len(b) == 1 {
|
||||||
|
includeTagSeqNum = b[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// all keys will be "lower case"
|
// all keys will be "lower case"
|
||||||
|
@ -228,6 +260,26 @@ func CoerceKeysToLower(b ...bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disableTrimWhiteSpace sets if the white space should be removed or not
|
||||||
|
var disableTrimWhiteSpace bool
|
||||||
|
var trimRunes = "\t\r\b\n "
|
||||||
|
|
||||||
|
// DisableTrimWhiteSpace set if the white space should be trimmed or not. By default white space is always trimmed. If
|
||||||
|
// no argument is provided, trim white space will be disabled.
|
||||||
|
func DisableTrimWhiteSpace(b ...bool) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
disableTrimWhiteSpace = true
|
||||||
|
} else {
|
||||||
|
disableTrimWhiteSpace = b[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if disableTrimWhiteSpace {
|
||||||
|
trimRunes = "\t\r\b\n"
|
||||||
|
} else {
|
||||||
|
trimRunes = "\t\r\b\n "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 25jun16: Allow user to specify the "prefix" character for XML attribute key labels.
|
// 25jun16: Allow user to specify the "prefix" character for XML attribute key labels.
|
||||||
// We do this by replacing '`' constant with attrPrefix var, replacing useHyphen with attrPrefix = "",
|
// We do this by replacing '`' constant with attrPrefix var, replacing useHyphen with attrPrefix = "",
|
||||||
// and adding a SetAttrPrefix(s string) function.
|
// and adding a SetAttrPrefix(s string) function.
|
||||||
|
@ -258,6 +310,21 @@ func CoerceKeysToSnakeCase(b ...bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 10jan19: use of pull request #57 should be conditional - legacy code assumes
|
||||||
|
// numeric values are float64.
|
||||||
|
var castToInt bool
|
||||||
|
|
||||||
|
// CastValuesToInt tries to coerce numeric valus to int64 or uint64 instead of the
|
||||||
|
// default float64. Repeated calls with no argument will toggle this on/off, or this
|
||||||
|
// handling will be set with the value of 'b'.
|
||||||
|
func CastValuesToInt(b ...bool) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
castToInt = !castToInt
|
||||||
|
} else if len(b) == 1 {
|
||||||
|
castToInt = b[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 05feb17: support processing XMPP streams (issue #36)
|
// 05feb17: support processing XMPP streams (issue #36)
|
||||||
var handleXMPPStreamTag bool
|
var handleXMPPStreamTag bool
|
||||||
|
|
||||||
|
@ -329,7 +396,10 @@ func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[stri
|
||||||
if lowerCase {
|
if lowerCase {
|
||||||
key = strings.ToLower(key)
|
key = strings.ToLower(key)
|
||||||
}
|
}
|
||||||
na[key] = cast(v.Value, r)
|
if xmlEscapeCharsDecoder { // per issue#84
|
||||||
|
v.Value = escapeChars(v.Value)
|
||||||
|
}
|
||||||
|
na[key] = cast(v.Value, r, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,7 +465,7 @@ func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[stri
|
||||||
val.(map[string]interface{})["_seq"] = seq // will overwrite an "_seq" XML tag
|
val.(map[string]interface{})["_seq"] = seq // will overwrite an "_seq" XML tag
|
||||||
seq++
|
seq++
|
||||||
case interface{}: // a non-nil simple element: string, float64, bool
|
case interface{}: // a non-nil simple element: string, float64, bool
|
||||||
v := map[string]interface{}{"#text": val}
|
v := map[string]interface{}{textK: val}
|
||||||
v["_seq"] = seq
|
v["_seq"] = seq
|
||||||
seq++
|
seq++
|
||||||
val = v
|
val = v
|
||||||
|
@ -430,16 +500,25 @@ func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[stri
|
||||||
} else {
|
} else {
|
||||||
n[skey] = "" // empty element
|
n[skey] = "" // empty element
|
||||||
}
|
}
|
||||||
|
} else if len(n) == 1 && len(na) > 0 {
|
||||||
|
// it's a simple element w/ no attributes w/ subelements
|
||||||
|
for _, v := range n {
|
||||||
|
na[textK] = v
|
||||||
|
}
|
||||||
|
n[skey] = na
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
case xml.CharData:
|
case xml.CharData:
|
||||||
// clean up possible noise
|
// clean up possible noise
|
||||||
tt := strings.Trim(string(t.(xml.CharData)), "\t\r\b\n ")
|
tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
|
||||||
|
if xmlEscapeCharsDecoder { // issue#84
|
||||||
|
tt = escapeChars(tt)
|
||||||
|
}
|
||||||
if len(tt) > 0 {
|
if len(tt) > 0 {
|
||||||
if len(na) > 0 || decodeSimpleValuesAsMap {
|
if len(na) > 0 || decodeSimpleValuesAsMap {
|
||||||
na["#text"] = cast(tt, r)
|
na[textK] = cast(tt, r, textK)
|
||||||
} else if skey != "" {
|
} else if skey != "" {
|
||||||
n[skey] = cast(tt, r)
|
n[skey] = cast(tt, r, skey)
|
||||||
} else {
|
} else {
|
||||||
// per Adrian (http://www.adrianlungu.com/) catch stray text
|
// per Adrian (http://www.adrianlungu.com/) catch stray text
|
||||||
// in decoder stream -
|
// in decoder stream -
|
||||||
|
@ -459,12 +538,23 @@ var castNanInf bool
|
||||||
|
|
||||||
// Cast "Nan", "Inf", "-Inf" XML values to 'float64'.
|
// Cast "Nan", "Inf", "-Inf" XML values to 'float64'.
|
||||||
// By default, these values will be decoded as 'string'.
|
// By default, these values will be decoded as 'string'.
|
||||||
func CastNanInf(b bool) {
|
func CastNanInf(b ...bool) {
|
||||||
castNanInf = b
|
if len(b) == 0 {
|
||||||
|
castNanInf = !castNanInf
|
||||||
|
} else if len(b) == 1 {
|
||||||
|
castNanInf = b[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cast - try to cast string values to bool or float64
|
// cast - try to cast string values to bool or float64
|
||||||
func cast(s string, r bool) interface{} {
|
// 't' is the tag key that can be checked for 'not-casting'
|
||||||
|
func cast(s string, r bool, t string) interface{} {
|
||||||
|
if checkTagToSkip != nil && t != "" && checkTagToSkip(t) {
|
||||||
|
// call the check-function here with 't[0]'
|
||||||
|
// if 'true' return s
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
if r {
|
if r {
|
||||||
// handle nan and inf
|
// handle nan and inf
|
||||||
if !castNanInf {
|
if !castNanInf {
|
||||||
|
@ -475,17 +565,31 @@ func cast(s string, r bool) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle numeric strings ahead of boolean
|
// handle numeric strings ahead of boolean
|
||||||
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
if castToInt {
|
||||||
return f
|
if f, err := strconv.ParseInt(s, 10, 64); err == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
if f, err := strconv.ParseUint(s, 10, 64); err == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if castToFloat {
|
||||||
|
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ParseBool treats "1"==true & "0"==false, we've already scanned those
|
// ParseBool treats "1"==true & "0"==false, we've already scanned those
|
||||||
// values as float64. See if value has 't' or 'f' as initial screen to
|
// values as float64. See if value has 't' or 'f' as initial screen to
|
||||||
// minimize calls to ParseBool; also, see if len(s) < 6.
|
// minimize calls to ParseBool; also, see if len(s) < 6.
|
||||||
if len(s) > 0 && len(s) < 6 {
|
if castToBool {
|
||||||
switch s[:1] {
|
if len(s) > 0 && len(s) < 6 {
|
||||||
case "t", "T", "f", "F":
|
switch s[:1] {
|
||||||
if b, err := strconv.ParseBool(s); err == nil {
|
case "t", "T", "f", "F":
|
||||||
return b
|
if b, err := strconv.ParseBool(s); err == nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,6 +597,47 @@ func cast(s string, r bool) interface{} {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pull request, #59
|
||||||
|
var castToFloat = true
|
||||||
|
|
||||||
|
// CastValuesToFloat can be used to skip casting to float64 when
|
||||||
|
// "cast" argument is 'true' in NewMapXml, etc.
|
||||||
|
// Default is true.
|
||||||
|
func CastValuesToFloat(b ...bool) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
castToFloat = !castToFloat
|
||||||
|
} else if len(b) == 1 {
|
||||||
|
castToFloat = b[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var castToBool = true
|
||||||
|
|
||||||
|
// CastValuesToBool can be used to skip casting to bool when
|
||||||
|
// "cast" argument is 'true' in NewMapXml, etc.
|
||||||
|
// Default is true.
|
||||||
|
func CastValuesToBool(b ...bool) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
castToBool = !castToBool
|
||||||
|
} else if len(b) == 1 {
|
||||||
|
castToBool = b[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkTagToSkip - switch to address Issue #58
|
||||||
|
|
||||||
|
var checkTagToSkip func(string) bool
|
||||||
|
|
||||||
|
// SetCheckTagToSkipFunc registers function to test whether the value
|
||||||
|
// for a tag should be cast to bool or float64 when "cast" argument is 'true'.
|
||||||
|
// (Dot tag path notation is not supported.)
|
||||||
|
// NOTE: key may be "#text" if it's a simple element with attributes
|
||||||
|
// or "decodeSimpleValuesAsMap == true".
|
||||||
|
// NOTE: does not apply to NewMapXmlSeq... functions.
|
||||||
|
func SetCheckTagToSkipFunc(fn func(string) bool) {
|
||||||
|
checkTagToSkip = fn
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
|
// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
|
||||||
|
|
||||||
// ------------------ mv.Xml & mv.XmlWriter - from j2x ------------------------
|
// ------------------ mv.Xml & mv.XmlWriter - from j2x ------------------------
|
||||||
|
@ -518,6 +663,20 @@ func XmlDefaultEmptyElemSyntax() {
|
||||||
useGoXmlEmptyElemSyntax = false
|
useGoXmlEmptyElemSyntax = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------- issue #88 ----------
|
||||||
|
// xmlCheckIsValid set switch to force decoding the encoded XML to
|
||||||
|
// see if it is valid XML.
|
||||||
|
var xmlCheckIsValid bool
|
||||||
|
|
||||||
|
// XmlCheckIsValid forces the encoded XML to be checked for validity.
|
||||||
|
func XmlCheckIsValid(b ...bool) {
|
||||||
|
if len(b) == 1 {
|
||||||
|
xmlCheckIsValid = b[0]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xmlCheckIsValid = !xmlCheckIsValid
|
||||||
|
}
|
||||||
|
|
||||||
// Encode a Map as XML. The companion of NewMapXml().
|
// Encode a Map as XML. The companion of NewMapXml().
|
||||||
// The following rules apply.
|
// The following rules apply.
|
||||||
// - The key label "#text" is treated as the value for a simple element with attributes.
|
// - The key label "#text" is treated as the value for a simple element with attributes.
|
||||||
|
@ -537,7 +696,7 @@ func XmlDefaultEmptyElemSyntax() {
|
||||||
func (mv Map) Xml(rootTag ...string) ([]byte, error) {
|
func (mv Map) Xml(rootTag ...string) ([]byte, error) {
|
||||||
m := map[string]interface{}(mv)
|
m := map[string]interface{}(mv)
|
||||||
var err error
|
var err error
|
||||||
s := new(string)
|
b := new(bytes.Buffer)
|
||||||
p := new(pretty) // just a stub
|
p := new(pretty) // just a stub
|
||||||
|
|
||||||
if len(m) == 1 && len(rootTag) == 0 {
|
if len(m) == 1 && len(rootTag) == 0 {
|
||||||
|
@ -551,20 +710,32 @@ func (mv Map) Xml(rootTag ...string) ([]byte, error) {
|
||||||
switch v.(type) {
|
switch v.(type) {
|
||||||
case map[string]interface{}: // noop
|
case map[string]interface{}: // noop
|
||||||
default: // anything else
|
default: // anything else
|
||||||
err = mapToXmlIndent(false, s, DefaultRootTag, m, p)
|
err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
|
||||||
goto done
|
goto done
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = mapToXmlIndent(false, s, key, value, p)
|
err = marshalMapToXmlIndent(false, b, key, value, p)
|
||||||
}
|
}
|
||||||
} else if len(rootTag) == 1 {
|
} else if len(rootTag) == 1 {
|
||||||
err = mapToXmlIndent(false, s, rootTag[0], m, p)
|
err = marshalMapToXmlIndent(false, b, rootTag[0], m, p)
|
||||||
} else {
|
} else {
|
||||||
err = mapToXmlIndent(false, s, DefaultRootTag, m, p)
|
err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
return []byte(*s), err
|
if xmlCheckIsValid {
|
||||||
|
d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
|
||||||
|
for {
|
||||||
|
_, err = d.Token()
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.Bytes(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
|
// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
|
||||||
|
@ -584,6 +755,7 @@ func (mv Map) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
|
||||||
|
|
||||||
// Writes the Map as XML on the Writer. []byte is the raw XML that was written.
|
// Writes the Map as XML on the Writer. []byte is the raw XML that was written.
|
||||||
// See Xml() for encoding rules.
|
// See Xml() for encoding rules.
|
||||||
|
/*
|
||||||
func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
|
func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
|
||||||
x, err := mv.Xml(rootTag...)
|
x, err := mv.Xml(rootTag...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -593,6 +765,7 @@ func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, erro
|
||||||
_, err = xmlWriter.Write(x)
|
_, err = xmlWriter.Write(x)
|
||||||
return x, err
|
return x, err
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Writes the Map as pretty XML on the Writer.
|
// Writes the Map as pretty XML on the Writer.
|
||||||
// See Xml() for encoding rules.
|
// See Xml() for encoding rules.
|
||||||
|
@ -608,6 +781,7 @@ func (mv Map) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTa
|
||||||
|
|
||||||
// Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
|
// Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
|
||||||
// See Xml() for encoding rules.
|
// See Xml() for encoding rules.
|
||||||
|
/*
|
||||||
func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
|
func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
|
||||||
x, err := mv.XmlIndent(prefix, indent, rootTag...)
|
x, err := mv.XmlIndent(prefix, indent, rootTag...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -617,6 +791,7 @@ func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, roo
|
||||||
_, err = xmlWriter.Write(x)
|
_, err = xmlWriter.Write(x)
|
||||||
return x, err
|
return x, err
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
|
// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
|
||||||
|
|
||||||
|
@ -762,6 +937,7 @@ func (b *byteReader) Read(p []byte) (int, error) {
|
||||||
func (b *byteReader) ReadByte() (byte, error) {
|
func (b *byteReader) ReadByte() (byte, error) {
|
||||||
_, err := b.r.Read(b.b)
|
_, err := b.r.Read(b.b)
|
||||||
if len(b.b) > 0 {
|
if len(b.b) > 0 {
|
||||||
|
// issue #38
|
||||||
return b.b[0], err
|
return b.b[0], err
|
||||||
}
|
}
|
||||||
var c byte
|
var c byte
|
||||||
|
@ -778,7 +954,7 @@ func (mv Map) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error
|
||||||
m := map[string]interface{}(mv)
|
m := map[string]interface{}(mv)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s := new(string)
|
b := new(bytes.Buffer)
|
||||||
p := new(pretty)
|
p := new(pretty)
|
||||||
p.indent = indent
|
p.indent = indent
|
||||||
p.padding = prefix
|
p.padding = prefix
|
||||||
|
@ -788,17 +964,29 @@ func (mv Map) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error
|
||||||
// use it if it isn't a key for a list
|
// use it if it isn't a key for a list
|
||||||
for key, value := range m {
|
for key, value := range m {
|
||||||
if _, ok := value.([]interface{}); ok {
|
if _, ok := value.([]interface{}); ok {
|
||||||
err = mapToXmlIndent(true, s, DefaultRootTag, m, p)
|
err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
|
||||||
} else {
|
} else {
|
||||||
err = mapToXmlIndent(true, s, key, value, p)
|
err = marshalMapToXmlIndent(true, b, key, value, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if len(rootTag) == 1 {
|
} else if len(rootTag) == 1 {
|
||||||
err = mapToXmlIndent(true, s, rootTag[0], m, p)
|
err = marshalMapToXmlIndent(true, b, rootTag[0], m, p)
|
||||||
} else {
|
} else {
|
||||||
err = mapToXmlIndent(true, s, DefaultRootTag, m, p)
|
err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
|
||||||
}
|
}
|
||||||
return []byte(*s), err
|
if xmlCheckIsValid {
|
||||||
|
d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
|
||||||
|
for {
|
||||||
|
_, err = d.Token()
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.Bytes(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
type pretty struct {
|
type pretty struct {
|
||||||
|
@ -823,7 +1011,9 @@ func (p *pretty) Outdent() {
|
||||||
|
|
||||||
// where the work actually happens
|
// where the work actually happens
|
||||||
// returns an error if an attribute is not atomic
|
// returns an error if an attribute is not atomic
|
||||||
func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp *pretty) error {
|
// NOTE: 01may20 - replaces mapToXmlIndent(); uses bytes.Buffer instead for string appends.
|
||||||
|
func marshalMapToXmlIndent(doIndent bool, b *bytes.Buffer, key string, value interface{}, pp *pretty) error {
|
||||||
|
var err error
|
||||||
var endTag bool
|
var endTag bool
|
||||||
var isSimple bool
|
var isSimple bool
|
||||||
var elen int
|
var elen int
|
||||||
|
@ -845,14 +1035,49 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 14jul20. The following block of code has become something of a catch all for odd stuff
|
||||||
|
// that might be passed in as a result of casting an arbitrary map[<T>]<T> to an mxj.Map
|
||||||
|
// value and then call m.Xml or m.XmlIndent. See issue #71 (and #73) for such edge cases.
|
||||||
switch value.(type) {
|
switch value.(type) {
|
||||||
// special handling of []interface{} values when len(value) == 0
|
// these types are handled during encoding
|
||||||
case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32, json.Number:
|
case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32, json.Number:
|
||||||
if doIndent {
|
case []map[string]interface{}, []string, []float64, []bool, []int, []int32, []int64, []float32, []json.Number:
|
||||||
*s += p.padding
|
case []interface{}:
|
||||||
|
case nil:
|
||||||
|
value = ""
|
||||||
|
default:
|
||||||
|
// see if value is a struct, if so marshal using encoding/xml package
|
||||||
|
if reflect.ValueOf(value).Kind() == reflect.Struct {
|
||||||
|
if v, err := xml.Marshal(value); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
value = string(v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// coerce eveything else into a string value
|
||||||
|
value = fmt.Sprint(value)
|
||||||
}
|
}
|
||||||
*s += `<` + key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start the XML tag with required indentaton and padding
|
||||||
|
if doIndent {
|
||||||
|
switch value.(type) {
|
||||||
|
case []interface{}, []string:
|
||||||
|
// list processing handles indentation for all elements
|
||||||
|
default:
|
||||||
|
if _, err = b.WriteString(p.padding); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch value.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
default:
|
||||||
|
if _, err = b.WriteString(`<` + key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch value.(type) {
|
switch value.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
vv := value.(map[string]interface{})
|
vv := value.(map[string]interface{})
|
||||||
|
@ -893,21 +1118,29 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
attrlist = attrlist[:n]
|
attrlist = attrlist[:n]
|
||||||
sort.Sort(attrList(attrlist))
|
sort.Sort(attrList(attrlist))
|
||||||
for _, v := range attrlist {
|
for _, v := range attrlist {
|
||||||
*s += ` ` + v[0] + `="` + v[1] + `"`
|
if _, err = b.WriteString(` ` + v[0] + `="` + v[1] + `"`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// only attributes?
|
// only attributes?
|
||||||
if n == lenvv {
|
if n == lenvv {
|
||||||
if useGoXmlEmptyElemSyntax {
|
if useGoXmlEmptyElemSyntax {
|
||||||
*s += `</` + key + ">"
|
if _, err = b.WriteString(`</` + key + ">"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*s += `/>`
|
if _, err = b.WriteString(`/>`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// simple element? Note: '#text" is an invalid XML tag.
|
// simple element? Note: '#text" is an invalid XML tag.
|
||||||
if v, ok := vv["#text"]; ok && n+1 == lenvv {
|
isComplex := false
|
||||||
|
if v, ok := vv[textK]; ok && n+1 == lenvv {
|
||||||
|
// just the value and attributes
|
||||||
switch v.(type) {
|
switch v.(type) {
|
||||||
case string:
|
case string:
|
||||||
if xmlEscapeChars {
|
if xmlEscapeChars {
|
||||||
|
@ -918,25 +1151,51 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
case []byte:
|
case []byte:
|
||||||
if xmlEscapeChars {
|
if xmlEscapeChars {
|
||||||
v = escapeChars(string(v.([]byte)))
|
v = escapeChars(string(v.([]byte)))
|
||||||
|
} else {
|
||||||
|
v = string(v.([]byte))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*s += ">" + fmt.Sprintf("%v", v)
|
if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
endTag = true
|
endTag = true
|
||||||
elen = 1
|
elen = 1
|
||||||
isSimple = true
|
isSimple = true
|
||||||
break
|
break
|
||||||
} else if ok {
|
} else if ok {
|
||||||
// Handle edge case where simple element with attributes
|
// need to handle when there are subelements in addition to the simple element value
|
||||||
// is unmarshal'd using NewMapXml() where attribute prefix
|
// issue #90
|
||||||
// has been set to "".
|
switch v.(type) {
|
||||||
// TODO(clb): should probably scan all keys for invalid chars.
|
case string:
|
||||||
return fmt.Errorf("invalid attribute key label: #text - due to attributes not being prefixed")
|
if xmlEscapeChars {
|
||||||
|
v = escapeChars(v.(string))
|
||||||
|
} else {
|
||||||
|
v = v.(string)
|
||||||
|
}
|
||||||
|
case []byte:
|
||||||
|
if xmlEscapeChars {
|
||||||
|
v = escapeChars(string(v.([]byte)))
|
||||||
|
} else {
|
||||||
|
v = string(v.([]byte))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
isComplex = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// close tag with possible attributes
|
// close tag with possible attributes
|
||||||
*s += ">"
|
if !isComplex {
|
||||||
|
if _, err = b.WriteString(">"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
if doIndent {
|
if doIndent {
|
||||||
*s += "\n"
|
// *s += "\n"
|
||||||
|
if _, err = b.WriteString("\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// something more complex
|
// something more complex
|
||||||
p.mapDepth++
|
p.mapDepth++
|
||||||
|
@ -944,6 +1203,10 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
elemlist := make([][2]interface{}, len(vv))
|
elemlist := make([][2]interface{}, len(vv))
|
||||||
n = 0
|
n = 0
|
||||||
for k, v := range vv {
|
for k, v := range vv {
|
||||||
|
if k == textK {
|
||||||
|
// simple element handled above
|
||||||
|
continue
|
||||||
|
}
|
||||||
if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
|
if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -963,7 +1226,7 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
if err := mapToXmlIndent(doIndent, s, v[0].(string), v[1], p); err != nil {
|
if err := marshalMapToXmlIndent(doIndent, b, v[0].(string), v[1], p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switch v[1].(type) {
|
switch v[1].(type) {
|
||||||
|
@ -982,9 +1245,13 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
// special case - found during implementing Issue #23
|
// special case - found during implementing Issue #23
|
||||||
if len(value.([]interface{})) == 0 {
|
if len(value.([]interface{})) == 0 {
|
||||||
if doIndent {
|
if doIndent {
|
||||||
*s += p.padding + p.indent
|
if _, err = b.WriteString(p.padding + p.indent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err = b.WriteString("<" + key); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
*s += "<" + key
|
|
||||||
elen = 0
|
elen = 0
|
||||||
endTag = true
|
endTag = true
|
||||||
break
|
break
|
||||||
|
@ -993,7 +1260,7 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
if doIndent {
|
if doIndent {
|
||||||
p.Indent()
|
p.Indent()
|
||||||
}
|
}
|
||||||
if err := mapToXmlIndent(doIndent, s, key, v, p); err != nil {
|
if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if doIndent {
|
if doIndent {
|
||||||
|
@ -1010,9 +1277,13 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
//[]string should be treated exaclty as []interface{}
|
//[]string should be treated exaclty as []interface{}
|
||||||
if len(value.([]string)) == 0 {
|
if len(value.([]string)) == 0 {
|
||||||
if doIndent {
|
if doIndent {
|
||||||
*s += p.padding + p.indent
|
if _, err = b.WriteString(p.padding + p.indent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err = b.WriteString("<" + key); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
*s += "<" + key
|
|
||||||
elen = 0
|
elen = 0
|
||||||
endTag = true
|
endTag = true
|
||||||
break
|
break
|
||||||
|
@ -1021,7 +1292,7 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
if doIndent {
|
if doIndent {
|
||||||
p.Indent()
|
p.Indent()
|
||||||
}
|
}
|
||||||
if err := mapToXmlIndent(doIndent, s, key, v, p); err != nil {
|
if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if doIndent {
|
if doIndent {
|
||||||
|
@ -1032,9 +1303,14 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
case nil:
|
case nil:
|
||||||
// terminate the tag
|
// terminate the tag
|
||||||
if doIndent {
|
if doIndent {
|
||||||
*s += p.padding
|
// *s += p.padding
|
||||||
|
if _, err = b.WriteString(p.padding); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err = b.WriteString("<" + key); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
*s += "<" + key
|
|
||||||
endTag, isSimple = true, true
|
endTag, isSimple = true, true
|
||||||
break
|
break
|
||||||
default: // handle anything - even goofy stuff
|
default: // handle anything - even goofy stuff
|
||||||
|
@ -1047,12 +1323,17 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
}
|
}
|
||||||
elen = len(v)
|
elen = len(v)
|
||||||
if elen > 0 {
|
if elen > 0 {
|
||||||
*s += ">" + v
|
// *s += ">" + v
|
||||||
|
if _, err = b.WriteString(">" + v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case float64, bool, int, int32, int64, float32, json.Number:
|
case float64, bool, int, int32, int64, float32, json.Number:
|
||||||
v := fmt.Sprintf("%v", value)
|
v := fmt.Sprintf("%v", value)
|
||||||
elen = len(v) // always > 0
|
elen = len(v) // always > 0
|
||||||
*s += ">" + v
|
if _, err = b.WriteString(">" + v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
case []byte: // NOTE: byte is just an alias for uint8
|
case []byte: // NOTE: byte is just an alias for uint8
|
||||||
// similar to how xml.Marshal handles []byte structure members
|
// similar to how xml.Marshal handles []byte structure members
|
||||||
v := string(value.([]byte))
|
v := string(value.([]byte))
|
||||||
|
@ -1061,9 +1342,15 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
}
|
}
|
||||||
elen = len(v)
|
elen = len(v)
|
||||||
if elen > 0 {
|
if elen > 0 {
|
||||||
*s += ">" + v
|
// *s += ">" + v
|
||||||
|
if _, err = b.WriteString(">" + v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
if _, err = b.WriteString(">"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var v []byte
|
var v []byte
|
||||||
var err error
|
var err error
|
||||||
if doIndent {
|
if doIndent {
|
||||||
|
@ -1072,11 +1359,15 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
v, err = xml.Marshal(value)
|
v, err = xml.Marshal(value)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
*s += ">UNKNOWN"
|
if _, err = b.WriteString(">UNKNOWN"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
elen = len(v)
|
elen = len(v)
|
||||||
if elen > 0 {
|
if elen > 0 {
|
||||||
*s += string(v)
|
if _, err = b.Write(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1086,21 +1377,31 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp
|
||||||
if endTag {
|
if endTag {
|
||||||
if doIndent {
|
if doIndent {
|
||||||
if !isSimple {
|
if !isSimple {
|
||||||
*s += p.padding
|
if _, err = b.WriteString(p.padding); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if elen > 0 || useGoXmlEmptyElemSyntax {
|
if elen > 0 || useGoXmlEmptyElemSyntax {
|
||||||
if elen == 0 {
|
if elen == 0 {
|
||||||
*s += ">"
|
if _, err = b.WriteString(">"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err = b.WriteString(`</` + key + ">"); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
*s += `</` + key + ">"
|
|
||||||
} else {
|
} else {
|
||||||
*s += `/>`
|
if _, err = b.WriteString(`/>`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if doIndent {
|
if doIndent {
|
||||||
if p.cnt > p.start {
|
if p.cnt > p.start {
|
||||||
*s += "\n"
|
if _, err = b.WriteString("\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.Outdent()
|
p.Outdent()
|
||||||
}
|
}
|
266
vendor/github.com/clbanning/mxj/xmlseq.go → vendor/github.com/clbanning/mxj/v2/xmlseq.go
generated
vendored
266
vendor/github.com/clbanning/mxj/xmlseq.go → vendor/github.com/clbanning/mxj/v2/xmlseq.go
generated
vendored
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2012-2016 Charles Banning. All rights reserved.
|
// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file
|
// license that can be found in the LICENSE file
|
||||||
|
|
||||||
|
@ -13,22 +13,29 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MapSeq is like Map but contains seqencing indices to allow recovering the original order of
|
||||||
|
// the XML elements when the map[string]interface{} is marshaled. Element attributes are
|
||||||
|
// stored as a map["#attr"]map[<attr_key>]map[string]interface{}{"#text":"<value>", "#seq":<attr_index>}
|
||||||
|
// value instead of denoting the keys with a prefix character. Also, comments, directives and
|
||||||
|
// process instructions are preserved.
|
||||||
|
type MapSeq map[string]interface{}
|
||||||
|
|
||||||
|
// NoRoot is returned by NewXmlSeq, etc., when a comment, directive or procinstr element is parsed
|
||||||
|
// in the XML data stream and the element is not contained in an XML object with a root element.
|
||||||
var NoRoot = errors.New("no root key")
|
var NoRoot = errors.New("no root key")
|
||||||
var NO_ROOT = NoRoot // maintain backwards compatibility
|
var NO_ROOT = NoRoot // maintain backwards compatibility
|
||||||
|
|
||||||
// ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... -------------------------
|
// ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... -------------------------
|
||||||
|
|
||||||
// This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure.
|
// NewMapXmlSeq converts a XML doc into a MapSeq value with elements id'd with decoding sequence key represented
|
||||||
// The xml.Decoder.RawToken method is used to parse the XML, so there is no checking for appropriate xml.EndElement values;
|
// as map["#seq"]<int value>.
|
||||||
// thus, it is assumed that the XML is valid.
|
|
||||||
//
|
|
||||||
// NewMapXmlSeq - convert a XML doc into a Map with elements id'd with decoding sequence int - #seq.
|
|
||||||
// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
|
// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
|
||||||
// NOTE: "#seq" key/value pairs are removed on encoding with mv.XmlSeq() / mv.XmlSeqIndent().
|
// NOTE: "#seq" key/value pairs are removed on encoding with msv.Xml() / msv.XmlIndent().
|
||||||
// • attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":<aval>, "#seq":<num>}
|
// • attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":<aval>, "#seq":<num>}
|
||||||
// • all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well.
|
// • all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well.
|
||||||
// • lists always decode as map["list_tag"][]map[string]interface{} where the array elements are maps that
|
// • lists always decode as map["list_tag"][]map[string]interface{} where the array elements are maps that
|
||||||
|
@ -50,7 +57,7 @@ var NO_ROOT = NoRoot // maintain backwards compatibility
|
||||||
// newtag :
|
// newtag :
|
||||||
// #seq :[int] 1
|
// #seq :[int] 1
|
||||||
// #text :[string] value 2
|
// #text :[string] value 2
|
||||||
// It will encode in proper sequence even though the Map representation merges all "ltag" elements in an array.
|
// It will encode in proper sequence even though the MapSeq representation merges all "ltag" elements in an array.
|
||||||
// • comments - "<!--comment-->" - are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair.
|
// • comments - "<!--comment-->" - are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair.
|
||||||
// • directives - "<!text>" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair.
|
// • directives - "<!text>" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair.
|
||||||
// • process instructions - "<?instr?>" - are decoded as map["#procinst"]interface{} where the #procinst value
|
// • process instructions - "<?instr?>" - are decoded as map["#procinst"]interface{} where the #procinst value
|
||||||
|
@ -68,10 +75,16 @@ var NO_ROOT = NoRoot // maintain backwards compatibility
|
||||||
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
||||||
//
|
//
|
||||||
// NAME SPACES:
|
// NAME SPACES:
|
||||||
// 1. Keys in the Map value that are parsed from a <name space prefix>:<local name> tag preserve the
|
// 1. Keys in the MapSeq value that are parsed from a <name space prefix>:<local name> tag preserve the
|
||||||
// "<prefix>:" notation rather than stripping it as with NewMapXml().
|
// "<prefix>:" notation rather than stripping it as with NewMapXml().
|
||||||
// 2. Attribute keys for name space prefix declarations preserve "xmlns:<prefix>" notation.
|
// 2. Attribute keys for name space prefix declarations preserve "xmlns:<prefix>" notation.
|
||||||
func NewMapXmlSeq(xmlVal []byte, cast ...bool) (Map, error) {
|
//
|
||||||
|
// ERRORS:
|
||||||
|
// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
|
||||||
|
// "#directive" or #procinst" key.
|
||||||
|
// 2. Unmarshaling an XML doc that is formatted using the whitespace character, " ", will error, since
|
||||||
|
// Decoder.RawToken treats such occurances as significant. See NewMapFormattedXmlSeq().
|
||||||
|
func NewMapXmlSeq(xmlVal []byte, cast ...bool) (MapSeq, error) {
|
||||||
var r bool
|
var r bool
|
||||||
if len(cast) == 1 {
|
if len(cast) == 1 {
|
||||||
r = cast[0]
|
r = cast[0]
|
||||||
|
@ -79,16 +92,40 @@ func NewMapXmlSeq(xmlVal []byte, cast ...bool) (Map, error) {
|
||||||
return xmlSeqToMap(xmlVal, r)
|
return xmlSeqToMap(xmlVal, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure.
|
// NewMapFormattedXmlSeq performs the same as NewMapXmlSeq but is useful for processing XML objects that
|
||||||
|
// are formatted using the whitespace character, " ". (The stdlib xml.Decoder, by default, treats all
|
||||||
|
// whitespace as significant; Decoder.Token() and Decoder.RawToken() will return strings of one or more
|
||||||
|
// whitespace characters and without alphanumeric or punctuation characters as xml.CharData values.)
|
||||||
//
|
//
|
||||||
// Get next XML doc from an io.Reader as a Map value. Returns Map value.
|
// If you're processing such XML, then this will convert all occurrences of whitespace-only strings
|
||||||
|
// into an empty string, "", prior to parsing the XML - irrespective of whether the occurrence is
|
||||||
|
// formatting or is a actual element value.
|
||||||
|
func NewMapFormattedXmlSeq(xmlVal []byte, cast ...bool) (MapSeq, error) {
|
||||||
|
var c bool
|
||||||
|
if len(cast) == 1 {
|
||||||
|
c = cast[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per PR #104 - clean out formatting characters so they don't show up in Decoder.RawToken() stream.
|
||||||
|
// NOTE: Also replaces element values that are solely comprised of formatting/whitespace characters
|
||||||
|
// with empty string, "".
|
||||||
|
r := regexp.MustCompile(`>[\n\t\r ]*<`)
|
||||||
|
xmlVal = r.ReplaceAll(xmlVal, []byte("><"))
|
||||||
|
return xmlSeqToMap(xmlVal, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMpaXmlSeqReader returns next XML doc from an io.Reader as a MapSeq value.
|
||||||
// NOTES:
|
// NOTES:
|
||||||
// 1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
|
// 1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
|
||||||
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
|
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
|
||||||
// 2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
|
// 2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
|
||||||
// re-encode the message in its original structure.
|
// re-encode the message in its original structure.
|
||||||
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
||||||
func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (Map, error) {
|
//
|
||||||
|
// ERRORS:
|
||||||
|
// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
|
||||||
|
// "#directive" or #procinst" key.
|
||||||
|
func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (MapSeq, error) {
|
||||||
var r bool
|
var r bool
|
||||||
if len(cast) == 1 {
|
if len(cast) == 1 {
|
||||||
r = cast[0]
|
r = cast[0]
|
||||||
|
@ -105,9 +142,8 @@ func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (Map, error) {
|
||||||
return xmlSeqReaderToMap(xmlReader, r)
|
return xmlSeqReaderToMap(xmlReader, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is only useful if you want to re-encode the Map as XML using mv.XmlSeq(), etc., to preserve the original structure.
|
// NewMapXmlSeqReaderRaw returns the next XML doc from an io.Reader as a MapSeq value.
|
||||||
//
|
// Returns MapSeq value, slice with the raw XML, and any error.
|
||||||
// Get next XML doc from an io.Reader as a Map value. Returns Map value and slice with the raw XML.
|
|
||||||
// NOTES:
|
// NOTES:
|
||||||
// 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
|
// 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
|
||||||
// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
|
// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
|
||||||
|
@ -120,7 +156,11 @@ func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (Map, error) {
|
||||||
// 4. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
|
// 4. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
|
||||||
// re-encode the message in its original structure.
|
// re-encode the message in its original structure.
|
||||||
// 5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
// 5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
|
||||||
func NewMapXmlSeqReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
|
//
|
||||||
|
// ERRORS:
|
||||||
|
// 1. If a NoRoot error, "no root key," is returned, check if the initial map key is "#comment",
|
||||||
|
// "#directive" or #procinst" key.
|
||||||
|
func NewMapXmlSeqReaderRaw(xmlReader io.Reader, cast ...bool) (MapSeq, []byte, error) {
|
||||||
var r bool
|
var r bool
|
||||||
if len(cast) == 1 {
|
if len(cast) == 1 {
|
||||||
r = cast[0]
|
r = cast[0]
|
||||||
|
@ -193,13 +233,16 @@ func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[s
|
||||||
if snakeCaseKeys {
|
if snakeCaseKeys {
|
||||||
v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
|
v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
|
||||||
}
|
}
|
||||||
|
if xmlEscapeCharsDecoder { // per issue#84
|
||||||
|
v.Value = escapeChars(v.Value)
|
||||||
|
}
|
||||||
if len(v.Name.Space) > 0 {
|
if len(v.Name.Space) > 0 {
|
||||||
aa[v.Name.Space+`:`+v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r), "#seq": i}
|
aa[v.Name.Space+`:`+v.Name.Local] = map[string]interface{}{textK: cast(v.Value, r, ""), seqK: i}
|
||||||
} else {
|
} else {
|
||||||
aa[v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r), "#seq": i}
|
aa[v.Name.Local] = map[string]interface{}{textK: cast(v.Value, r, ""), seqK: i}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
na["#attr"] = aa
|
na[attrK] = aa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,10 +310,10 @@ func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[s
|
||||||
// where all the "list" subelements are decoded into an array.
|
// where all the "list" subelements are decoded into an array.
|
||||||
switch val.(type) {
|
switch val.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
val.(map[string]interface{})["#seq"] = seq
|
val.(map[string]interface{})[seqK] = seq
|
||||||
seq++
|
seq++
|
||||||
case interface{}: // a non-nil simple element: string, float64, bool
|
case interface{}: // a non-nil simple element: string, float64, bool
|
||||||
v := map[string]interface{}{"#text": val, "#seq": seq}
|
v := map[string]interface{}{textK: val, seqK: seq}
|
||||||
seq++
|
seq++
|
||||||
val = v
|
val = v
|
||||||
}
|
}
|
||||||
|
@ -323,7 +366,10 @@ func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[s
|
||||||
return n, nil
|
return n, nil
|
||||||
case xml.CharData:
|
case xml.CharData:
|
||||||
// clean up possible noise
|
// clean up possible noise
|
||||||
tt := strings.Trim(string(t.(xml.CharData)), "\t\r\b\n ")
|
tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
|
||||||
|
if xmlEscapeCharsDecoder { // issue#84
|
||||||
|
tt = escapeChars(tt)
|
||||||
|
}
|
||||||
if skey == "" {
|
if skey == "" {
|
||||||
// per Adrian (http://www.adrianlungu.com/) catch stray text
|
// per Adrian (http://www.adrianlungu.com/) catch stray text
|
||||||
// in decoder stream -
|
// in decoder stream -
|
||||||
|
@ -334,42 +380,42 @@ func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[s
|
||||||
}
|
}
|
||||||
if len(tt) > 0 {
|
if len(tt) > 0 {
|
||||||
// every simple element is a #text and has #seq associated with it
|
// every simple element is a #text and has #seq associated with it
|
||||||
na["#text"] = cast(tt, r)
|
na[textK] = cast(tt, r, "")
|
||||||
na["#seq"] = seq
|
na[seqK] = seq
|
||||||
seq++
|
seq++
|
||||||
}
|
}
|
||||||
case xml.Comment:
|
case xml.Comment:
|
||||||
if n == nil { // no root 'key'
|
if n == nil { // no root 'key'
|
||||||
n = map[string]interface{}{"#comment": string(t.(xml.Comment))}
|
n = map[string]interface{}{commentK: string(t.(xml.Comment))}
|
||||||
return n, NoRoot
|
return n, NoRoot
|
||||||
}
|
}
|
||||||
cm := make(map[string]interface{}, 2)
|
cm := make(map[string]interface{}, 2)
|
||||||
cm["#text"] = string(t.(xml.Comment))
|
cm[textK] = string(t.(xml.Comment))
|
||||||
cm["#seq"] = seq
|
cm[seqK] = seq
|
||||||
seq++
|
seq++
|
||||||
na["#comment"] = cm
|
na[commentK] = cm
|
||||||
case xml.Directive:
|
case xml.Directive:
|
||||||
if n == nil { // no root 'key'
|
if n == nil { // no root 'key'
|
||||||
n = map[string]interface{}{"#directive": string(t.(xml.Directive))}
|
n = map[string]interface{}{directiveK: string(t.(xml.Directive))}
|
||||||
return n, NoRoot
|
return n, NoRoot
|
||||||
}
|
}
|
||||||
dm := make(map[string]interface{}, 2)
|
dm := make(map[string]interface{}, 2)
|
||||||
dm["#text"] = string(t.(xml.Directive))
|
dm[textK] = string(t.(xml.Directive))
|
||||||
dm["#seq"] = seq
|
dm[seqK] = seq
|
||||||
seq++
|
seq++
|
||||||
na["#directive"] = dm
|
na[directiveK] = dm
|
||||||
case xml.ProcInst:
|
case xml.ProcInst:
|
||||||
if n == nil {
|
if n == nil {
|
||||||
na = map[string]interface{}{"#target": t.(xml.ProcInst).Target, "#inst": string(t.(xml.ProcInst).Inst)}
|
na = map[string]interface{}{targetK: t.(xml.ProcInst).Target, instK: string(t.(xml.ProcInst).Inst)}
|
||||||
n = map[string]interface{}{"#procinst": na}
|
n = map[string]interface{}{procinstK: na}
|
||||||
return n, NoRoot
|
return n, NoRoot
|
||||||
}
|
}
|
||||||
pm := make(map[string]interface{}, 3)
|
pm := make(map[string]interface{}, 3)
|
||||||
pm["#target"] = t.(xml.ProcInst).Target
|
pm[targetK] = t.(xml.ProcInst).Target
|
||||||
pm["#inst"] = string(t.(xml.ProcInst).Inst)
|
pm[instK] = string(t.(xml.ProcInst).Inst)
|
||||||
pm["#seq"] = seq
|
pm[seqK] = seq
|
||||||
seq++
|
seq++
|
||||||
na["#procinst"] = pm
|
na[procinstK] = pm
|
||||||
default:
|
default:
|
||||||
// noop - shouldn't ever get here, now, since we handle all token types
|
// noop - shouldn't ever get here, now, since we handle all token types
|
||||||
}
|
}
|
||||||
|
@ -380,12 +426,9 @@ func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[s
|
||||||
|
|
||||||
// --------------------- mv.XmlSeq & mv.XmlSeqWriter -------------------------
|
// --------------------- mv.XmlSeq & mv.XmlSeqWriter -------------------------
|
||||||
|
|
||||||
// This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
|
// Xml encodes a MapSeq as XML with elements sorted on #seq. The companion of NewMapXmlSeq().
|
||||||
//
|
|
||||||
// Encode a Map as XML with elements sorted on #seq. The companion of NewMapXmlSeq().
|
|
||||||
// The following rules apply.
|
// The following rules apply.
|
||||||
// - The key label "#text" is treated as the value for a simple element with attributes.
|
// - The "#seq" key value is used to seqence the subelements or attributes only.
|
||||||
// - The "#seq" key is used to seqence the subelements or attributes but is ignored for writing.
|
|
||||||
// - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key.
|
// - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key.
|
||||||
// - The "#comment" map key identifies a comment in the value "#text" map entry - <!--comment-->.
|
// - The "#comment" map key identifies a comment in the value "#text" map entry - <!--comment-->.
|
||||||
// - The "#directive" map key identifies a directive in the value "#text" map entry - <!directive>.
|
// - The "#directive" map key identifies a directive in the value "#text" map entry - <!directive>.
|
||||||
|
@ -399,7 +442,7 @@ func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[s
|
||||||
// - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called.
|
// - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called.
|
||||||
// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
|
// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
|
||||||
// Thus, `{ "key":"value" }` encodes as "<key>value</key>".
|
// Thus, `{ "key":"value" }` encodes as "<key>value</key>".
|
||||||
func (mv Map) XmlSeq(rootTag ...string) ([]byte, error) {
|
func (mv MapSeq) Xml(rootTag ...string) ([]byte, error) {
|
||||||
m := map[string]interface{}(mv)
|
m := map[string]interface{}(mv)
|
||||||
var err error
|
var err error
|
||||||
s := new(string)
|
s := new(string)
|
||||||
|
@ -429,18 +472,28 @@ func (mv Map) XmlSeq(rootTag ...string) ([]byte, error) {
|
||||||
err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
|
err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
if xmlCheckIsValid {
|
||||||
|
d := xml.NewDecoder(bytes.NewReader([]byte(*s)))
|
||||||
|
for {
|
||||||
|
_, err = d.Token()
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return []byte(*s), err
|
return []byte(*s), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
|
// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
|
||||||
// The names will also provide a key for the number of return arguments.
|
// The names will also provide a key for the number of return arguments.
|
||||||
|
|
||||||
// This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
|
// XmlWriter Writes the MapSeq value as XML on the Writer.
|
||||||
//
|
// See MapSeq.Xml() for encoding rules.
|
||||||
// Writes the Map as XML on the Writer.
|
func (mv MapSeq) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
|
||||||
// See XmlSeq() for encoding rules.
|
x, err := mv.Xml(rootTag...)
|
||||||
func (mv Map) XmlSeqWriter(xmlWriter io.Writer, rootTag ...string) error {
|
|
||||||
x, err := mv.XmlSeq(rootTag...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -449,12 +502,11 @@ func (mv Map) XmlSeqWriter(xmlWriter io.Writer, rootTag ...string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
|
// XmlWriteRaw writes the MapSeq value as XML on the Writer. []byte is the raw XML that was written.
|
||||||
//
|
// See Map.XmlSeq() for encoding rules.
|
||||||
// Writes the Map as XML on the Writer. []byte is the raw XML that was written.
|
/*
|
||||||
// See XmlSeq() for encoding rules.
|
func (mv MapSeq) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
|
||||||
func (mv Map) XmlSeqWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
|
x, err := mv.Xml(rootTag...)
|
||||||
x, err := mv.XmlSeq(rootTag...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return x, err
|
return x, err
|
||||||
}
|
}
|
||||||
|
@ -462,13 +514,12 @@ func (mv Map) XmlSeqWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, e
|
||||||
_, err = xmlWriter.Write(x)
|
_, err = xmlWriter.Write(x)
|
||||||
return x, err
|
return x, err
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
|
// XmlIndentWriter writes the MapSeq value as pretty XML on the Writer.
|
||||||
//
|
// See MapSeq.Xml() for encoding rules.
|
||||||
// Writes the Map as pretty XML on the Writer.
|
func (mv MapSeq) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
|
||||||
// See Xml() for encoding rules.
|
x, err := mv.XmlIndent(prefix, indent, rootTag...)
|
||||||
func (mv Map) XmlSeqIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
|
|
||||||
x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -477,11 +528,10 @@ func (mv Map) XmlSeqIndentWriter(xmlWriter io.Writer, prefix, indent string, roo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
|
// XmlIndentWriterRaw writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
|
||||||
//
|
// See Map.XmlSeq() for encoding rules.
|
||||||
// Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
|
/*
|
||||||
// See XmlSeq() for encoding rules.
|
func (mv MapSeq) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
|
||||||
func (mv Map) XmlSeqIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
|
|
||||||
x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
|
x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return x, err
|
return x, err
|
||||||
|
@ -490,16 +540,15 @@ func (mv Map) XmlSeqIndentWriterRaw(xmlWriter io.Writer, prefix, indent string,
|
||||||
_, err = xmlWriter.Write(x)
|
_, err = xmlWriter.Write(x)
|
||||||
return x, err
|
return x, err
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
|
// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
|
||||||
|
|
||||||
// ---------------------- XmlSeqIndent ----------------------------
|
// ---------------------- XmlSeqIndent ----------------------------
|
||||||
|
|
||||||
// This should ONLY be used on Map values that were decoded using NewMapXmlSeq() & co.
|
// XmlIndent encodes a map[string]interface{} as a pretty XML string.
|
||||||
//
|
// See MapSeq.XmlSeq() for encoding rules.
|
||||||
// Encode a map[string]interface{} as a pretty XML string.
|
func (mv MapSeq) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
|
||||||
// See mv.XmlSeq() for encoding rules.
|
|
||||||
func (mv Map) XmlSeqIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
|
|
||||||
m := map[string]interface{}(mv)
|
m := map[string]interface{}(mv)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -523,6 +572,21 @@ func (mv Map) XmlSeqIndent(prefix, indent string, rootTag ...string) ([]byte, er
|
||||||
} else {
|
} else {
|
||||||
err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
|
err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
|
||||||
}
|
}
|
||||||
|
if xmlCheckIsValid {
|
||||||
|
if _, err = NewMapXml([]byte(*s)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d := xml.NewDecoder(bytes.NewReader([]byte(*s)))
|
||||||
|
for {
|
||||||
|
_, err = d.Token()
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return []byte(*s), err
|
return []byte(*s), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,7 +605,7 @@ func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{},
|
||||||
if doIndent {
|
if doIndent {
|
||||||
*s += p.padding
|
*s += p.padding
|
||||||
}
|
}
|
||||||
if key != "#comment" && key != "#directive" && key != "#procinst" {
|
if key != commentK && key != directiveK && key != procinstK {
|
||||||
*s += `<` + key
|
*s += `<` + key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -549,27 +613,27 @@ func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{},
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
val := value.(map[string]interface{})
|
val := value.(map[string]interface{})
|
||||||
|
|
||||||
if key == "#comment" {
|
if key == commentK {
|
||||||
*s += `<!--` + val["#text"].(string) + `-->`
|
*s += `<!--` + val[textK].(string) + `-->`
|
||||||
noEndTag = true
|
noEndTag = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == "#directive" {
|
if key == directiveK {
|
||||||
*s += `<!` + val["#text"].(string) + `>`
|
*s += `<!` + val[textK].(string) + `>`
|
||||||
noEndTag = true
|
noEndTag = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == "#procinst" {
|
if key == procinstK {
|
||||||
*s += `<?` + val["#target"].(string) + ` ` + val["#inst"].(string) + `?>`
|
*s += `<?` + val[targetK].(string) + ` ` + val[instK].(string) + `?>`
|
||||||
noEndTag = true
|
noEndTag = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
haveAttrs := false
|
haveAttrs := false
|
||||||
// process attributes first
|
// process attributes first
|
||||||
if v, ok := val["#attr"].(map[string]interface{}); ok {
|
if v, ok := val[attrK].(map[string]interface{}); ok {
|
||||||
// First, unroll the map[string]interface{} into a []keyval array.
|
// First, unroll the map[string]interface{} into a []keyval array.
|
||||||
// Then sequence it.
|
// Then sequence it.
|
||||||
kv := make([]keyval, len(v))
|
kv := make([]keyval, len(v))
|
||||||
|
@ -582,21 +646,21 @@ func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{},
|
||||||
// Now encode the attributes in original decoding sequence, using keyval array.
|
// Now encode the attributes in original decoding sequence, using keyval array.
|
||||||
for _, a := range kv {
|
for _, a := range kv {
|
||||||
vv := a.v.(map[string]interface{})
|
vv := a.v.(map[string]interface{})
|
||||||
switch vv["#text"].(type) {
|
switch vv[textK].(type) {
|
||||||
case string:
|
case string:
|
||||||
if xmlEscapeChars {
|
if xmlEscapeChars {
|
||||||
ss = escapeChars(vv["#text"].(string))
|
ss = escapeChars(vv[textK].(string))
|
||||||
} else {
|
} else {
|
||||||
ss = vv["#text"].(string)
|
ss = vv[textK].(string)
|
||||||
}
|
}
|
||||||
*s += ` ` + a.k + `="` + ss + `"`
|
*s += ` ` + a.k + `="` + ss + `"`
|
||||||
case float64, bool, int, int32, int64, float32:
|
case float64, bool, int, int32, int64, float32:
|
||||||
*s += ` ` + a.k + `="` + fmt.Sprintf("%v", vv["#text"]) + `"`
|
*s += ` ` + a.k + `="` + fmt.Sprintf("%v", vv[textK]) + `"`
|
||||||
case []byte:
|
case []byte:
|
||||||
if xmlEscapeChars {
|
if xmlEscapeChars {
|
||||||
ss = escapeChars(string(vv["#text"].([]byte)))
|
ss = escapeChars(string(vv[textK].([]byte)))
|
||||||
} else {
|
} else {
|
||||||
ss = string(vv["#text"].([]byte))
|
ss = string(vv[textK].([]byte))
|
||||||
}
|
}
|
||||||
*s += ` ` + a.k + `="` + ss + `"`
|
*s += ` ` + a.k + `="` + ss + `"`
|
||||||
default:
|
default:
|
||||||
|
@ -608,8 +672,8 @@ func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{},
|
||||||
|
|
||||||
// simple element?
|
// simple element?
|
||||||
// every map value has, at least, "#seq" and, perhaps, "#text" and/or "#attr"
|
// every map value has, at least, "#seq" and, perhaps, "#text" and/or "#attr"
|
||||||
_, seqOK := val["#seq"] // have key
|
_, seqOK := val[seqK] // have key
|
||||||
if v, ok := val["#text"]; ok && ((len(val) == 3 && haveAttrs) || (len(val) == 2 && !haveAttrs)) && seqOK {
|
if v, ok := val[textK]; ok && ((len(val) == 3 && haveAttrs) || (len(val) == 2 && !haveAttrs)) && seqOK {
|
||||||
if stmp, ok := v.(string); ok && stmp != "" {
|
if stmp, ok := v.(string); ok && stmp != "" {
|
||||||
if xmlEscapeChars {
|
if xmlEscapeChars {
|
||||||
stmp = escapeChars(stmp)
|
stmp = escapeChars(stmp)
|
||||||
|
@ -630,10 +694,10 @@ func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{},
|
||||||
// 'kv' will hold everything that needs to be written
|
// 'kv' will hold everything that needs to be written
|
||||||
kv := make([]keyval, 0)
|
kv := make([]keyval, 0)
|
||||||
for k, v := range val {
|
for k, v := range val {
|
||||||
if k == "#attr" { // already processed
|
if k == attrK { // already processed
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if k == "#seq" { // ignore - just for sorting
|
if k == seqK { // ignore - just for sorting
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch v.(type) {
|
switch v.(type) {
|
||||||
|
@ -804,13 +868,22 @@ func (e elemListSeq) Swap(i, j int) {
|
||||||
|
|
||||||
func (e elemListSeq) Less(i, j int) bool {
|
func (e elemListSeq) Less(i, j int) bool {
|
||||||
var iseq, jseq int
|
var iseq, jseq int
|
||||||
|
var fiseq, fjseq float64
|
||||||
var ok bool
|
var ok bool
|
||||||
if iseq, ok = e[i].v.(map[string]interface{})["#seq"].(int); !ok {
|
if iseq, ok = e[i].v.(map[string]interface{})[seqK].(int); !ok {
|
||||||
iseq = 9999999
|
if fiseq, ok = e[i].v.(map[string]interface{})[seqK].(float64); ok {
|
||||||
|
iseq = int(fiseq)
|
||||||
|
} else {
|
||||||
|
iseq = 9999999
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if jseq, ok = e[j].v.(map[string]interface{})["#seq"].(int); !ok {
|
if jseq, ok = e[j].v.(map[string]interface{})[seqK].(int); !ok {
|
||||||
jseq = 9999999
|
if fjseq, ok = e[j].v.(map[string]interface{})[seqK].(float64); ok {
|
||||||
|
jseq = int(fjseq)
|
||||||
|
} else {
|
||||||
|
jseq = 9999999
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return iseq <= jseq
|
return iseq <= jseq
|
||||||
|
@ -819,10 +892,11 @@ func (e elemListSeq) Less(i, j int) bool {
|
||||||
// =============== https://groups.google.com/forum/#!topic/golang-nuts/lHPOHD-8qio
|
// =============== https://groups.google.com/forum/#!topic/golang-nuts/lHPOHD-8qio
|
||||||
|
|
||||||
// BeautifyXml (re)formats an XML doc similar to Map.XmlIndent().
|
// BeautifyXml (re)formats an XML doc similar to Map.XmlIndent().
|
||||||
|
// It preserves comments, directives and process instructions,
|
||||||
func BeautifyXml(b []byte, prefix, indent string) ([]byte, error) {
|
func BeautifyXml(b []byte, prefix, indent string) ([]byte, error) {
|
||||||
x, err := NewMapXmlSeq(b)
|
x, err := NewMapXmlSeq(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return x.XmlSeqIndent(prefix, indent)
|
return x.XmlIndent(prefix, indent)
|
||||||
}
|
}
|
18
vendor/github.com/clbanning/mxj/v2/xmlseq2.go
generated
vendored
Normal file
18
vendor/github.com/clbanning/mxj/v2/xmlseq2.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file
|
||||||
|
|
||||||
|
package mxj
|
||||||
|
|
||||||
|
// ---------------- expose Map methods to MapSeq type ---------------------------
|
||||||
|
|
||||||
|
// Pretty print a Map.
|
||||||
|
func (msv MapSeq) StringIndent(offset ...int) string {
|
||||||
|
return writeMap(map[string]interface{}(msv), true, true, offset...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretty print a Map without the value type information - just key:value entries.
|
||||||
|
func (msv MapSeq) StringIndentNoTypeInfo(offset ...int) string {
|
||||||
|
return writeMap(map[string]interface{}(msv), false, true, offset...)
|
||||||
|
}
|
||||||
|
|
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
|
@ -1,6 +1,6 @@
|
||||||
# github.com/clbanning/mxj v1.8.4
|
# github.com/clbanning/mxj/v2 v2.7.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/clbanning/mxj
|
github.com/clbanning/mxj/v2
|
||||||
# github.com/dustin/go-humanize v1.0.1
|
# github.com/dustin/go-humanize v1.0.1
|
||||||
## explicit
|
## explicit
|
||||||
github.com/dustin/go-humanize
|
github.com/dustin/go-humanize
|
||||||
|
@ -16,6 +16,8 @@ github.com/go-chi/chi/v5/middleware
|
||||||
# github.com/gofrs/uuid/v5 v5.0.0
|
# github.com/gofrs/uuid/v5 v5.0.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/gofrs/uuid/v5
|
github.com/gofrs/uuid/v5
|
||||||
|
# github.com/google/go-cmp v0.6.0
|
||||||
|
## explicit
|
||||||
# github.com/gorilla/mux v1.8.1
|
# github.com/gorilla/mux v1.8.1
|
||||||
## explicit
|
## explicit
|
||||||
github.com/gorilla/mux
|
github.com/gorilla/mux
|
||||||
|
|
Loading…
Add table
Reference in a new issue