├── README
├── README.md
├── xmlSimple.lua
└── xmlTest.lua
/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cluain/Lua-Simple-XML-Parser/2c318f3b01121dad61dee34b8c80c970ff938192/README
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | 1. Copy the xmlSimple.lua file to your project.
4 | 2. Create a local variable `local xml = require("xmlSimple.lua").newParser()`
5 | 3. Read xml using `xml:ParseXmlText(xmlString)` or `xml:loadFile(xmlFilename, base)`
6 |
7 | # Parsing XML
8 |
9 | ``` xml
10 |
11 |
12 | eight
13 | twelve
14 |
15 | ```
16 | You can access values in two ways:
17 |
18 | Using the simple method:
19 |
20 | ``` lua
21 | xml.test["@one"] == "two"
22 | xml.test.nine["@ten"] == "eleven"
23 | xml.test.nine:value() == "twelve"
24 | xml.test.three[1]["@four"][1] == "five"
25 | xml.test.three[1]["@four"][2] == "six"
26 | xml.test.three[2]:value() == "eight"
27 | ```
28 |
29 | or if your XML is a little bit more complicated you can do it like this:
30 |
31 | ``` lua
32 | xml:children()[1]:name() == "test"
33 | xml:children()[1]:children()[2]:value() == "eight"
34 | xml:properties()[1] == {name = "one", value = "two"}
35 | ```
36 |
37 | # Limitations
38 |
39 | There's no support for namespaces. When I see namespaces I immediately start to remember days when I worked at corporate. We had to use namespaces only because XML was so convoluted we would not be able to handle it without them. In the end XML parsing took longer for some APIs then actual logic of the API.
40 | If you're in this situation it is better to step back and do something about it rather than asking for namespace support.
41 | I am using this module to read fairly simple XML. Even if it is a large XML string, the structure is still simple, so I was not able to test it properly. Please create a new Issue if you spot a problem.
42 | Please take a loook at xmlTest.lua for an example of use.
43 |
44 | # Final notes
45 |
46 | This is a modified version of [Corona-XML-Module](https://github.com/jonbeebe/Corona-XML-Module) by Jonathan Beebe which in turn is based on Alexander Makeev's Lua-only XML parser found [here](http://lua-users.org/wiki/LuaXml)
47 |
--------------------------------------------------------------------------------
/xmlSimple.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | ---------------------------------------------------------------------------------
4 | ---------------------------------------------------------------------------------
5 | --
6 | -- xml.lua - XML parser for use with the Corona SDK.
7 | --
8 | -- version: 1.2
9 | --
10 | -- CHANGELOG:
11 | --
12 | -- 1.2 - Created new structure for returned table
13 | -- 1.1 - Fixed base directory issue with the loadFile() function.
14 | --
15 | -- NOTE: This is a modified version of Alexander Makeev's Lua-only XML parser
16 | -- found here: http://lua-users.org/wiki/LuaXml
17 | --
18 | ---------------------------------------------------------------------------------
19 | ---------------------------------------------------------------------------------
20 | function newParser()
21 |
22 | XmlParser = {};
23 |
24 | function XmlParser:ToXmlString(value)
25 | value = string.gsub(value, "&", "&"); -- '&' -> "&"
26 | value = string.gsub(value, "<", "<"); -- '<' -> "<"
27 | value = string.gsub(value, ">", ">"); -- '>' -> ">"
28 | value = string.gsub(value, "\"", """); -- '"' -> """
29 | value = string.gsub(value, "([^%w%&%;%p%\t% ])",
30 | function(c)
31 | return string.format("%X;", string.byte(c))
32 | end);
33 | return value;
34 | end
35 |
36 | function XmlParser:FromXmlString(value)
37 | value = string.gsub(value, "([%x]+)%;",
38 | function(h)
39 | return string.char(tonumber(h, 16))
40 | end);
41 | value = string.gsub(value, "([0-9]+)%;",
42 | function(h)
43 | return string.char(tonumber(h, 10))
44 | end);
45 | value = string.gsub(value, """, "\"");
46 | value = string.gsub(value, "'", "'");
47 | value = string.gsub(value, ">", ">");
48 | value = string.gsub(value, "<", "<");
49 | value = string.gsub(value, "&", "&");
50 | return value;
51 | end
52 |
53 | function XmlParser:ParseArgs(node, s)
54 | string.gsub(s, "(%w+)=([\"'])(.-)%2", function(w, _, a)
55 | node:addProperty(w, self:FromXmlString(a))
56 | end)
57 | end
58 |
59 | function XmlParser:ParseXmlText(xmlText)
60 | local stack = {}
61 | local top = newNode()
62 | table.insert(stack, top)
63 | local ni, c, label, xarg, empty
64 | local i, j = 1, 1
65 | while true do
66 | ni, j, c, label, xarg, empty = string.find(xmlText, "<(%/?)([%w_:]+)(.-)(%/?)>", i)
67 | if not ni then break end
68 | local text = string.sub(xmlText, i, ni - 1);
69 | if not string.find(text, "^%s*$") then
70 | local lVal = (top:value() or "") .. self:FromXmlString(text)
71 | stack[#stack]:setValue(lVal)
72 | end
73 | if empty == "/" then -- empty element tag
74 | local lNode = newNode(label)
75 | self:ParseArgs(lNode, xarg)
76 | top:addChild(lNode)
77 | elseif c == "" then -- start tag
78 | local lNode = newNode(label)
79 | self:ParseArgs(lNode, xarg)
80 | table.insert(stack, lNode)
81 | top = lNode
82 | else -- end tag
83 | local toclose = table.remove(stack) -- remove top
84 |
85 | top = stack[#stack]
86 | if #stack < 1 then
87 | error("XmlParser: nothing to close with " .. label)
88 | end
89 | if toclose:name() ~= label then
90 | error("XmlParser: trying to close " .. toclose.name .. " with " .. label)
91 | end
92 | top:addChild(toclose)
93 | end
94 | i = j + 1
95 | end
96 | local text = string.sub(xmlText, i);
97 | if #stack > 1 then
98 | error("XmlParser: unclosed " .. stack[#stack]:name())
99 | end
100 | return top
101 | end
102 |
103 | function XmlParser:loadFile(xmlFilename, base)
104 | if not base then
105 | base = system.ResourceDirectory
106 | end
107 |
108 | local path = system.pathForFile(xmlFilename, base)
109 | local hFile, err = io.open(path, "r");
110 |
111 | if hFile and not err then
112 | local xmlText = hFile:read("*a"); -- read file content
113 | io.close(hFile);
114 | return self:ParseXmlText(xmlText), nil;
115 | else
116 | print(err)
117 | return nil
118 | end
119 | end
120 |
121 | return XmlParser
122 | end
123 |
124 | function newNode(name)
125 | local node = {}
126 | node.___value = nil
127 | node.___name = name
128 | node.___children = {}
129 | node.___props = {}
130 |
131 | function node:value() return self.___value end
132 | function node:setValue(val) self.___value = val end
133 | function node:name() return self.___name end
134 | function node:setName(name) self.___name = name end
135 | function node:children() return self.___children end
136 | function node:numChildren() return #self.___children end
137 | function node:addChild(child)
138 | if self[child:name()] ~= nil then
139 | if type(self[child:name()].name) == "function" then
140 | local tempTable = {}
141 | table.insert(tempTable, self[child:name()])
142 | self[child:name()] = tempTable
143 | end
144 | table.insert(self[child:name()], child)
145 | else
146 | self[child:name()] = child
147 | end
148 | table.insert(self.___children, child)
149 | end
150 |
151 | function node:properties() return self.___props end
152 | function node:numProperties() return #self.___props end
153 | function node:addProperty(name, value)
154 | local lName = "@" .. name
155 | if self[lName] ~= nil then
156 | if type(self[lName]) == "string" then
157 | local tempTable = {}
158 | table.insert(tempTable, self[lName])
159 | self[lName] = tempTable
160 | end
161 | table.insert(self[lName], value)
162 | else
163 | self[lName] = value
164 | end
165 | table.insert(self.___props, { name = name, value = self[name] })
166 | end
167 |
168 | return node
169 | end
170 |
--------------------------------------------------------------------------------
/xmlTest.lua:
--------------------------------------------------------------------------------
1 | ---
2 | -- User: krystian
3 | -- Company: Cluain Krystian Szczęsny
4 | -- Date: 12/21/11
5 | -- Time: 7:40 PM
6 | --
7 |
8 | local xml = require("xmlSimple").newParser()
9 |
10 | local testXml = ''
11 | testXml = testXml .. ''
12 | testXml = testXml .. ''
13 | testXml = testXml .. 'testThreeValue'
14 | testXml = testXml .. ''
15 | testXml = testXml .. ''
16 | testXml = testXml .. 'testThreeValueTwo'
17 | testXml = testXml .. ''
18 | testXml = testXml .. ''
19 | testXml = testXml .. 'testFourValue'
20 | testXml = testXml .. ''
21 | testXml = testXml .. ''
22 | testXml = testXml .. ''
23 | testXml = testXml .. ''
24 | testXml = testXml .. ''
25 | testXml = testXml .. ''
26 | testXml = testXml .. ''
27 | testXml = testXml .. ''
28 | testXml = testXml .. 'testTwoValue'
29 | testXml = testXml .. ''
30 | testXml = testXml .. ''
31 |
32 |
33 | local parsedXml = xml:ParseXmlText(testXml)
34 |
35 |
36 | if parsedXml.testOne == nil then error("Node not created") end
37 | if parsedXml.testOne:name() ~= "testOne" then error("Node name not set") end
38 | if parsedXml.testOne.testTwo == nil then error("Child node not created") end
39 | if parsedXml.testOne.testTwo:name() ~= "testTwo" then error("Child node name not set") end
40 | if parsedXml.testOne.testTwo:value() ~= "testTwoValue" then error("Node value not set") end
41 | if parsedXml.testOne.testTwo.test_Four:value() ~= "testFourValue" then error("Second child node value not set") end
42 | if parsedXml.testOne["@param"] ~= "param1value" then error("Parameter not set") end
43 | if parsedXml.testOne.testTwo["@paramTwo"] ~= "param2value" then error("Second child node parameter not set") end
44 | if parsedXml.testOne.testTwo.test_Four["@something"] ~= "else" then error("Deepest node parameter not set") end
45 |
46 | -- duplicate names tests
47 | if parsedXml.testOne.testTwo.testThree[1]:value() ~= "testThreeValue" then error("First of duplicate nodes value not set") end
48 | if parsedXml.testOne.testTwo.testThree[2]:value() ~= "testThreeValueTwo" then error("Second of duplicate nodes value not set") end
49 | if parsedXml.testOne.testTwo.testThree[2]["@duplicate"][1] ~= "one" then error("First of duplicate parameters not set") end
50 | if parsedXml.testOne.testTwo.testThree[2]["@duplicate"][2] ~= "two" then error("Second of duplicate parameters not set") end
51 |
52 | -- deep element test
53 |
54 | if parsedXml.testOne.testTwo.testFive.testFiveDeep.testFiveEvenDeeper.testSix['@someParam'] ~= "someValue" then error("Deep test error") end
55 |
56 | -- node functions test
57 | local node = require("xmlSimple").newNode("testName")
58 |
59 | if node:name() ~= "testName" then error("Node creation failed") end
60 |
61 | node:setName("nameTest")
62 | if node:name() ~= "nameTest" then error("Name function test failed") end
63 |
64 | node:setValue("valueTest")
65 | if node:value() ~= "valueTest" then error("Value function test failed") end
66 |
67 | local childNode = require("xmlSimple").newNode("parent")
68 |
69 | node:addChild(childNode)
70 |
71 | if type(node:children()) ~= "table" then error("children function test failed") end
72 | if #node:children() ~= 1 then error("AddChild function test failed") end
73 | if node:numChildren() ~= 1 then error("numChildren function test failed") end
74 |
75 |
76 | node:addProperty("name", "value")
77 |
78 | if type(node:properties()) ~= "table" then error("properties function test failed") end
79 | if #node:properties() ~= 1 then error("Add property function test failed") end
80 | if node:numProperties() ~= 1 then error("Num properties function test failed") end
81 |
82 | print("Tests passed")
83 |
--------------------------------------------------------------------------------