├── 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 | --------------------------------------------------------------------------------