This commit is contained in:
Bel LaPointe
2020-05-18 14:57:30 -06:00
commit e373627c09
82 changed files with 175690 additions and 0 deletions

21
vendor/github.com/basgys/goxml2json/LICENSE generated vendored Executable file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Bastien Gysler
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.

107
vendor/github.com/basgys/goxml2json/README.md generated vendored Executable file
View File

@@ -0,0 +1,107 @@
# goxml2json [![CircleCI](https://circleci.com/gh/basgys/goxml2json.svg?style=svg)](https://circleci.com/gh/basgys/goxml2json)
Go package that converts XML to JSON
### Install
go get -u github.com/basgys/goxml2json
### Importing
import github.com/basgys/goxml2json
### Usage
**Code example**
```go
package main
import (
"fmt"
"strings"
xj "github.com/basgys/goxml2json"
)
func main() {
// xml is an io.Reader
xml := strings.NewReader(`<?xml version="1.0" encoding="UTF-8"?><hello>world</hello>`)
json, err := xj.Convert(xml)
if err != nil {
panic("That's embarrassing...")
}
fmt.Println(json.String())
// {"hello": "world"}
}
```
**Input**
```xml
<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="CGImap 0.0.2">
<bounds minlat="54.0889580" minlon="12.2487570" maxlat="54.0913900" maxlon="12.2524800"/>
<foo>bar</foo>
</osm>
```
**Output**
```json
{
"osm": {
"-version": 0.6,
"-generator": "CGImap 0.0.2",
"bounds": {
"-minlat": "54.0889580",
"-minlon": "12.2487570",
"-maxlat": "54.0913900",
"-maxlon": "12.2524800"
},
"foo": "bar"
}
}
```
**With type conversion**
```go
package main
import (
"fmt"
"strings"
xj "github.com/basgys/goxml2json"
)
func main() {
// xml is an io.Reader
xml := strings.NewReader(`<?xml version="1.0" encoding="UTF-8"?><price>19.95</price>`)
json, err := xj.Convert(xml, xj.WithTypeConverter(xj.Float))
if err != nil {
panic("That's embarrassing...")
}
fmt.Println(json.String())
// {"price": 19.95}
}
```
### Contributing
Feel free to contribute to this project if you want to fix/extend/improve it.
### Contributors
- [DirectX](https://github.com/directx)
- [powerslacker](https://github.com/powerslacker)
- [samuelhug](https://github.com/samuelhug)
### TODO
* Categorise errors
* Option to prettify the JSON output
* Benchmark

26
vendor/github.com/basgys/goxml2json/converter.go generated vendored Executable file
View File

@@ -0,0 +1,26 @@
package xml2json
import (
"bytes"
"io"
)
// Convert converts the given XML document to JSON
func Convert(r io.Reader, ps ...plugin) (*bytes.Buffer, error) {
// Decode XML document
root := &Node{}
err := NewDecoder(r, ps...).Decode(root)
if err != nil {
return nil, err
}
// Then encode it in JSON
buf := new(bytes.Buffer)
e := NewEncoder(buf, ps...)
err = e.Encode(root)
if err != nil {
return nil, err
}
return buf, nil
}

155
vendor/github.com/basgys/goxml2json/decoder.go generated vendored Executable file
View File

@@ -0,0 +1,155 @@
package xml2json
import (
"encoding/xml"
"io"
"unicode"
"golang.org/x/net/html/charset"
)
const (
attrPrefix = "-"
contentPrefix = "#"
)
// A Decoder reads and decodes XML objects from an input stream.
type Decoder struct {
r io.Reader
err error
attributePrefix string
contentPrefix string
excludeAttrs map[string]bool
formatters []nodeFormatter
}
type element struct {
parent *element
n *Node
label string
}
func (dec *Decoder) SetAttributePrefix(prefix string) {
dec.attributePrefix = prefix
}
func (dec *Decoder) SetContentPrefix(prefix string) {
dec.contentPrefix = prefix
}
func (dec *Decoder) AddFormatters(formatters []nodeFormatter) {
dec.formatters = formatters
}
func (dec *Decoder) ExcludeAttributes(attrs []string) {
for _, attr := range attrs {
dec.excludeAttrs[attr] = true
}
}
func (dec *Decoder) DecodeWithCustomPrefixes(root *Node, contentPrefix string, attributePrefix string) error {
dec.contentPrefix = contentPrefix
dec.attributePrefix = attributePrefix
return dec.Decode(root)
}
// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader, plugins ...plugin) *Decoder {
d := &Decoder{r: r, contentPrefix: contentPrefix, attributePrefix: attrPrefix, excludeAttrs: map[string]bool{}}
for _, p := range plugins {
d = p.AddToDecoder(d)
}
return d
}
// Decode reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v.
func (dec *Decoder) Decode(root *Node) error {
xmlDec := xml.NewDecoder(dec.r)
// That will convert the charset if the provided XML is non-UTF-8
xmlDec.CharsetReader = charset.NewReaderLabel
// Create first element from the root node
elem := &element{
parent: nil,
n: root,
}
for {
t, _ := xmlDec.Token()
if t == nil {
break
}
switch se := t.(type) {
case xml.StartElement:
// Build new a new current element and link it to its parent
elem = &element{
parent: elem,
n: &Node{},
label: se.Name.Local,
}
// Extract attributes as children
for _, a := range se.Attr {
if _, ok := dec.excludeAttrs[a.Name.Local]; ok {
continue
}
elem.n.AddChild(dec.attributePrefix+a.Name.Local, &Node{Data: a.Value})
}
case xml.CharData:
// Extract XML data (if any)
elem.n.Data = trimNonGraphic(string(xml.CharData(se)))
case xml.EndElement:
// And add it to its parent list
if elem.parent != nil {
elem.parent.n.AddChild(elem.label, elem.n)
}
// Then change the current element to its parent
elem = elem.parent
}
}
for _, formatter := range dec.formatters {
formatter.Format(root)
}
return nil
}
// trimNonGraphic returns a slice of the string s, with all leading and trailing
// non graphic characters and spaces removed.
//
// Graphic characters include letters, marks, numbers, punctuation, symbols,
// and spaces, from categories L, M, N, P, S, Zs.
// Spacing characters are set by category Z and property Pattern_White_Space.
func trimNonGraphic(s string) string {
if s == "" {
return s
}
var first *int
var last int
for i, r := range []rune(s) {
if !unicode.IsGraphic(r) || unicode.IsSpace(r) {
continue
}
if first == nil {
f := i // copy i
first = &f
last = i
} else {
last = i
}
}
// If first is nil, it means there are no graphic characters
if first == nil {
return ""
}
return string([]rune(s)[*first : last+1])
}

2
vendor/github.com/basgys/goxml2json/doc.go generated vendored Executable file
View File

@@ -0,0 +1,2 @@
// Package xml2json is an XML to JSON converter
package xml2json

191
vendor/github.com/basgys/goxml2json/encoder.go generated vendored Executable file
View File

@@ -0,0 +1,191 @@
package xml2json
import (
"bytes"
"io"
"unicode/utf8"
)
// An Encoder writes JSON objects to an output stream.
type Encoder struct {
w io.Writer
err error
contentPrefix string
attributePrefix string
tc encoderTypeConverter
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer, plugins ...plugin) *Encoder {
e := &Encoder{w: w, contentPrefix: contentPrefix, attributePrefix: attrPrefix}
for _, p := range plugins {
e = p.AddToEncoder(e)
}
return e
}
// Encode writes the JSON encoding of v to the stream
func (enc *Encoder) Encode(root *Node) error {
if enc.err != nil {
return enc.err
}
if root == nil {
return nil
}
enc.err = enc.format(root, 0)
// Terminate each value with a newline.
// This makes the output look a little nicer
// when debugging, and some kind of space
// is required if the encoded value was a number,
// so that the reader knows there aren't more
// digits coming.
enc.write("\n")
return enc.err
}
func (enc *Encoder) format(n *Node, lvl int) error {
if n.IsComplex() {
enc.write("{")
// Add data as an additional attibute (if any)
if len(n.Data) > 0 {
enc.write("\"")
enc.write(enc.contentPrefix)
enc.write("content")
enc.write("\": ")
enc.write(sanitiseString(n.Data))
enc.write(", ")
}
i := 0
tot := len(n.Children)
for label, children := range n.Children {
enc.write("\"")
enc.write(label)
enc.write("\": ")
if n.ChildrenAlwaysAsArray || len(children) > 1 {
// Array
enc.write("[")
for j, c := range children {
enc.format(c, lvl+1)
if j < len(children)-1 {
enc.write(", ")
}
}
enc.write("]")
} else {
// Map
enc.format(children[0], lvl+1)
}
if i < tot-1 {
enc.write(", ")
}
i++
}
enc.write("}")
} else {
s := sanitiseString(n.Data)
if enc.tc == nil {
// do nothing
} else {
s = enc.tc.Convert(s)
}
enc.write(s)
}
return nil
}
func (enc *Encoder) write(s string) {
enc.w.Write([]byte(s))
}
// https://golang.org/src/encoding/json/encode.go?s=5584:5627#L788
var hex = "0123456789abcdef"
func sanitiseString(s string) string {
var buf bytes.Buffer
buf.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
i++
continue
}
if start < i {
buf.WriteString(s[start:i])
}
switch b {
case '\\', '"':
buf.WriteByte('\\')
buf.WriteByte(b)
case '\n':
buf.WriteByte('\\')
buf.WriteByte('n')
case '\r':
buf.WriteByte('\\')
buf.WriteByte('r')
case '\t':
buf.WriteByte('\\')
buf.WriteByte('t')
default:
// This encodes bytes < 0x20 except for \n and \r,
// as well as <, > and &. The latter are escaped because they
// can lead to security holes when user-controlled strings
// are rendered into JSON and served to some browsers.
buf.WriteString(`\u00`)
buf.WriteByte(hex[b>>4])
buf.WriteByte(hex[b&0xF])
}
i++
start = i
continue
}
c, size := utf8.DecodeRuneInString(s[i:])
if c == utf8.RuneError && size == 1 {
if start < i {
buf.WriteString(s[start:i])
}
buf.WriteString(`\ufffd`)
i += size
start = i
continue
}
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
if c == '\u2028' || c == '\u2029' {
if start < i {
buf.WriteString(s[start:i])
}
buf.WriteString(`\u202`)
buf.WriteByte(hex[c&0xF])
i += size
start = i
continue
}
i += size
}
if start < len(s) {
buf.WriteString(s[start:])
}
buf.WriteByte('"')
return buf.String()
}

74
vendor/github.com/basgys/goxml2json/jstype.go generated vendored Executable file
View File

@@ -0,0 +1,74 @@
package xml2json
import (
"strconv"
"strings"
)
// https://cswr.github.io/JsonSchema/spec/basic_types/
// JSType is a JavaScript extracted from a string
type JSType int
const (
Bool JSType = iota
Int
Float
String
Null
)
// Str2JSType extract a JavaScript type from a string
func Str2JSType(s string) JSType {
var (
output JSType
)
s = strings.TrimSpace(s) // santize the given string
switch {
case isBool(s):
output = Bool
case isFloat(s):
output = Float
case isInt(s):
output = Int
case isNull(s):
output = Null
default:
output = String // if all alternatives have been eliminated, the input is a string
}
return output
}
func isBool(s string) bool {
return s == "true" || s == "false"
}
func isFloat(s string) bool {
var output = false
if strings.Contains(s, ".") {
_, err := strconv.ParseFloat(s, 64)
if err == nil { // the string successfully converts to a decimal
output = true
}
}
return output
}
func isInt(s string) bool {
var output = false
if len(s) >= 1 {
_, err := strconv.Atoi(s)
if err == nil { // the string successfully converts to an int
if s != "0" && s[0] == '0' {
// if the first rune is '0' and there is more than 1 rune, then the input is most likely a float or intended to be
// a string value -- such as in the case of a guid, or an international phone number
} else {
output = true
}
}
}
return output
}
func isNull(s string) bool {
return s == "null"
}

161
vendor/github.com/basgys/goxml2json/plugins.go generated vendored Executable file
View File

@@ -0,0 +1,161 @@
package xml2json
import (
"strings"
)
type (
// an plugin is added to an encoder or/and to an decoder to allow custom functionality at runtime
plugin interface {
AddToEncoder(*Encoder) *Encoder
AddToDecoder(*Decoder) *Decoder
}
// a type converter overides the default string sanitization for encoding json
encoderTypeConverter interface {
Convert(string) string
}
// customTypeConverter converts strings to JSON types using a best guess approach, only parses the JSON types given
// when initialized via WithTypeConverter
customTypeConverter struct {
parseTypes []JSType
}
attrPrefixer string
contentPrefixer string
excluder []string
nodesFormatter struct {
list []nodeFormatter
}
nodeFormatter struct {
path string
plugin nodePlugin
}
nodePlugin interface {
AddTo(*Node)
}
arrayFormatter struct{}
)
// WithTypeConverter allows customized js type conversion behavior by passing in the desired JSTypes
func WithTypeConverter(ts ...JSType) *customTypeConverter {
return &customTypeConverter{parseTypes: ts}
}
func (tc *customTypeConverter) parseAsString(t JSType) bool {
if t == String {
return true
}
for i := 0; i < len(tc.parseTypes); i++ {
if tc.parseTypes[i] == t {
return false
}
}
return true
}
// Adds the type converter to the encoder
func (tc *customTypeConverter) AddToEncoder(e *Encoder) *Encoder {
e.tc = tc
return e
}
func (tc *customTypeConverter) AddToDecoder(d *Decoder) *Decoder {
return d
}
func (tc *customTypeConverter) Convert(s string) string {
// remove quotes if they exists
if strings.HasPrefix(s, `"`) && strings.HasSuffix(s, `"`) {
s = s[1 : len(s)-1]
}
jsType := Str2JSType(s)
if tc.parseAsString(jsType) {
// add the quotes removed at the start of this func
s = `"` + s + `"`
}
return s
}
// WithAttrPrefix appends the given prefix to the json output of xml attribute fields to preserve namespaces
func WithAttrPrefix(prefix string) *attrPrefixer {
ap := attrPrefixer(prefix)
return &ap
}
func (a *attrPrefixer) AddToEncoder(e *Encoder) *Encoder {
e.attributePrefix = string((*a))
return e
}
func (a *attrPrefixer) AddToDecoder(d *Decoder) *Decoder {
d.attributePrefix = string((*a))
return d
}
// WithContentPrefix appends the given prefix to the json output of xml content fields to preserve namespaces
func WithContentPrefix(prefix string) *contentPrefixer {
c := contentPrefixer(prefix)
return &c
}
func (c *contentPrefixer) AddToEncoder(e *Encoder) *Encoder {
e.contentPrefix = string((*c))
return e
}
func (c *contentPrefixer) AddToDecoder(d *Decoder) *Decoder {
d.contentPrefix = string((*c))
return d
}
// ExcludeAttributes excludes some xml attributes, for example, xmlns:xsi, xsi:noNamespaceSchemaLocation
func ExcludeAttributes(attrs []string) *excluder {
ex := excluder(attrs)
return &ex
}
func (ex *excluder) AddToEncoder(e *Encoder) *Encoder {
return e
}
func (ex *excluder) AddToDecoder(d *Decoder) *Decoder {
d.ExcludeAttributes([]string((*ex)))
return d
}
// WithNodes formats specific nodes
func WithNodes(n ...nodeFormatter) *nodesFormatter {
return &nodesFormatter{list: n}
}
func (nf *nodesFormatter) AddToEncoder(e *Encoder) *Encoder {
return e
}
func (nf *nodesFormatter) AddToDecoder(d *Decoder) *Decoder {
d.AddFormatters(nf.list)
return d
}
func NodePlugin(path string, plugin nodePlugin) nodeFormatter {
return nodeFormatter{path: path, plugin: plugin}
}
func (nf *nodeFormatter) Format(node *Node) {
child := node.GetChild(nf.path)
if child != nil {
nf.plugin.AddTo(child)
}
}
func ToArray() *arrayFormatter {
return &arrayFormatter{}
}
func (af *arrayFormatter) AddTo(n *Node) {
n.ChildrenAlwaysAsArray = true
}

47
vendor/github.com/basgys/goxml2json/struct.go generated vendored Executable file
View File

@@ -0,0 +1,47 @@
package xml2json
import (
"strings"
)
// Node is a data element on a tree
type Node struct {
Children map[string]Nodes
Data string
ChildrenAlwaysAsArray bool
}
// Nodes is a list of nodes
type Nodes []*Node
// AddChild appends a node to the list of children
func (n *Node) AddChild(s string, c *Node) {
// Lazy lazy
if n.Children == nil {
n.Children = map[string]Nodes{}
}
n.Children[s] = append(n.Children[s], c)
}
// IsComplex returns whether it is a complex type (has children)
func (n *Node) IsComplex() bool {
return len(n.Children) > 0
}
// GetChild returns child by path if exists. Path looks like "grandparent.parent.child.grandchild"
func (n *Node) GetChild(path string) *Node {
result := n
names := strings.Split(path, ".")
for _, name := range names {
children, exists := result.Children[name]
if !exists {
return nil
}
if len(children) == 0 {
return nil
}
result = children[0]
}
return result
}