mirror of
https://github.com/astaxie/beego.git
synced 2025-06-27 22:10:19 +00:00
add vendor
This commit is contained in:
4
vendor/github.com/beego/goyaml2/README.md
generated
vendored
Normal file
4
vendor/github.com/beego/goyaml2/README.md
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
goyaml2
|
||||
=======
|
||||
|
||||
YAML for Golang
|
41
vendor/github.com/beego/goyaml2/conv.go
generated
vendored
Normal file
41
vendor/github.com/beego/goyaml2/conv.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package goyaml2
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
//"time"
|
||||
)
|
||||
|
||||
var (
|
||||
RE_INT, _ = regexp.Compile("^[0-9,]+$")
|
||||
RE_FLOAT, _ = regexp.Compile("^[0-9]+[.][0-9]+$")
|
||||
RE_DATE, _ = regexp.Compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}$")
|
||||
RE_TIME, _ = regexp.Compile("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$")
|
||||
)
|
||||
|
||||
func string2Val(str string) interface{} {
|
||||
tmp := []byte(str)
|
||||
switch {
|
||||
case str == "false":
|
||||
return false
|
||||
case str == "true":
|
||||
return true
|
||||
case RE_INT.Match(tmp):
|
||||
// TODO check err
|
||||
_int, _ := strconv.ParseInt(str, 10, 64)
|
||||
return _int
|
||||
case RE_FLOAT.Match(tmp):
|
||||
_float, _ := strconv.ParseFloat(str, 64)
|
||||
return _float
|
||||
//TODO support time or Not?
|
||||
/*
|
||||
case RE_DATE.Match(tmp):
|
||||
_date, _ := time.Parse("2006-01-02", str)
|
||||
return _date
|
||||
case RE_TIME.Match(tmp):
|
||||
_time, _ := time.Parse("2006-01-02 03:04:05", str)
|
||||
return _time
|
||||
*/
|
||||
}
|
||||
return str
|
||||
}
|
433
vendor/github.com/beego/goyaml2/reader.go
generated
vendored
Normal file
433
vendor/github.com/beego/goyaml2/reader.go
generated
vendored
Normal file
@ -0,0 +1,433 @@
|
||||
package goyaml2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/wendal/errors"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
DEBUG = true
|
||||
MAP_KEY_ONLY = iota
|
||||
)
|
||||
|
||||
func Read(r io.Reader) (interface{}, error) {
|
||||
yr := &yamlReader{}
|
||||
yr.br = bufio.NewReader(r)
|
||||
obj, err := yr.ReadObject(0)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
if obj == nil {
|
||||
log.Println("Obj == nil")
|
||||
}
|
||||
return obj, err
|
||||
}
|
||||
|
||||
type yamlReader struct {
|
||||
br *bufio.Reader
|
||||
nodes []interface{}
|
||||
lineNum int
|
||||
lastLine string
|
||||
}
|
||||
|
||||
func (y *yamlReader) ReadObject(minIndent int) (interface{}, error) {
|
||||
line, err := y.NextLine()
|
||||
if err != nil {
|
||||
if err == io.EOF && line != "" {
|
||||
//log.Println("Read EOF , but still some data here")
|
||||
} else {
|
||||
//log.Println("ReadERR", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
y.lastLine = line
|
||||
indent, str := getIndent(line)
|
||||
if indent < minIndent {
|
||||
//log.Println("Current Indent Unexpect : ", str, indent, minIndent)
|
||||
return nil, y.Error("Unexpect Indent", nil)
|
||||
}
|
||||
if indent > minIndent {
|
||||
//log.Println("Change minIndent from %d to %d", minIndent, indent)
|
||||
minIndent = indent
|
||||
}
|
||||
switch str[0] {
|
||||
case '-':
|
||||
return y.ReadList(minIndent)
|
||||
case '[':
|
||||
fallthrough
|
||||
case '{':
|
||||
y.lastLine = ""
|
||||
_, value, err := y.asMapKeyValue("tmp:" + str)
|
||||
if err != nil {
|
||||
return nil, y.Error("Err inline map/list", nil)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
//log.Println("Read Objcet as Map", indent, str)
|
||||
|
||||
return y.ReadMap(minIndent)
|
||||
|
||||
}
|
||||
|
||||
func (y *yamlReader) ReadList(minIndent int) ([]interface{}, error) {
|
||||
list := []interface{}{}
|
||||
for {
|
||||
line, err := y.NextLine()
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
indent, str := getIndent(line)
|
||||
switch {
|
||||
case indent < minIndent:
|
||||
y.lastLine = line
|
||||
if len(list) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return list, nil
|
||||
case indent == minIndent:
|
||||
if str[0] != '-' {
|
||||
y.lastLine = line
|
||||
return list, nil
|
||||
}
|
||||
if len(str) < 2 {
|
||||
return nil, y.Error("ListItem is Emtry", nil)
|
||||
}
|
||||
key, value, err := y.asMapKeyValue(str[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch value {
|
||||
case nil:
|
||||
list = append(list, key)
|
||||
case MAP_KEY_ONLY:
|
||||
return nil, y.Error("Not support List-Map yet", nil)
|
||||
default:
|
||||
_map := map[string]interface{}{key.(string): value}
|
||||
list = append(list, _map)
|
||||
|
||||
_line, _err := y.NextLine()
|
||||
if _err != nil && _err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
if _line == "" {
|
||||
return list, nil
|
||||
}
|
||||
y.lastLine = _line
|
||||
_indent, _str := getIndent(line)
|
||||
if _indent >= minIndent+2 {
|
||||
switch _str[0] {
|
||||
case '-':
|
||||
return nil, y.Error("Unexpect", nil)
|
||||
case '[':
|
||||
return nil, y.Error("Unexpect", nil)
|
||||
case '{':
|
||||
return nil, y.Error("Unexpect", nil)
|
||||
}
|
||||
// look like a map
|
||||
_map2, _err := y.ReadMap(_indent)
|
||||
if _map2 != nil {
|
||||
_map2[key.(string)] = value
|
||||
}
|
||||
if err != nil {
|
||||
return list, _err
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
default:
|
||||
return nil, y.Error("Bad Indent\n"+line, nil)
|
||||
}
|
||||
}
|
||||
panic("ERROR")
|
||||
return nil, errors.New("Impossible")
|
||||
}
|
||||
|
||||
func (y *yamlReader) ReadMap(minIndent int) (map[string]interface{}, error) {
|
||||
_map := map[string]interface{}{}
|
||||
//log.Println("ReadMap", minIndent)
|
||||
OUT:
|
||||
for {
|
||||
line, err := y.NextLine()
|
||||
if err != nil {
|
||||
return _map, err
|
||||
}
|
||||
indent, str := getIndent(line)
|
||||
//log.Printf("Indent : %d, str = %s", indent, str)
|
||||
switch {
|
||||
case indent < minIndent:
|
||||
y.lastLine = line
|
||||
if len(_map) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return _map, nil
|
||||
case indent == minIndent:
|
||||
key, value, err := y.asMapKeyValue(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//log.Println("Key=", key, "value=", value)
|
||||
switch value {
|
||||
case nil:
|
||||
return nil, y.Error("Unexpect", nil)
|
||||
case MAP_KEY_ONLY:
|
||||
//log.Println("KeyOnly, read inner Map", key)
|
||||
|
||||
//--------------------------------------
|
||||
_line, err := y.NextLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
if _line == "" {
|
||||
// Emtry map item?
|
||||
_map[key.(string)] = nil
|
||||
return _map, err
|
||||
}
|
||||
} else {
|
||||
return nil, y.Error("ERR?", err)
|
||||
}
|
||||
}
|
||||
y.lastLine = _line
|
||||
_indent, _str := getIndent(_line)
|
||||
if _indent < minIndent {
|
||||
return _map, nil
|
||||
}
|
||||
////log.Println("##>>", _indent, _str)
|
||||
if _indent == minIndent {
|
||||
if _str[0] == '-' {
|
||||
//log.Println("Read Same-Indent ListItem for Map")
|
||||
_list, err := y.ReadList(minIndent)
|
||||
if _list != nil {
|
||||
_map[key.(string)] = _list
|
||||
}
|
||||
if err != nil {
|
||||
return _map, nil
|
||||
}
|
||||
continue OUT
|
||||
} else {
|
||||
// Emtry map item?
|
||||
_map[key.(string)] = nil
|
||||
continue OUT
|
||||
}
|
||||
}
|
||||
//--------------------------------------
|
||||
//log.Println("Read Map Item", _indent, _str)
|
||||
|
||||
obj, err := y.ReadObject(_indent)
|
||||
if obj != nil {
|
||||
_map[key.(string)] = obj
|
||||
}
|
||||
if err != nil {
|
||||
return _map, err
|
||||
}
|
||||
default:
|
||||
_map[key.(string)] = value
|
||||
}
|
||||
default:
|
||||
//log.Println("Bad", indent, str)
|
||||
return nil, y.Error("Bad Indent\n"+line, nil)
|
||||
}
|
||||
}
|
||||
panic("ERROR")
|
||||
return nil, errors.New("Impossible")
|
||||
|
||||
}
|
||||
|
||||
func (y *yamlReader) NextLine() (line string, err error) {
|
||||
if y.lastLine != "" {
|
||||
line = y.lastLine
|
||||
y.lastLine = ""
|
||||
//log.Println("Return lastLine", line)
|
||||
return
|
||||
}
|
||||
for {
|
||||
line, err = y.br.ReadString('\n')
|
||||
y.lineNum++
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(line, "---") || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
line = strings.TrimRight(line, "\n\t\r ")
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
//log.Println("Return Line", line)
|
||||
return
|
||||
}
|
||||
//log.Println("Impossbible : " + line)
|
||||
return // impossbile!
|
||||
}
|
||||
|
||||
func getIndent(str string) (int, string) {
|
||||
indent := 0
|
||||
for i, s := range str {
|
||||
switch s {
|
||||
case ' ':
|
||||
indent++
|
||||
case '\t':
|
||||
indent += 4
|
||||
default:
|
||||
return indent, str[i:]
|
||||
}
|
||||
}
|
||||
panic("Invalid indent : " + str)
|
||||
return -1, ""
|
||||
}
|
||||
|
||||
func (y *yamlReader) asMapKeyValue(str string) (key interface{}, val interface{}, err error) {
|
||||
tokens := splitToken(str)
|
||||
key = tokens[0]
|
||||
if len(tokens) == 1 {
|
||||
return key, nil, nil
|
||||
}
|
||||
if tokens[1] != ":" {
|
||||
return "", nil, y.Error("Unexpect "+str, nil)
|
||||
}
|
||||
|
||||
if len(tokens) == 2 {
|
||||
return key, MAP_KEY_ONLY, nil
|
||||
}
|
||||
if len(tokens) == 3 {
|
||||
return key, tokens[2], nil
|
||||
}
|
||||
switch tokens[2] {
|
||||
case "[":
|
||||
list := []interface{}{}
|
||||
for i := 3; i < len(tokens)-1; i++ {
|
||||
list = append(list, tokens[i])
|
||||
}
|
||||
return key, list, nil
|
||||
case "{":
|
||||
_map := map[string]interface{}{}
|
||||
for i := 3; i < len(tokens)-1; i += 4 {
|
||||
//log.Println(">>>", i, tokens[i])
|
||||
if i > len(tokens)-2 {
|
||||
return "", nil, y.Error("Unexpect "+str, nil)
|
||||
}
|
||||
if tokens[i+1] != ":" {
|
||||
return "", nil, y.Error("Unexpect "+str, nil)
|
||||
}
|
||||
_map[tokens[i].(string)] = tokens[i+2]
|
||||
if (i + 3) < (len(tokens) - 1) {
|
||||
if tokens[i+3] != "," {
|
||||
return "", "", y.Error("Unexpect "+str, nil)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return key, _map, nil
|
||||
}
|
||||
//log.Println(str, tokens)
|
||||
return "", nil, y.Error("Unexpect "+str, nil)
|
||||
}
|
||||
|
||||
func splitToken(str string) (tokens []interface{}) {
|
||||
str = strings.Trim(str, "\r\t\n ")
|
||||
if str == "" {
|
||||
panic("Impossbile")
|
||||
return
|
||||
}
|
||||
|
||||
tokens = []interface{}{}
|
||||
lastPos := 0
|
||||
for i := 0; i < len(str); i++ {
|
||||
switch str[i] {
|
||||
case ':':
|
||||
fallthrough
|
||||
case '{':
|
||||
fallthrough
|
||||
case '[':
|
||||
fallthrough
|
||||
case '}':
|
||||
fallthrough
|
||||
case ']':
|
||||
fallthrough
|
||||
case ',':
|
||||
if i > lastPos {
|
||||
tokens = append(tokens, str[lastPos:i])
|
||||
}
|
||||
tokens = append(tokens, str[i:i+1])
|
||||
lastPos = i + 1
|
||||
case ' ':
|
||||
if i > lastPos {
|
||||
tokens = append(tokens, str[lastPos:i])
|
||||
}
|
||||
lastPos = i + 1
|
||||
case '\'':
|
||||
//log.Println("Scan End of String")
|
||||
i++
|
||||
start := i
|
||||
for ; i < len(str); i++ {
|
||||
if str[i] == '\'' {
|
||||
//log.Println("Found End of String", start, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
tokens = append(tokens, str[start:i])
|
||||
lastPos = i + 1
|
||||
case '"':
|
||||
i++
|
||||
start := i
|
||||
for ; i < len(str); i++ {
|
||||
if str[i] == '"' {
|
||||
break
|
||||
}
|
||||
}
|
||||
tokens = append(tokens, str[start:i])
|
||||
lastPos = i + 1
|
||||
}
|
||||
}
|
||||
////log.Println("last", lastPos)
|
||||
if lastPos < len(str) {
|
||||
tokens = append(tokens, str[lastPos:])
|
||||
}
|
||||
|
||||
if len(tokens) == 1 {
|
||||
tokens[0] = string2Val(tokens[0].(string))
|
||||
return
|
||||
}
|
||||
|
||||
if tokens[1] == ":" {
|
||||
if len(tokens) == 2 {
|
||||
return
|
||||
}
|
||||
if tokens[2] == "{" || tokens[2] == "[" {
|
||||
return
|
||||
}
|
||||
str = strings.Trim(strings.SplitN(str, ":", 2)[1], "\t ")
|
||||
if len(str) > 2 {
|
||||
if str[0] == '\'' && str[len(str)-1] == '\'' {
|
||||
str = str[1 : len(str)-1]
|
||||
} else if str[0] == '"' && str[len(str)-1] == '"' {
|
||||
str = str[1 : len(str)-1]
|
||||
}
|
||||
}
|
||||
val := string2Val(str)
|
||||
tokens = []interface{}{tokens[0], tokens[1], val}
|
||||
return
|
||||
}
|
||||
|
||||
if len(str) > 2 {
|
||||
if str[0] == '\'' && str[len(str)-1] == '\'' {
|
||||
str = str[1 : len(str)-1]
|
||||
} else if str[0] == '"' && str[len(str)-1] == '"' {
|
||||
str = str[1 : len(str)-1]
|
||||
}
|
||||
}
|
||||
val := string2Val(str)
|
||||
tokens = []interface{}{val}
|
||||
return
|
||||
}
|
||||
|
||||
func (y *yamlReader) Error(msg string, err error) error {
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("line %d : %s : %v", y.lineNum, msg, err.Error()))
|
||||
}
|
||||
return errors.New(fmt.Sprintf("line %d >> %s", y.lineNum, msg))
|
||||
}
|
29
vendor/github.com/beego/goyaml2/types.go
generated
vendored
Normal file
29
vendor/github.com/beego/goyaml2/types.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package goyaml2
|
||||
|
||||
const (
|
||||
N_Map = iota
|
||||
N_List
|
||||
N_String
|
||||
)
|
||||
|
||||
type Node interface {
|
||||
Type() int
|
||||
}
|
||||
|
||||
type MapNode map[string]interface{}
|
||||
|
||||
type ListNode []interface{}
|
||||
|
||||
type StringNode string
|
||||
|
||||
func (m *MapNode) Type() int {
|
||||
return N_Map
|
||||
}
|
||||
|
||||
func (l *ListNode) Type() int {
|
||||
return N_List
|
||||
}
|
||||
|
||||
func (s *StringNode) Type() int {
|
||||
return N_String
|
||||
}
|
9
vendor/github.com/beego/goyaml2/writer.go
generated
vendored
Normal file
9
vendor/github.com/beego/goyaml2/writer.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
package goyaml2
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
func Write(w io.Writer, v interface{}) error {
|
||||
return nil
|
||||
}
|
28
vendor/github.com/beego/x2j/LICENSE
generated
vendored
Normal file
28
vendor/github.com/beego/x2j/LICENSE
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
Copyright (c) 2012-2013 Charles Banning <clbanning@gmail.com>.
|
||||
All rights reserved.
|
||||
|
||||
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.
|
157
vendor/github.com/beego/x2j/README
generated
vendored
Normal file
157
vendor/github.com/beego/x2j/README
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
x2j.go - Unmarshal dynamic / arbitrary XML docs and extract values (using wildcards, if necessary).
|
||||
|
||||
ANNOUNCEMENTS
|
||||
|
||||
20 December 2013:
|
||||
|
||||
Non-UTF8 character sets supported via the X2jCharsetReader variable.
|
||||
|
||||
12 December 2013:
|
||||
|
||||
For symmetry, the package j2x has functions that marshal JSON strings and
|
||||
map[string]interface{} values to XML encoded strings: http://godoc.org/github.com/clbanning/j2x.
|
||||
|
||||
Also, ToTree(), ToMap(), ToJson(), ToJsonIndent(), ReaderValuesFromTagPath() and ReaderValuesForTag() use io.Reader instead of string or []byte.
|
||||
|
||||
If you want to process a stream of XML messages check out XmlMsgsFromReader().
|
||||
|
||||
MOTIVATION
|
||||
|
||||
I make extensive use of JSON for messaging and typically unmarshal the messages into
|
||||
map[string]interface{} variables. This is easily done using json.Unmarshal from the
|
||||
standard Go libraries. Unfortunately, many legacy solutions use structured
|
||||
XML messages; in those environments the applications would have to be refitted to
|
||||
interoperate with my components.
|
||||
|
||||
The better solution is to just provide an alternative HTTP handler that receives
|
||||
XML doc messages and parses it into a map[string]interface{} variable and then reuse
|
||||
all the JSON-based code. The Go xml.Unmarshal() function does not provide the same
|
||||
option of unmarshaling XML messages into map[string]interface{} variables. So I wrote
|
||||
a couple of small functions to fill this gap.
|
||||
|
||||
Of course, once the XML doc was unmarshal'd into a map[string]interface{} variable it
|
||||
was just a matter of calling json.Marshal() to provide it as a JSON string. Hence 'x2j'
|
||||
rather than just 'x2m'.
|
||||
|
||||
USAGE
|
||||
|
||||
The package is fairly well self-documented. (http://godoc.org/github.com/clbanning/x2j)
|
||||
The one really useful function is:
|
||||
|
||||
- Unmarshal(doc []byte, v interface{}) error
|
||||
where v is a pointer to a variable of type 'map[string]interface{}', 'string', or
|
||||
any other type supported by xml.Unmarshal().
|
||||
|
||||
To retrieve a value for specific tag use:
|
||||
|
||||
- DocValue(doc, path string, attrs ...string) (interface{},error)
|
||||
- MapValue(m map[string]interface{}, path string, attr map[string]interface{}, recast ...bool) (interface{}, error)
|
||||
|
||||
The 'path' argument is a period-separated tag hierarchy - also known as dot-notation.
|
||||
It is the program's responsibility to cast the returned value to the proper type; possible
|
||||
types are the normal JSON unmarshaling types: string, float64, bool, []interface, map[string]interface{}.
|
||||
|
||||
To retrieve all values associated with a tag occurring anywhere in the XML document use:
|
||||
|
||||
- ValuesForTag(doc, tag string) ([]interface{}, error)
|
||||
- ValuesForKey(m map[string]interface{}, key string) []interface{}
|
||||
|
||||
Demos: http://play.golang.org/p/m8zP-cpk0O
|
||||
http://play.golang.org/p/cIteTS1iSg
|
||||
http://play.golang.org/p/vd8pMiI21b
|
||||
|
||||
Returned values should be one of map[string]interface, []interface{}, or string.
|
||||
|
||||
All the values assocated with a tag-path that may include one or more wildcard characters -
|
||||
'*' - can also be retrieved using:
|
||||
|
||||
- ValuesFromTagPath(doc, path string, getAttrs ...bool) ([]interface{}, error)
|
||||
- ValuesFromKeyPath(map[string]interface{}, path string, getAttrs ...bool) []interface{}
|
||||
|
||||
Demos: http://play.golang.org/p/kUQnZ8VuhS
|
||||
http://play.golang.org/p/l1aMHYtz7G
|
||||
|
||||
NOTE: care should be taken when using "*" at the end of a path - i.e., "books.book.*". See
|
||||
the x2jpath_test.go case on how the wildcard returns all key values and collapses list values;
|
||||
the same message structure can load a []interface{} or a map[string]interface{} (or an interface{})
|
||||
value for a tag.
|
||||
|
||||
See the test cases in "x2jpath_test.go" and programs in "example" subdirectory for more.
|
||||
|
||||
XML PARSING CONVENTIONS
|
||||
|
||||
- Attributes are parsed to map[string]interface{} values by prefixing a hyphen, '-',
|
||||
to the attribute label.
|
||||
- If the element is a simple element and has attributes, the element value
|
||||
is given the key '#text' for its map[string]interface{} representation. (See
|
||||
the 'atomFeedString.xml' test data, below.)
|
||||
|
||||
BULK PROCESSING OF MESSAGE FILES
|
||||
|
||||
Sometime messages may be logged into files for transmission via FTP (e.g.) and subsequent
|
||||
processing. You can use the bulk XML message processor to convert files of XML messages into
|
||||
map[string]interface{} values with custom processing and error handler functions. See
|
||||
the notes and test code for:
|
||||
|
||||
- XmlMsgsFromFile(fname string, phandler func(map[string]interface{}) bool, ehandler func(error) bool,recast ...bool) error
|
||||
|
||||
IMPLEMENTATION NOTES
|
||||
|
||||
Nothing fancy here, just brute force.
|
||||
|
||||
- Use xml.Decoder to parse the XML doc and build a tree.
|
||||
- Walk the tree and load values into a map[string]interface{} variable, 'm', as
|
||||
appropriate.
|
||||
- Use json.Marshaler to convert 'm' to JSON.
|
||||
|
||||
As for testing:
|
||||
|
||||
- Copy an XML doc into 'x2j_test.xml'.
|
||||
- Run "go test" and you'll get a full dump.
|
||||
("pathTestString.xml" and "atomFeedString.xml" are test data from "read_test.go"
|
||||
in the encoding/xml directory of the standard package library.)
|
||||
|
||||
USES
|
||||
|
||||
- putting a XML API on our message hub middleware (http://jsonhub.net)
|
||||
- loading XML data into NoSQL database, such as, mongoDB
|
||||
|
||||
PERFORMANCE IMPROVEMENTS WITH GO 1.1 and 1.2
|
||||
|
||||
Upgrading to Go 1.1 environment results in performance improvements for XML and JSON
|
||||
unmarshalling, in general. The x2j package gets an average performance boost of 40%.
|
||||
|
||||
----- Go 1.0.2 ----- ----------- Go 1.1 -----------
|
||||
iterations ns/op iterations ns/op % improved
|
||||
Benchmark_UseXml-4 100000 18776 200000 10377 45%
|
||||
Benchmark_UseX2j-4 50000 55323 50000 33958 39%
|
||||
Benchmark_UseJson-4 1000000 2257 1000000 1484 34%
|
||||
Benchmark_UseJsonToMap-4 1000000 2531 1000000 1566 38%
|
||||
BenchmarkBig_UseXml-4 100000 28918 100000 15876 45%
|
||||
BenchmarkBig_UseX2j-4 20000 86338 50000 52661 39%
|
||||
BenchmarkBig_UseJson-4 500000 4448 1000000 2664 40%
|
||||
BenchmarkBig_UseJsonToMap-4 200000 9076 500000 5753 37%
|
||||
BenchmarkBig3_UseXml-4 50000 42224 100000 24686 42%
|
||||
BenchmarkBig3_UseX2j-4 10000 147407 20000 84332 43%
|
||||
BenchmarkBig3_UseJson-4 500000 5921 500000 3930 34%
|
||||
BenchmarkBig3_UseJsonToMap-4 200000 13037 200000 8670 33%
|
||||
|
||||
The x2j package gets an additional 15-20% performance boost going to Go 1.2.
|
||||
|
||||
------ Go 1.1 ------ ----------- Go 1.2 -----------
|
||||
iterations ns/op iterations ns/op % improved
|
||||
Benchmark_UseXml-4 200000 10377 200000 11031 -6%
|
||||
Benchmark_UseX2j-4 50000 33958 100000 29188 14%
|
||||
Benchmark_UseJson-4 1000000 1484 1000000 1347 9%
|
||||
Benchmark_UseJsonToMap-4 1000000 1566 1000000 1434 8%
|
||||
BenchmarkBig_UseXml-4 100000 15876 100000 16585 -4%
|
||||
BenchmarkBig_UseX2j-4 50000 52661 50000 43452 17%
|
||||
BenchmarkBig_UseJson-4 1000000 2664 1000000 2523 5%
|
||||
BenchmarkBig_UseJsonToMap-4 500000 5753 500000 4992 13%
|
||||
BenchmarkBig3_UseXml-4 100000 24686 100000 24348 1%
|
||||
BenchmarkBig3_UseX2j-4 20000 84332 50000 66736 21%
|
||||
BenchmarkBig3_UseJson-4 500000 3930 500000 3733 5%
|
||||
BenchmarkBig3_UseJsonToMap-4 200000 8670 200000 7810 10%
|
||||
|
||||
|
||||
|
54
vendor/github.com/beego/x2j/atomFeedString.xml
generated
vendored
Normal file
54
vendor/github.com/beego/x2j/atomFeedString.xml
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us" updated="2009-10-04T01:35:58+00:00"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><author><name>rietveld<></name></author><entry><title>rietveld: an attempt at pubsubhubbub
|
||||
</title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
|
||||
An attempt at adding pubsubhubbub support to Rietveld.
|
||||
http://code.google.com/p/pubsubhubbub
|
||||
http://code.google.com/p/rietveld/issues/detail?id=155
|
||||
|
||||
The server side of the protocol is trivial:
|
||||
1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all
|
||||
feeds that will be pubsubhubbubbed.
|
||||
2. every time one of those feeds changes, tell the hub
|
||||
with a simple POST request.
|
||||
|
||||
I have tested this by adding debug prints to a local hub
|
||||
server and checking that the server got the right publish
|
||||
requests.
|
||||
|
||||
I can&#39;t quite get the server to work, but I think the bug
|
||||
is not in my code. I think that the server expects to be
|
||||
able to grab the feed and see the feed&#39;s actual URL in
|
||||
the link rel=&quot;self&quot;, but the default value for that drops
|
||||
the :port from the URL, and I cannot for the life of me
|
||||
figure out how to get the Atom generator deep inside
|
||||
django not to do that, or even where it is doing that,
|
||||
or even what code is running to generate the Atom feed.
|
||||
(I thought I knew but I added some assert False statements
|
||||
and it kept running!)
|
||||
|
||||
Ignoring that particular problem, I would appreciate
|
||||
feedback on the right way to get the two values at
|
||||
the top of feeds.py marked NOTE(rsc).
|
||||
|
||||
|
||||
</summary></entry><entry><title>rietveld: correct tab handling
|
||||
</title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html">
|
||||
This fixes the buggy tab rendering that can be seen at
|
||||
http://codereview.appspot.com/116075/diff/1/2
|
||||
|
||||
The fundamental problem was that the tab code was
|
||||
not being told what column the text began in, so it
|
||||
didn&#39;t know where to put the tab stops. Another problem
|
||||
was that some of the code assumed that string byte
|
||||
offsets were the same as column offsets, which is only
|
||||
true if there are no tabs.
|
||||
|
||||
In the process of fixing this, I cleaned up the arguments
|
||||
to Fold and ExpandTabs and renamed them Break and
|
||||
_ExpandTabs so that I could be sure that I found all the
|
||||
call sites. I also wanted to verify that ExpandTabs was
|
||||
not being used from outside intra_region_diff.py.
|
||||
|
||||
|
||||
</summary></entry></feed> `
|
||||
|
20
vendor/github.com/beego/x2j/pathTestString.xml
generated
vendored
Normal file
20
vendor/github.com/beego/x2j/pathTestString.xml
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<Result>
|
||||
<Before>1</Before>
|
||||
<Items>
|
||||
<Item1>
|
||||
<Value>A</Value>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<Value>B</Value>
|
||||
</Item2>
|
||||
<Item1>
|
||||
<Value>C</Value>
|
||||
<Value>D</Value>
|
||||
</Item1>
|
||||
<_>
|
||||
<Value>E</Value>
|
||||
</_>
|
||||
</Items>
|
||||
<After>2</After>
|
||||
</Result>
|
||||
|
105
vendor/github.com/beego/x2j/reader2j.go
generated
vendored
Normal file
105
vendor/github.com/beego/x2j/reader2j.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
// io.Reader --> map[string]interface{} or JSON string
|
||||
// nothing magic - just implements generic Go case
|
||||
|
||||
package x2j
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ToTree() - parse a XML io.Reader to a tree of Nodes
|
||||
func ToTree(rdr io.Reader) (*Node, error) {
|
||||
p := xml.NewDecoder(rdr)
|
||||
p.CharsetReader = X2jCharsetReader
|
||||
n, perr := xmlToTree("", nil, p)
|
||||
if perr != nil {
|
||||
return nil, perr
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ToMap() - parse a XML io.Reader to a map[string]interface{}
|
||||
func ToMap(rdr io.Reader, recast ...bool) (map[string]interface{}, error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
n, err := ToTree(rdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
m[n.key] = n.treeToMap(r)
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ToJson() - parse a XML io.Reader to a JSON string
|
||||
func ToJson(rdr io.Reader, recast ...bool) (string, error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
m, merr := ToMap(rdr, r)
|
||||
if m == nil || merr != nil {
|
||||
return "", merr
|
||||
}
|
||||
|
||||
b, berr := json.Marshal(m)
|
||||
if berr != nil {
|
||||
return "", berr
|
||||
}
|
||||
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// ToJsonIndent - the pretty form of ReaderToJson
|
||||
func ToJsonIndent(rdr io.Reader, recast ...bool) (string, error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
m, merr := ToMap(rdr, r)
|
||||
if m == nil || merr != nil {
|
||||
return "", merr
|
||||
}
|
||||
|
||||
b, berr := json.MarshalIndent(m, "", " ")
|
||||
if berr != nil {
|
||||
return "", berr
|
||||
}
|
||||
|
||||
// NOTE: don't have to worry about safe JSON marshaling with json.Marshal, since '<' and '>" are reservedin XML.
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
|
||||
// ReaderValuesFromTagPath - io.Reader version of ValuesFromTagPath()
|
||||
func ReaderValuesFromTagPath(rdr io.Reader, path string, getAttrs ...bool) ([]interface{}, error) {
|
||||
var a bool
|
||||
if len(getAttrs) == 1 {
|
||||
a = getAttrs[0]
|
||||
}
|
||||
m, err := ToMap(rdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ValuesFromKeyPath(m, path, a), nil
|
||||
}
|
||||
|
||||
// ReaderValuesForTag - io.Reader version of ValuesForTag()
|
||||
func ReaderValuesForTag(rdr io.Reader, tag string) ([]interface{}, error) {
|
||||
m, err := ToMap(rdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ValuesForKey(m, tag), nil
|
||||
}
|
||||
|
||||
|
29
vendor/github.com/beego/x2j/songTextString.xml
generated
vendored
Normal file
29
vendor/github.com/beego/x2j/songTextString.xml
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<msg mtype="alert" mpriority="1">
|
||||
<text>help me!</text>
|
||||
<song title="A Long Time" author="Mayer Hawthorne">
|
||||
<verses>
|
||||
<verse name="verse 1" no="1">
|
||||
<line no="1">Henry was a renegade</line>
|
||||
<line no="2">Didn't like to play it safe</line>
|
||||
<line no="3">One component at a time</line>
|
||||
<line no="4">There's got to be a better way</line>
|
||||
<line no="5">Oh, people came from miles around</line>
|
||||
<line no="6">Searching for a steady job</line>
|
||||
<line no="7">Welcome to the Motor Town</line>
|
||||
<line no="8">Booming like an atom bomb</line>
|
||||
</verse>
|
||||
<verse name="verse 2" no="2">
|
||||
<line no="1">Oh, Henry was the end of the story</line>
|
||||
<line no="2">Then everything went wrong</line>
|
||||
<line no="3">And we'll return it to its former glory</line>
|
||||
<line no="4">But it just takes so long</line>
|
||||
</verse>
|
||||
</verses>
|
||||
<chorus>
|
||||
<line no="1">It's going to take a long time</line>
|
||||
<line no="2">It's going to take it, but we'll make it one day</line>
|
||||
<line no="3">It's going to take a long time</line>
|
||||
<line no="4">It's going to take it, but we'll make it one day</line>
|
||||
</chorus>
|
||||
</song>
|
||||
</msg>
|
656
vendor/github.com/beego/x2j/x2j.go
generated
vendored
Normal file
656
vendor/github.com/beego/x2j/x2j.go
generated
vendored
Normal file
@ -0,0 +1,656 @@
|
||||
// Unmarshal dynamic / arbitrary XML docs and extract values (using wildcards, if necessary).
|
||||
// Copyright 2012-2013 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
|
||||
/*
|
||||
Unmarshal dynamic / arbitrary XML docs and extract values (using wildcards, if necessary).
|
||||
|
||||
One useful function is:
|
||||
|
||||
- Unmarshal(doc []byte, v interface{}) error
|
||||
where v is a pointer to a variable of type 'map[string]interface{}', 'string', or
|
||||
any other type supported by xml.Unmarshal().
|
||||
|
||||
To retrieve a value for specific tag use:
|
||||
|
||||
- DocValue(doc, path string, attrs ...string) (interface{},error)
|
||||
- MapValue(m map[string]interface{}, path string, attr map[string]interface{}, recast ...bool) (interface{}, error)
|
||||
|
||||
The 'path' argument is a period-separated tag hierarchy - also known as dot-notation.
|
||||
It is the program's responsibility to cast the returned value to the proper type; possible
|
||||
types are the normal JSON unmarshaling types: string, float64, bool, []interface, map[string]interface{}.
|
||||
|
||||
To retrieve all values associated with a tag occurring anywhere in the XML document use:
|
||||
|
||||
- ValuesForTag(doc, tag string) ([]interface{}, error)
|
||||
- ValuesForKey(m map[string]interface{}, key string) []interface{}
|
||||
|
||||
Demos: http://play.golang.org/p/m8zP-cpk0O
|
||||
http://play.golang.org/p/cIteTS1iSg
|
||||
http://play.golang.org/p/vd8pMiI21b
|
||||
|
||||
Returned values should be one of map[string]interface, []interface{}, or string.
|
||||
|
||||
All the values assocated with a tag-path that may include one or more wildcard characters -
|
||||
'*' - can also be retrieved using:
|
||||
|
||||
- ValuesFromTagPath(doc, path string, getAttrs ...bool) ([]interface{}, error)
|
||||
- ValuesFromKeyPath(map[string]interface{}, path string, getAttrs ...bool) []interface{}
|
||||
|
||||
Demos: http://play.golang.org/p/kUQnZ8VuhS
|
||||
http://play.golang.org/p/l1aMHYtz7G
|
||||
|
||||
NOTE: care should be taken when using "*" at the end of a path - i.e., "books.book.*". See
|
||||
the x2jpath_test.go case on how the wildcard returns all key values and collapses list values;
|
||||
the same message structure can load a []interface{} or a map[string]interface{} (or an interface{})
|
||||
value for a tag.
|
||||
|
||||
See the test cases in "x2jpath_test.go" and programs in "example" subdirectory for more.
|
||||
|
||||
XML PARSING CONVENTIONS
|
||||
|
||||
- Attributes are parsed to map[string]interface{} values by prefixing a hyphen, '-',
|
||||
to the attribute label.
|
||||
- If the element is a simple element and has attributes, the element value
|
||||
is given the key '#text' for its map[string]interface{} representation. (See
|
||||
the 'atomFeedString.xml' test data, below.)
|
||||
|
||||
io.Reader HANDLING
|
||||
|
||||
ToTree(), ToMap(), ToJson(), and ToJsonIndent() provide parsing of messages from an io.Reader.
|
||||
If you want to handle a message stream, look at XmlMsgsFromReader().
|
||||
|
||||
NON-UTF8 CHARACTER SETS
|
||||
|
||||
Use the X2jCharsetReader variable to assign io.Reader for alternative character sets.
|
||||
*/
|
||||
package x2j
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// If X2jCharsetReader != nil, it will be used to decode the doc or stream if required
|
||||
// import charset "code.google.com/p/go-charset/charset"
|
||||
// ...
|
||||
// x2j.X2jCharsetReader = charset.NewReader
|
||||
// s, err := x2j.DocToJson(doc)
|
||||
var X2jCharsetReader func(charset string, input io.Reader)(io.Reader, error)
|
||||
|
||||
type Node struct {
|
||||
dup bool // is member of a list
|
||||
attr bool // is an attribute
|
||||
key string // XML tag
|
||||
val string // element value
|
||||
nodes []*Node
|
||||
}
|
||||
|
||||
// DocToJson - return an XML doc as a JSON string.
|
||||
// If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
|
||||
func DocToJson(doc string, recast ...bool) (string, error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
m, merr := DocToMap(doc, r)
|
||||
if m == nil || merr != nil {
|
||||
return "", merr
|
||||
}
|
||||
|
||||
b, berr := json.Marshal(m)
|
||||
if berr != nil {
|
||||
return "", berr
|
||||
}
|
||||
|
||||
// NOTE: don't have to worry about safe JSON marshaling with json.Marshal, since '<' and '>" are reservedin XML.
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// DocToJsonIndent - return an XML doc as a prettified JSON string.
|
||||
// If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
|
||||
// Note: recasting is only applied to element values, not attribute values.
|
||||
func DocToJsonIndent(doc string, recast ...bool) (string, error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
m, merr := DocToMap(doc, r)
|
||||
if m == nil || merr != nil {
|
||||
return "", merr
|
||||
}
|
||||
|
||||
b, berr := json.MarshalIndent(m, "", " ")
|
||||
if berr != nil {
|
||||
return "", berr
|
||||
}
|
||||
|
||||
// NOTE: don't have to worry about safe JSON marshaling with json.Marshal, since '<' and '>" are reservedin XML.
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// DocToMap - convert an XML doc into a map[string]interface{}.
|
||||
// (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)
|
||||
// If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
|
||||
// Note: recasting is only applied to element values, not attribute values.
|
||||
func DocToMap(doc string, recast ...bool) (map[string]interface{}, error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
n, err := DocToTree(doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
m[n.key] = n.treeToMap(r)
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// DocToTree - convert an XML doc into a tree of nodes.
|
||||
func DocToTree(doc string) (*Node, error) {
|
||||
// xml.Decoder doesn't properly handle whitespace in some doc
|
||||
// see songTextString.xml test case ...
|
||||
reg, _ := regexp.Compile("[ \t\n\r]*<")
|
||||
doc = reg.ReplaceAllString(doc, "<")
|
||||
|
||||
b := bytes.NewBufferString(doc)
|
||||
p := xml.NewDecoder(b)
|
||||
p.CharsetReader = X2jCharsetReader
|
||||
n, berr := xmlToTree("", nil, p)
|
||||
if berr != nil {
|
||||
return nil, berr
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// (*Node)WriteTree - convert a tree of nodes into a printable string.
|
||||
// 'padding' is the starting indentation; typically: n.WriteTree().
|
||||
func (n *Node) WriteTree(padding ...int) string {
|
||||
var indent int
|
||||
if len(padding) == 1 {
|
||||
indent = padding[0]
|
||||
}
|
||||
|
||||
var s string
|
||||
if n.val != "" {
|
||||
for i := 0; i < indent; i++ {
|
||||
s += " "
|
||||
}
|
||||
s += n.key + " : " + n.val + "\n"
|
||||
} else {
|
||||
for i := 0; i < indent; i++ {
|
||||
s += " "
|
||||
}
|
||||
s += n.key + " :" + "\n"
|
||||
for _, nn := range n.nodes {
|
||||
s += nn.WriteTree(indent + 1)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// xmlToTree - load a 'clean' XML doc into a tree of *Node.
|
||||
func xmlToTree(skey string, a []xml.Attr, p *xml.Decoder) (*Node, error) {
|
||||
n := new(Node)
|
||||
n.nodes = make([]*Node, 0)
|
||||
|
||||
if skey != "" {
|
||||
n.key = skey
|
||||
if len(a) > 0 {
|
||||
for _, v := range a {
|
||||
na := new(Node)
|
||||
na.attr = true
|
||||
na.key = `-` + v.Name.Local
|
||||
na.val = v.Value
|
||||
n.nodes = append(n.nodes, na)
|
||||
}
|
||||
}
|
||||
}
|
||||
for {
|
||||
t, err := p.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t.(type) {
|
||||
case xml.StartElement:
|
||||
tt := t.(xml.StartElement)
|
||||
// handle root
|
||||
if n.key == "" {
|
||||
n.key = tt.Name.Local
|
||||
if len(tt.Attr) > 0 {
|
||||
for _, v := range tt.Attr {
|
||||
na := new(Node)
|
||||
na.attr = true
|
||||
na.key = `-` + v.Name.Local
|
||||
na.val = v.Value
|
||||
n.nodes = append(n.nodes, na)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nn, nnerr := xmlToTree(tt.Name.Local, tt.Attr, p)
|
||||
if nnerr != nil {
|
||||
return nil, nnerr
|
||||
}
|
||||
n.nodes = append(n.nodes, nn)
|
||||
}
|
||||
case xml.EndElement:
|
||||
// scan n.nodes for duplicate n.key values
|
||||
n.markDuplicateKeys()
|
||||
return n, nil
|
||||
case xml.CharData:
|
||||
tt := string(t.(xml.CharData))
|
||||
if len(n.nodes) > 0 {
|
||||
nn := new(Node)
|
||||
nn.key = "#text"
|
||||
nn.val = tt
|
||||
n.nodes = append(n.nodes, nn)
|
||||
} else {
|
||||
n.val = tt
|
||||
}
|
||||
default:
|
||||
// noop
|
||||
}
|
||||
}
|
||||
// Logically we can't get here, but provide an error message anyway.
|
||||
return nil, errors.New("Unknown parse error in xmlToTree() for: " + n.key)
|
||||
}
|
||||
|
||||
// (*Node)markDuplicateKeys - set node.dup flag for loading map[string]interface{}.
|
||||
func (n *Node) markDuplicateKeys() {
|
||||
l := len(n.nodes)
|
||||
for i := 0; i < l; i++ {
|
||||
if n.nodes[i].dup {
|
||||
continue
|
||||
}
|
||||
for j := i + 1; j < l; j++ {
|
||||
if n.nodes[i].key == n.nodes[j].key {
|
||||
n.nodes[i].dup = true
|
||||
n.nodes[j].dup = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (*Node)treeToMap - convert a tree of nodes into a map[string]interface{}.
|
||||
// (Parses to map that is structurally the same as from json.Unmarshal().)
|
||||
// Note: root is not instantiated; call with: "m[n.key] = n.treeToMap(recast)".
|
||||
func (n *Node) treeToMap(r bool) interface{} {
|
||||
if len(n.nodes) == 0 {
|
||||
return recast(n.val, r)
|
||||
}
|
||||
|
||||
m := make(map[string]interface{}, 0)
|
||||
for _, v := range n.nodes {
|
||||
// just a value
|
||||
if !v.dup && len(v.nodes) == 0 {
|
||||
m[v.key] = recast(v.val, r)
|
||||
continue
|
||||
}
|
||||
|
||||
// a list of values
|
||||
if v.dup {
|
||||
var a []interface{}
|
||||
if vv, ok := m[v.key]; ok {
|
||||
a = vv.([]interface{})
|
||||
} else {
|
||||
a = make([]interface{}, 0)
|
||||
}
|
||||
a = append(a, v.treeToMap(r))
|
||||
m[v.key] = interface{}(a)
|
||||
continue
|
||||
}
|
||||
|
||||
// it's a unique key
|
||||
m[v.key] = v.treeToMap(r)
|
||||
}
|
||||
|
||||
return interface{}(m)
|
||||
}
|
||||
|
||||
// recast - try to cast string values to bool or float64
|
||||
func recast(s string, r bool) interface{} {
|
||||
if r {
|
||||
// handle numeric strings ahead of boolean
|
||||
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
||||
return interface{}(f)
|
||||
}
|
||||
// ParseBool treats "1"==true & "0"==false
|
||||
if b, err := strconv.ParseBool(s); err == nil {
|
||||
return interface{}(b)
|
||||
}
|
||||
}
|
||||
return interface{}(s)
|
||||
}
|
||||
|
||||
// WriteMap - dumps the map[string]interface{} for examination.
|
||||
// 'offset' is initial indentation count; typically: WriteMap(m).
|
||||
// NOTE: with XML all element types are 'string'.
|
||||
// But code written as generic for use with maps[string]interface{} values from json.Unmarshal().
|
||||
// Or it can handle a DocToMap(doc,true) result where values have been recast'd.
|
||||
func WriteMap(m interface{}, offset ...int) string {
|
||||
var indent int
|
||||
if len(offset) == 1 {
|
||||
indent = offset[0]
|
||||
}
|
||||
|
||||
var s string
|
||||
switch m.(type) {
|
||||
case nil:
|
||||
return "[nil] nil"
|
||||
case string:
|
||||
return "[string] " + m.(string)
|
||||
case float64:
|
||||
return "[float64] " + strconv.FormatFloat(m.(float64), 'e', 2, 64)
|
||||
case bool:
|
||||
return "[bool] " + strconv.FormatBool(m.(bool))
|
||||
case []interface{}:
|
||||
s += "[[]interface{}]"
|
||||
for i, v := range m.([]interface{}) {
|
||||
s += "\n"
|
||||
for i := 0; i < indent; i++ {
|
||||
s += " "
|
||||
}
|
||||
s += "[item: " + strconv.FormatInt(int64(i), 10) + "]"
|
||||
switch v.(type) {
|
||||
case string, float64, bool:
|
||||
s += "\n"
|
||||
default:
|
||||
// noop
|
||||
}
|
||||
for i := 0; i < indent; i++ {
|
||||
s += " "
|
||||
}
|
||||
s += WriteMap(v, indent+1)
|
||||
}
|
||||
case map[string]interface{}:
|
||||
for k, v := range m.(map[string]interface{}) {
|
||||
s += "\n"
|
||||
for i := 0; i < indent; i++ {
|
||||
s += " "
|
||||
}
|
||||
// s += "[map[string]interface{}] "+k+" :"+WriteMap(v,indent+1)
|
||||
s += k + " :" + WriteMap(v, indent+1)
|
||||
}
|
||||
default:
|
||||
// shouldn't ever be here ...
|
||||
s += fmt.Sprintf("unknown type for: %v", m)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ------------------------ value extraction from XML doc --------------------------
|
||||
|
||||
// DocValue - return a value for a specific tag
|
||||
// 'doc' is a valid XML message.
|
||||
// 'path' is a hierarchy of XML tags, e.g., "doc.name".
|
||||
// 'attrs' is an OPTIONAL list of "name:value" pairs for attributes.
|
||||
// Note: 'recast' is not enabled here. Use DocToMap(), NewAttributeMap(), and MapValue() calls for that.
|
||||
func DocValue(doc, path string, attrs ...string) (interface{}, error) {
|
||||
n, err := DocToTree(doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
m[n.key] = n.treeToMap(false)
|
||||
|
||||
a, aerr := NewAttributeMap(attrs...)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
v, verr := MapValue(m, path, a)
|
||||
if verr != nil {
|
||||
return nil, verr
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// MapValue - retrieves value based on walking the map, 'm'.
|
||||
// 'm' is the map value of interest.
|
||||
// 'path' is a period-separated hierarchy of keys in the map.
|
||||
// 'attr' is a map of attribute "name:value" pairs from NewAttributeMap(). May be 'nil'.
|
||||
// If the path can't be traversed, an error is returned.
|
||||
// Note: the optional argument 'r' can be used to coerce attribute values, 'attr', if done so for 'm'.
|
||||
func MapValue(m map[string]interface{}, path string, attr map[string]interface{}, r ...bool) (interface{}, error) {
|
||||
// attribute values may have been recasted during map construction; default is 'false'.
|
||||
if len(r) == 1 && r[0] == true {
|
||||
for k, v := range attr {
|
||||
attr[k] = recast(v.(string), true)
|
||||
}
|
||||
}
|
||||
|
||||
// parse the path
|
||||
keys := strings.Split(path, ".")
|
||||
|
||||
// initialize return value to 'm' so a path of "" will work correctly
|
||||
var v interface{} = m
|
||||
var ok bool
|
||||
var okey string
|
||||
var isMap bool = true
|
||||
if keys[0] == "" && len(attr) == 0 {
|
||||
return v, nil
|
||||
}
|
||||
for _, key := range keys {
|
||||
if !isMap {
|
||||
return nil, errors.New("no keys beyond: " + okey)
|
||||
}
|
||||
if v, ok = m[key]; !ok {
|
||||
return nil, errors.New("no key in map: " + key)
|
||||
} else {
|
||||
switch v.(type) {
|
||||
case map[string]interface{}:
|
||||
m = v.(map[string]interface{})
|
||||
isMap = true
|
||||
default:
|
||||
isMap = false
|
||||
}
|
||||
}
|
||||
// save 'key' for error reporting
|
||||
okey = key
|
||||
}
|
||||
|
||||
// match attributes; value is "#text" or nil
|
||||
if attr == nil {
|
||||
return v, nil
|
||||
}
|
||||
return hasAttributes(v, attr)
|
||||
}
|
||||
|
||||
// hasAttributes() - interface{} equality works for string, float64, bool
|
||||
func hasAttributes(v interface{}, a map[string]interface{}) (interface{}, error) {
|
||||
switch v.(type) {
|
||||
case []interface{}:
|
||||
// run through all entries looking one with matching attributes
|
||||
for _, vv := range v.([]interface{}) {
|
||||
if vvv, vvverr := hasAttributes(vv, a); vvverr == nil {
|
||||
return vvv, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("no list member with matching attributes")
|
||||
case map[string]interface{}:
|
||||
// do all attribute name:value pairs match?
|
||||
nv := v.(map[string]interface{})
|
||||
for key, val := range a {
|
||||
if vv, ok := nv[key]; !ok {
|
||||
return nil, errors.New("no attribute with name: " + key[1:])
|
||||
} else if val != vv {
|
||||
return nil, errors.New("no attribute key:value pair: " + fmt.Sprintf("%s:%v", key[1:], val))
|
||||
}
|
||||
}
|
||||
// they all match; so return value associated with "#text" key.
|
||||
if vv, ok := nv["#text"]; ok {
|
||||
return vv, nil
|
||||
} else {
|
||||
// this happens when another element is value of tag rather than just a string value
|
||||
return nv, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("no match for attributes")
|
||||
}
|
||||
|
||||
// NewAttributeMap() - generate map of attributes=value entries as map["-"+string]string.
|
||||
// 'kv' arguments are "name:value" pairs that appear as attributes, name="value".
|
||||
// If len(kv) == 0, the return is (nil, nil).
|
||||
func NewAttributeMap(kv ...string) (map[string]interface{}, error) {
|
||||
if len(kv) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
m := make(map[string]interface{}, 0)
|
||||
for _, v := range kv {
|
||||
vv := strings.Split(v, ":")
|
||||
if len(vv) != 2 {
|
||||
return nil, errors.New("attribute not \"name:value\" pair: " + v)
|
||||
}
|
||||
// attributes are stored as keys prepended with hyphen
|
||||
m["-"+vv[0]] = interface{}(vv[1])
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
//------------------------- get values for key ----------------------------
|
||||
|
||||
// ValuesForTag - return all values in doc associated with 'tag'.
|
||||
// Returns nil if the 'tag' does not occur in the doc.
|
||||
// If there is an error encounted while parsing doc, that is returned.
|
||||
// If you want values 'recast' use DocToMap() and ValuesForKey().
|
||||
func ValuesForTag(doc, tag string) ([]interface{}, error) {
|
||||
m, err := DocToMap(doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ValuesForKey(m, tag), nil
|
||||
}
|
||||
|
||||
// ValuesForKey - return all values in map associated with 'key'
|
||||
// Returns nil if the 'key' does not occur in the map
|
||||
func ValuesForKey(m map[string]interface{}, key string) []interface{} {
|
||||
ret := make([]interface{}, 0)
|
||||
|
||||
hasKey(m, key, &ret)
|
||||
if len(ret) > 0 {
|
||||
return ret
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasKey - if the map 'key' exists append it to array
|
||||
// if it doesn't do nothing except scan array and map values
|
||||
func hasKey(iv interface{}, key string, ret *[]interface{}) {
|
||||
switch iv.(type) {
|
||||
case map[string]interface{}:
|
||||
vv := iv.(map[string]interface{})
|
||||
if v, ok := vv[key]; ok {
|
||||
*ret = append(*ret, v)
|
||||
}
|
||||
for _, v := range iv.(map[string]interface{}) {
|
||||
hasKey(v, key, ret)
|
||||
}
|
||||
case []interface{}:
|
||||
for _, v := range iv.([]interface{}) {
|
||||
hasKey(v, key, ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======== 2013.07.01 - x2j.Unmarshal, wraps xml.Unmarshal ==============
|
||||
|
||||
// Unmarshal - wraps xml.Unmarshal with handling of map[string]interface{}
|
||||
// and string type variables.
|
||||
// Usage: x2j.Unmarshal(doc,&m) where m of type map[string]interface{}
|
||||
// x2j.Unmarshal(doc,&s) where s of type string (Overrides xml.Unmarshal().)
|
||||
// x2j.Unmarshal(doc,&struct) - passed to xml.Unmarshal()
|
||||
// x2j.Unmarshal(doc,&slice) - passed to xml.Unmarshal()
|
||||
func Unmarshal(doc []byte, v interface{}) error {
|
||||
switch v.(type) {
|
||||
case *map[string]interface{}:
|
||||
m, err := ByteDocToMap(doc)
|
||||
vv := *v.(*map[string]interface{})
|
||||
for k, v := range m {
|
||||
vv[k] = v
|
||||
}
|
||||
return err
|
||||
case *string:
|
||||
s, err := ByteDocToJson(doc)
|
||||
*(v.(*string)) = s
|
||||
return err
|
||||
default:
|
||||
b := bytes.NewBuffer(doc)
|
||||
p := xml.NewDecoder(b)
|
||||
p.CharsetReader = X2jCharsetReader
|
||||
return p.Decode(v)
|
||||
// return xml.Unmarshal(doc, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ByteDocToJson - return an XML doc as a JSON string.
|
||||
// If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
|
||||
func ByteDocToJson(doc []byte, recast ...bool) (string, error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
m, merr := ByteDocToMap(doc, r)
|
||||
if m == nil || merr != nil {
|
||||
return "", merr
|
||||
}
|
||||
|
||||
b, berr := json.Marshal(m)
|
||||
if berr != nil {
|
||||
return "", berr
|
||||
}
|
||||
|
||||
// NOTE: don't have to worry about safe JSON marshaling with json.Marshal, since '<' and '>" are reservedin XML.
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// ByteDocToMap - convert an XML doc into a map[string]interface{}.
|
||||
// (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)
|
||||
// If the optional argument 'recast' is 'true', then values will be converted to boolean or float64 if possible.
|
||||
// Note: recasting is only applied to element values, not attribute values.
|
||||
func ByteDocToMap(doc []byte, recast ...bool) (map[string]interface{}, error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
n, err := ByteDocToTree(doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
m[n.key] = n.treeToMap(r)
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ByteDocToTree - convert an XML doc into a tree of nodes.
|
||||
func ByteDocToTree(doc []byte) (*Node, error) {
|
||||
// xml.Decoder doesn't properly handle whitespace in some doc
|
||||
// see songTextString.xml test case ...
|
||||
reg, _ := regexp.Compile("[ \t\n\r]*<")
|
||||
doc = reg.ReplaceAll(doc, []byte("<"))
|
||||
|
||||
b := bytes.NewBuffer(doc)
|
||||
p := xml.NewDecoder(b)
|
||||
p.CharsetReader = X2jCharsetReader
|
||||
n, berr := xmlToTree("", nil, p)
|
||||
if berr != nil {
|
||||
return nil, berr
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
127
vendor/github.com/beego/x2j/x2j_bulk.go
generated
vendored
Normal file
127
vendor/github.com/beego/x2j/x2j_bulk.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright 2012-2013 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
|
||||
|
||||
// x2j_bulk.go: Process files with multiple XML messages.
|
||||
// Extends x2m_bulk.go to work with JSON strings rather than map[string]interface{}.
|
||||
|
||||
package x2j
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// XmlMsgsFromFileAsJson()
|
||||
// 'fname' is name of file
|
||||
// 'phandler' is the JSON string processing handler. Return of 'false' stops further processing.
|
||||
// 'ehandler' is the parsing error handler. Return of 'false' stops further processing and returns error.
|
||||
// Note: phandler() and ehandler() calls are blocking, so reading and processing of messages is serialized.
|
||||
// This means that you can stop reading the file on error or after processing a particular message.
|
||||
// To have reading and handling run concurrently, pass arguments to a go routine in handler and return true.
|
||||
func XmlMsgsFromFileAsJson(fname string, phandler func(string)(bool), ehandler func(error)(bool), recast ...bool) error {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
fi, fierr := os.Stat(fname)
|
||||
if fierr != nil {
|
||||
return fierr
|
||||
}
|
||||
fh, fherr := os.Open(fname)
|
||||
if fherr != nil {
|
||||
return fherr
|
||||
}
|
||||
defer fh.Close()
|
||||
buf := make([]byte,fi.Size())
|
||||
_, rerr := fh.Read(buf)
|
||||
if rerr != nil {
|
||||
return rerr
|
||||
}
|
||||
doc := string(buf)
|
||||
|
||||
// xml.Decoder doesn't properly handle whitespace in some doc
|
||||
// see songTextString.xml test case ...
|
||||
reg,_ := regexp.Compile("[ \t\n\r]*<")
|
||||
doc = reg.ReplaceAllString(doc,"<")
|
||||
b := bytes.NewBufferString(doc)
|
||||
|
||||
for {
|
||||
s, serr := XmlBufferToJson(b,r)
|
||||
if serr != nil && serr != io.EOF {
|
||||
if ok := ehandler(serr); !ok {
|
||||
// caused reader termination
|
||||
return serr
|
||||
}
|
||||
}
|
||||
if s != "" {
|
||||
if ok := phandler(s); !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if serr == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// XmlBufferToJson - process XML message from a bytes.Buffer
|
||||
// 'b' is the buffer
|
||||
// Optional argument 'recast' coerces values to float64 or bool where possible.
|
||||
func XmlBufferToJson(b *bytes.Buffer,recast ...bool) (string,error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
|
||||
n,err := XmlBufferToTree(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
m[n.key] = n.treeToMap(r)
|
||||
|
||||
j, jerr := json.Marshal(m)
|
||||
return string(j), jerr
|
||||
}
|
||||
|
||||
// ============================= io.Reader version for stream processing ======================
|
||||
|
||||
// XmlMsgsFromReaderAsJson() - io.Reader version of XmlMsgsFromFileAsJson
|
||||
// 'rdr' is an io.Reader for an XML message (stream)
|
||||
// 'phandler' is the JSON string processing handler. Return of 'false' stops further processing.
|
||||
// 'ehandler' is the parsing error handler. Return of 'false' stops further processing and returns error.
|
||||
// Note: phandler() and ehandler() calls are blocking, so reading and processing of messages is serialized.
|
||||
// This means that you can stop reading the file on error or after processing a particular message.
|
||||
// To have reading and handling run concurrently, pass arguments to a go routine in handler and return true.
|
||||
func XmlMsgsFromReaderAsJson(rdr io.Reader, phandler func(string)(bool), ehandler func(error)(bool), recast ...bool) error {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
|
||||
for {
|
||||
s, serr := ToJson(rdr,r)
|
||||
if serr != nil && serr != io.EOF {
|
||||
if ok := ehandler(serr); !ok {
|
||||
// caused reader termination
|
||||
return serr
|
||||
}
|
||||
}
|
||||
if s != "" {
|
||||
if ok := phandler(s); !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if serr == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
29
vendor/github.com/beego/x2j/x2j_test.xml
generated
vendored
Normal file
29
vendor/github.com/beego/x2j/x2j_test.xml
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<msg mtype="alert" mpriority="1">
|
||||
<text>help me!</text>
|
||||
<song title="A Long Time" author="Mayer Hawthorne">
|
||||
<verses>
|
||||
<verse name="verse 1" no="1">
|
||||
<line no="1">Henry was a renegade</line>
|
||||
<line no="2">Didn't like to play it safe</line>
|
||||
<line no="3">One component at a time</line>
|
||||
<line no="4">There's got to be a better way</line>
|
||||
<line no="5">Oh, people came from miles around</line>
|
||||
<line no="6">Searching for a steady job</line>
|
||||
<line no="7">Welcome to the Motor Town</line>
|
||||
<line no="8">Booming like an atom bomb</line>
|
||||
</verse>
|
||||
<verse name="verse 2" no="2">
|
||||
<line no="1">Oh, Henry was the end of the story</line>
|
||||
<line no="2">Then everything went wrong</line>
|
||||
<line no="3">And we'll return it to its former glory</line>
|
||||
<line no="4">But it just takes so long</line>
|
||||
</verse>
|
||||
</verses>
|
||||
<chorus>
|
||||
<line no="1">It's going to take a long time</line>
|
||||
<line no="2">It's going to take it, but we'll make it one day</line>
|
||||
<line no="3">It's going to take a long time</line>
|
||||
<line no="4">It's going to take it, but we'll make it one day</line>
|
||||
</chorus>
|
||||
</song>
|
||||
</msg>
|
125
vendor/github.com/beego/x2j/x2j_valuesFrom.go
generated
vendored
Normal file
125
vendor/github.com/beego/x2j/x2j_valuesFrom.go
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright 2012-2013 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
|
||||
|
||||
// x2j_valuesFrom.go: Extract values from an arbitrary XML doc. Tag path can include wildcard characters.
|
||||
|
||||
package x2j
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ------------------- sweep up everything for some point in the node tree ---------------------
|
||||
|
||||
// ValuesFromTagPath - deliver all values for a path node from a XML doc
|
||||
// If there are no values for the path 'nil' is returned.
|
||||
// A return value of (nil, nil) means that there were no values and no errors parsing the doc.
|
||||
// 'doc' is the XML document
|
||||
// 'path' is a dot-separated path of tag nodes
|
||||
// 'getAttrs' can be set 'true' to return attribute values for "*"-terminated path
|
||||
// If a node is '*', then everything beyond is scanned for values.
|
||||
// E.g., "doc.books' might return a single value 'book' of type []interface{}, but
|
||||
// "doc.books.*" could return all the 'book' entries as []map[string]interface{}.
|
||||
// "doc.books.*.author" might return all the 'author' tag values as []string - or
|
||||
// "doc.books.*.author.lastname" might be required, depending on he schema.
|
||||
func ValuesFromTagPath(doc, path string, getAttrs ...bool) ([]interface{}, error) {
|
||||
var a bool
|
||||
if len(getAttrs) == 1 {
|
||||
a = getAttrs[0]
|
||||
}
|
||||
m, err := DocToMap(doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := ValuesFromKeyPath(m, path, a)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ValuesFromKeyPath - deliver all values for a path node from a map[string]interface{}
|
||||
// If there are no values for the path 'nil' is returned.
|
||||
// 'm' is the map to be walked
|
||||
// 'path' is a dot-separated path of key values
|
||||
// 'getAttrs' can be set 'true' to return attribute values for "*"-terminated path
|
||||
// If a node is '*', then everything beyond is walked.
|
||||
// E.g., see ValuesFromTagPath documentation.
|
||||
func ValuesFromKeyPath(m map[string]interface{}, path string, getAttrs ...bool) []interface{} {
|
||||
var a bool
|
||||
if len(getAttrs) == 1 {
|
||||
a = getAttrs[0]
|
||||
}
|
||||
keys := strings.Split(path, ".")
|
||||
ret := make([]interface{}, 0)
|
||||
valuesFromKeyPath(&ret, m, keys, a)
|
||||
if len(ret) == 0 {
|
||||
return nil
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func valuesFromKeyPath(ret *[]interface{}, m interface{}, keys []string, getAttrs bool) {
|
||||
lenKeys := len(keys)
|
||||
|
||||
// load 'm' values into 'ret'
|
||||
// expand any lists
|
||||
if lenKeys == 0 {
|
||||
switch m.(type) {
|
||||
case map[string]interface{}:
|
||||
*ret = append(*ret, m)
|
||||
case []interface{}:
|
||||
for _, v := range m.([]interface{}) {
|
||||
*ret = append(*ret, v)
|
||||
}
|
||||
default:
|
||||
*ret = append(*ret, m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// key of interest
|
||||
key := keys[0]
|
||||
switch key {
|
||||
case "*": // wildcard - scan all values
|
||||
switch m.(type) {
|
||||
case map[string]interface{}:
|
||||
for k, v := range m.(map[string]interface{}) {
|
||||
if string(k[:1]) == "-" && !getAttrs { // skip attributes?
|
||||
continue
|
||||
}
|
||||
valuesFromKeyPath(ret, v, keys[1:], getAttrs)
|
||||
}
|
||||
case []interface{}:
|
||||
for _, v := range m.([]interface{}) {
|
||||
switch v.(type) {
|
||||
// flatten out a list of maps - keys are processed
|
||||
case map[string]interface{}:
|
||||
for kk, vv := range v.(map[string]interface{}) {
|
||||
if string(kk[:1]) == "-" && !getAttrs { // skip attributes?
|
||||
continue
|
||||
}
|
||||
valuesFromKeyPath(ret, vv, keys[1:], getAttrs)
|
||||
}
|
||||
default:
|
||||
valuesFromKeyPath(ret, v, keys[1:], getAttrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
default: // key - must be map[string]interface{}
|
||||
switch m.(type) {
|
||||
case map[string]interface{}:
|
||||
if v, ok := m.(map[string]interface{})[key]; ok {
|
||||
valuesFromKeyPath(ret, v, keys[1:], getAttrs)
|
||||
}
|
||||
case []interface{}: // may be buried in list
|
||||
for _, v := range m.([]interface{}) {
|
||||
switch v.(type) {
|
||||
case map[string]interface{}:
|
||||
if vv, ok := v.(map[string]interface{})[key]; ok {
|
||||
valuesFromKeyPath(ret, vv, keys[1:], getAttrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
201
vendor/github.com/beego/x2j/x2m_bulk.go
generated
vendored
Normal file
201
vendor/github.com/beego/x2j/x2m_bulk.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
// Copyright 2012-2013 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
|
||||
|
||||
// x2m_bulk.go: Process files with multiple XML messages.
|
||||
|
||||
package x2j
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// XmlMsgsFromFile()
|
||||
// 'fname' is name of file
|
||||
// 'phandler' is the map processing handler. Return of 'false' stops further processing.
|
||||
// 'ehandler' is the parsing error handler. Return of 'false' stops further processing and returns error.
|
||||
// Note: phandler() and ehandler() calls are blocking, so reading and processing of messages is serialized.
|
||||
// This means that you can stop reading the file on error or after processing a particular message.
|
||||
// To have reading and handling run concurrently, pass arguments to a go routine in handler and return true.
|
||||
func XmlMsgsFromFile(fname string, phandler func(map[string]interface{})(bool), ehandler func(error)(bool), recast ...bool) error {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
fi, fierr := os.Stat(fname)
|
||||
if fierr != nil {
|
||||
return fierr
|
||||
}
|
||||
fh, fherr := os.Open(fname)
|
||||
if fherr != nil {
|
||||
return fherr
|
||||
}
|
||||
defer fh.Close()
|
||||
buf := make([]byte,fi.Size())
|
||||
_, rerr := fh.Read(buf)
|
||||
if rerr != nil {
|
||||
return rerr
|
||||
}
|
||||
doc := string(buf)
|
||||
|
||||
// xml.Decoder doesn't properly handle whitespace in some doc
|
||||
// see songTextString.xml test case ...
|
||||
reg,_ := regexp.Compile("[ \t\n\r]*<")
|
||||
doc = reg.ReplaceAllString(doc,"<")
|
||||
b := bytes.NewBufferString(doc)
|
||||
|
||||
for {
|
||||
m, merr := XmlBufferToMap(b,r)
|
||||
if merr != nil && merr != io.EOF {
|
||||
if ok := ehandler(merr); !ok {
|
||||
// caused reader termination
|
||||
return merr
|
||||
}
|
||||
}
|
||||
if m != nil {
|
||||
if ok := phandler(m); !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if merr == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// XmlBufferToMap - process XML message from a bytes.Buffer
|
||||
// 'b' is the buffer
|
||||
// Optional argument 'recast' coerces map values to float64 or bool where possible.
|
||||
func XmlBufferToMap(b *bytes.Buffer,recast ...bool) (map[string]interface{},error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
|
||||
n,err := XmlBufferToTree(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
m[n.key] = n.treeToMap(r)
|
||||
|
||||
return m,nil
|
||||
}
|
||||
|
||||
// BufferToTree - derived from DocToTree()
|
||||
func XmlBufferToTree(b *bytes.Buffer) (*Node, error) {
|
||||
p := xml.NewDecoder(b)
|
||||
p.CharsetReader = X2jCharsetReader
|
||||
n, berr := xmlToTree("",nil,p)
|
||||
if berr != nil {
|
||||
return nil, berr
|
||||
}
|
||||
|
||||
return n,nil
|
||||
}
|
||||
|
||||
// XmlBuffer - create XML decoder buffer for a string from anywhere, not necessarily a file.
|
||||
type XmlBuffer struct {
|
||||
cnt uint64
|
||||
str *string
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
var mtx sync.Mutex
|
||||
var cnt uint64
|
||||
var activeXmlBufs = make(map[uint64]*XmlBuffer)
|
||||
|
||||
// NewXmlBuffer() - creates a bytes.Buffer from a string with multiple messages
|
||||
// Use Close() function to release the buffer for garbage collection.
|
||||
func NewXmlBuffer(s string) *XmlBuffer {
|
||||
// xml.Decoder doesn't properly handle whitespace in some doc
|
||||
// see songTextString.xml test case ...
|
||||
reg,_ := regexp.Compile("[ \t\n\r]*<")
|
||||
s = reg.ReplaceAllString(s,"<")
|
||||
b := bytes.NewBufferString(s)
|
||||
buf := new(XmlBuffer)
|
||||
buf.str = &s
|
||||
buf.buf = b
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
buf.cnt = cnt ; cnt++
|
||||
activeXmlBufs[buf.cnt] = buf
|
||||
return buf
|
||||
}
|
||||
|
||||
// BytesNewXmlBuffer() - creates a bytes.Buffer from b with possibly multiple messages
|
||||
// Use Close() function to release the buffer for garbage collection.
|
||||
func BytesNewXmlBuffer(b []byte) *XmlBuffer {
|
||||
bb := bytes.NewBuffer(b)
|
||||
buf := new(XmlBuffer)
|
||||
buf.buf = bb
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
buf.cnt = cnt ; cnt++
|
||||
activeXmlBufs[buf.cnt] = buf
|
||||
return buf
|
||||
}
|
||||
|
||||
// Close() - release the buffer address for garbage collection
|
||||
func (buf *XmlBuffer)Close() {
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
delete(activeXmlBufs,buf.cnt)
|
||||
}
|
||||
|
||||
// NextMap() - retrieve next XML message in buffer as a map[string]interface{} value.
|
||||
// The optional argument 'recast' will try and coerce values to float64 or bool as appropriate.
|
||||
func (buf *XmlBuffer)NextMap(recast ...bool) (map[string]interface{}, error) {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
if _, ok := activeXmlBufs[buf.cnt]; !ok {
|
||||
return nil, errors.New("Buffer is not active.")
|
||||
}
|
||||
return XmlBufferToMap(buf.buf,r)
|
||||
}
|
||||
|
||||
|
||||
// ============================= io.Reader version for stream processing ======================
|
||||
|
||||
// XmlMsgsFromReader() - io.Reader version of XmlMsgsFromFile
|
||||
// 'rdr' is an io.Reader for an XML message (stream)
|
||||
// 'phandler' is the map processing handler. Return of 'false' stops further processing.
|
||||
// 'ehandler' is the parsing error handler. Return of 'false' stops further processing and returns error.
|
||||
// Note: phandler() and ehandler() calls are blocking, so reading and processing of messages is serialized.
|
||||
// This means that you can stop reading the file on error or after processing a particular message.
|
||||
// To have reading and handling run concurrently, pass arguments to a go routine in handler and return true.
|
||||
func XmlMsgsFromReader(rdr io.Reader, phandler func(map[string]interface{})(bool), ehandler func(error)(bool), recast ...bool) error {
|
||||
var r bool
|
||||
if len(recast) == 1 {
|
||||
r = recast[0]
|
||||
}
|
||||
|
||||
for {
|
||||
m, merr := ToMap(rdr,r)
|
||||
if merr != nil && merr != io.EOF {
|
||||
if ok := ehandler(merr); !ok {
|
||||
// caused reader termination
|
||||
return merr
|
||||
}
|
||||
}
|
||||
if m != nil {
|
||||
if ok := phandler(m); !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if merr == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
67
vendor/github.com/beego/x2j/x2m_bulk.xml
generated
vendored
Normal file
67
vendor/github.com/beego/x2j/x2m_bulk.xml
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<msg mtype="alert" mpriority="1">
|
||||
<text>help me!</text>
|
||||
<song title="A Long Time" author="Mayer Hawthorne">
|
||||
<verses>
|
||||
<verse name="verse 1" no="1">
|
||||
<line no="1">Henry was a renegade</line>
|
||||
<line no="2">Didn't like to play it safe</line>
|
||||
<line no="3">One component at a time</line>
|
||||
<line no="4">There's got to be a better way</line>
|
||||
<line no="5">Oh, people came from miles around</line>
|
||||
<line no="6">Searching for a steady job</line>
|
||||
<line no="7">Welcome to the Motor Town</line>
|
||||
<line no="8">Booming like an atom bomb</line>
|
||||
</verse>
|
||||
<verse name="verse 2" no="2">
|
||||
<line no="1">Oh, Henry was the end of the story</line>
|
||||
<line no="2">Then everything went wrong</line>
|
||||
<line no="3">And we'll return it to its former glory</line>
|
||||
<line no="4">But it just takes so long</line>
|
||||
</verse>
|
||||
</verses>
|
||||
<chorus>
|
||||
<line no="1">It's going to take a long time</line>
|
||||
<line no="2">It's going to take it, but we'll make it one day</line>
|
||||
<line no="3">It's going to take a long time</line>
|
||||
<line no="4">It's going to take it, but we'll make it one day</line>
|
||||
</chorus>
|
||||
</song>
|
||||
</msg>
|
||||
<msg mtype="alert" mpriority="1">
|
||||
<text>help me!</text>
|
||||
<song title="A Long Time" author="Mayer Hawthorne">
|
||||
<verses>
|
||||
<verse name="verse 1" no="1">
|
||||
<line no="1">Henry was a renegade</line>
|
||||
<line no="2">Didn't like to play it safe</line>
|
||||
<line no="3">One component at a time</line>
|
||||
<line no="4">There's got to be a better way</line>
|
||||
<line no="5">Oh, people came from miles around</line>
|
||||
<line no="6">Searching for a steady job</line>
|
||||
<line no="7">Welcome to the Motor Town</line>
|
||||
<line no="8">Booming like an atom bomb</line>
|
||||
</verse>
|
||||
</verses>
|
||||
</song>
|
||||
</msg>
|
||||
<msg mtype="alert" mpriority="1">
|
||||
<text>help me!</text>
|
||||
<song title="A Long Time" author="Mayer Hawthorne">
|
||||
<chorus>
|
||||
<line no="1">It's going to take a long time</line>
|
||||
<line no="2">It's going to take it, but we'll make it one day</line>
|
||||
<line no="3">It's going to take a long time</line>
|
||||
<line no="4">It's going to take it, but we'll make it one day</line>
|
||||
</chorus>
|
||||
</song>
|
||||
</msg>
|
||||
<msg mtype="alert" mpriority="1">
|
||||
<text>help me!</text>
|
||||
<song title="A Long Time" author="Mayer Hawthorne">
|
||||
<chorus>
|
||||
<line no="1">It's going to take a long time</line>
|
||||
<line no="2">It's going to take it, but we'll make it one day</line>
|
||||
<line no="3">It's going to take a long time</line>
|
||||
<line no="4">It's going to take it, but we'll make it one day</line>
|
||||
</song>
|
||||
</msg>
|
Reference in New Issue
Block a user