├── README.md
├── examples
├── file.json
├── file.xml
├── rssTransformer.go
└── transformer.go
└── xml2json.go
/README.md:
--------------------------------------------------------------------------------
1 | xml2json
2 | ========
3 |
4 | Transform a stream of XML into a stream of JSON, without requiring a schema or structs, written in Go (golang.org)
--------------------------------------------------------------------------------
/examples/file.json:
--------------------------------------------------------------------------------
1 | {"parent":{"obfuscatedChildrenKey":[{"child1":{"c1_attr1":"abc","c1_attr2":"def","obfuscatedChildrenKey":[{"gchild1":{"gc1_attr1":"hij","gc1_attr2":"klm","obfuscatedChildrenKey":[],"obfuscatedTextKey":"n"}},{"gchild2":{"obfuscatedChildrenKey":[]}},{"gchild3":{"gc3_attr1":"nop","obfuscatedChildrenKey":[]}}],"obfuscatedTextKey":"g"}},{"child2":{"c2_attr1":"def","c2_attr2":"ghi","obfuscatedChildrenKey":[],"obfuscatedTextKey":"xyz"}}]}}
--------------------------------------------------------------------------------
/examples/file.xml:
--------------------------------------------------------------------------------
1 | gnxyz
2 |
--------------------------------------------------------------------------------
/examples/rssTransformer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "os"
6 | "net/http"
7 | "github.com/textnode/xml2json"
8 | )
9 |
10 | func main() {
11 | log.Println("Started")
12 |
13 | var err error = nil
14 |
15 | resp, err := http.Get("http://search.twitter.com/search.rss?q=from%3Atextnode")
16 | if err != nil {
17 | log.Fatal(err)
18 | }
19 | defer resp.Body.Close()
20 |
21 | out, err := os.Create("rss.json")
22 | if err != nil {
23 | log.Fatal(err)
24 | os.Exit(-1)
25 | }
26 | defer out.Close()
27 |
28 | var x2j *xml2json.Xml2Json = xml2json.NewXml2Json("obfuscatedTextKey", "obfuscatedChildrenKey")
29 | err = x2j.Transform(resp.Body, out)
30 |
31 | log.Println("Finished: ", err)
32 | }
33 |
--------------------------------------------------------------------------------
/examples/transformer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 Darren Elwood http://www.textnode.com @textnode
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "log"
19 | "os"
20 | "github.com/textnode/xml2json"
21 | )
22 |
23 | func main() {
24 | log.Println("Started")
25 |
26 | var err error = nil
27 |
28 | in, err := os.Open("file.xml")
29 | if err != nil {
30 | log.Fatal(err)
31 | }
32 | defer in.Close()
33 |
34 | out, err := os.Create("file.json")
35 | if err != nil {
36 | log.Fatal(err)
37 | os.Exit(-1)
38 | }
39 | defer out.Close()
40 |
41 | var x2j *xml2json.Xml2Json = xml2json.NewXml2Json("obfuscatedTextKey", "obfuscatedChildrenKey")
42 | err = x2j.Transform(in, out)
43 |
44 | log.Println("Finished: ", err)
45 | }
46 |
--------------------------------------------------------------------------------
/xml2json.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 Darren Elwood http://www.textnode.com @textnode
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package xml2json
16 |
17 | import (
18 | "encoding/xml"
19 | "github.com/textnode/jsonStreamer"
20 | "io"
21 | )
22 |
23 | type Frame struct {
24 | collectedText []byte
25 | }
26 |
27 | func NewFrame() *Frame {
28 | return &Frame{collectedText: make([]byte, 0, 0)}
29 | }
30 |
31 | func (self *Frame) AddText(text []byte) {
32 | capacity := cap(self.collectedText)
33 | currentSize := len(self.collectedText)
34 | requiredSize := currentSize + len(text)
35 | if requiredSize > capacity {
36 | newCollectedText := make([]byte, requiredSize, requiredSize*2)
37 | copy(newCollectedText[0:currentSize], self.collectedText[0:currentSize])
38 | self.collectedText = newCollectedText
39 | }
40 | copy(self.collectedText[currentSize:requiredSize], text)
41 | }
42 |
43 | type Xml2Json struct {
44 | textKey string
45 | childrenKey string
46 | frames []*Frame
47 | }
48 |
49 | func NewXml2Json(textKey string, childrenKey string) *Xml2Json {
50 | var newXml2Json *Xml2Json = &Xml2Json{textKey: textKey, childrenKey: childrenKey, frames: make([]*Frame, 1, 10)}
51 | newXml2Json.frames[0] = NewFrame()
52 | return newXml2Json
53 | }
54 |
55 | func (self *Xml2Json) Transform(in io.Reader, out io.Writer) (err error) {
56 | var decoder *xml.Decoder = xml.NewDecoder(in)
57 | var encoder *jsonStreamer.JsonStreamer = jsonStreamer.NewJsonStreamer(out)
58 |
59 | var token xml.Token
60 | token, err = decoder.Token()
61 |
62 | for ; err == nil; token, err = decoder.Token() {
63 | var currentFrameIndex int = len(self.frames) - 1
64 | var currentFrame *Frame = self.frames[currentFrameIndex]
65 |
66 | switch specific := token.(type) {
67 | case xml.StartElement:
68 | err = encoder.StartObject()
69 | if err != nil {
70 | return
71 | }
72 |
73 | self.frames = append(self.frames, NewFrame())
74 | currentFrameIndex = len(self.frames) - 1
75 | currentFrame = self.frames[currentFrameIndex]
76 |
77 | err = encoder.WriteKey(specific.Name.Local)
78 | if err != nil {
79 | return
80 | }
81 | err = encoder.StartObject()
82 | if err != nil {
83 | return
84 | }
85 |
86 | for _, attr := range specific.Attr {
87 | err = encoder.WriteKey(attr.Name.Local)
88 | if err != nil {
89 | return
90 | }
91 | err = encoder.WriteStringValue(&(attr.Value))
92 | if err != nil {
93 | return
94 | }
95 | }
96 |
97 | err = encoder.WriteKey(self.childrenKey)
98 | if err != nil {
99 | return
100 | }
101 | err = encoder.StartArray()
102 | if err != nil {
103 | return
104 | }
105 |
106 | case xml.EndElement:
107 | err = encoder.EndArray() //close children
108 | if err != nil {
109 | return
110 | }
111 |
112 | if 0 < len(currentFrame.collectedText) {
113 | err = encoder.WriteKey(self.textKey)
114 | if err != nil {
115 | return
116 | }
117 | valueString := string(currentFrame.collectedText)
118 | err = encoder.WriteStringValue(&(valueString))
119 | if err != nil {
120 | return
121 | }
122 | }
123 | self.frames = self.frames[:len(self.frames)-1]
124 | err = encoder.EndObject()
125 | if err != nil {
126 | return
127 | }
128 | err = encoder.EndObject()
129 | if err != nil {
130 | return
131 | }
132 | case xml.CharData:
133 | currentFrame.AddText(specific)
134 | //case xml.Comment:
135 | //case xml.ProcInst:
136 | //case xml.Directive:
137 | }
138 | }
139 | return
140 | }
141 |
--------------------------------------------------------------------------------