| Home | zeGraph lib | Lua lib | Custom lib | Tutorials | Notes | XML Script | C-Talk | Z-Script |
Lesson 1
Lesson 2
Lesson 3
Lesson 4
Lesson 5
Lesson 6
Lesson 7
Lesson 8
Lesson 9
Lesson 10
Lesson 11
Lesson 12
Lesson 13
Lesson 14
Lesson 15
Lesson 16
Lesson 17
Lesson 18
Lesson 19

Lesson 11 Parsing XML

In addition to using the parse() function of zeArray to transfer data between a text string to numbers, you can use the zeExpat object, a very primitive binding of Lua with the EXPAT library, to parse XML string or to save data to and load data from a XML file. zeGraph also has zeHDF, zeNetCDF, and zeBIO objects for binary data serialization. You can also use zeSqlite to manage a relational datase.

Using zeExpat is no more difficult than understanding the callback mechanism. To explains the mechanism, let's create a XML file first:

require("register")

arr = zeUtl.new("int")
arr:range(1, 1, 9)
arr:reshape(3, 3)

file = io.open("array.txt", "a")    -- io is Lua's building function

file:write("‹array type = \"int\" nrec = \"3\" nvec = \"3\"›\n")
file:write("  ‹comment›Test XML input/output‹/comment›\n")
file:write("  ‹data›\n")
file:flush()

arr:print("  ", " ", "array.txt")

file:write("  ‹/data›\n")
file:write("‹/array›\n")
file:flush()

--[[Outputs in array.text file:
‹array type = "int" nrec = "3" nvec = "3"›
  ‹comment›Test XML input/output‹/comment›
  ‹data›
  1 4 7
  2 5 8
  3 6 9
  ‹/data›
‹/array›
]]

Note that the file should be opened for appending so that the Lua IO functions properly append outputs after the array prints its contents to the file. Now let's just implement the start-tag callback, end-tag callback, and data callback functions to examine the feedback contents from zeExpat:

require("register")

--The start, end, and data callback functions print whatever comes from the parser.

f1 = function(n, s, t)		-- start-tag callback
    print("f1", n, s)
    if (t) then
        for k, v in t do print("f1", k, v) end
    end
end

f2 = function(n, s)		-- end-tag callback
    print("f2", n, s)
end

f3 = function(n, s, l)		-- data callback
    print("f3", n, s, l)
end

-- Create a zeExpat object
xml = zeUtl.new("xml", "f1", "f2", "f3");

for line in io.lines("array.txt") do
    xml:parse(line)
end

--[[Outputs:
f1	1	array
f1	nvec	3
f1	type	int
f1	nrec	3
f3	1	  	2
f1	2	comment
f3	2	Test XML input/output	21
f2	2	comment
f3	1	  	2
f1	2	data
f3	2	  1 4 7	7
f3	2	  2 5 8	7
f3	2	  3 6 9	7
f3	2	  	2
f2	2	data
f2	1	array
]]
  • In parsing the first line in array.txt, zeExpat informs the start-tag callback f1() that the current depth is by 1, the tag is array, and the tag's attributes are nrec = "3", nvec = "3", and type = "int".
  • In parsing the second line, zeExpat informs the data callback f3() that the depth is 1 and the content is a blank string (before <comment>) of length 2; informs the start-tag callback f1() that the tag is comment; informs the data callback f3() that the depth is 2 and the content is a string of length 21; and informs the end-tag callback f3() that the depth is 2 and the tag is comment.
  • In parsing the third line, zeExpat informs the data callback f3() that the depth is 1 and the content is a blank string of length 2; and informs the start-tag callback f1() that the depth is 2 and the tag is data.
  • In parsing the fourth line, zeExpat informs the data callback f3() that the depth is 2 and the content is a string of length 7.
  • In parsing the fifth line, zeExpat informs the data callback f3() that the depth is 2 and the content is a string of length 7.
  • In parsing the sixth line zeExpat informs the data callback f3() that the depth is 2 and the content is a string of length 7.
  • In parsing the seventh line, zeExpat informs the data callback f3() that the depth is 2 and the content is a blank string of length 2; and informs the end-tag callback that depth is 2 and the tag is data.
  • In parsing the eighth line, zeExpat informs the end-tag callback f3() that the depth is 1 and the tag is array.

If you understand the parsing process described so far and notice that blanks before a tag are treated as data content, recovering data from a file becomes trivial, as shown below:

require("register")

ivec = 0
info = {}	--save tags and array info in this table

f1 = function(n, s, t)
    info[s] = 1
    if (t) then
        for k, v in t do info[k] = v; end
    end
    if (s == "array") then
        arr = zeUtl.new("int")
        arr:resize(tonumber(info.nrec), tonumber(info.nvec))
    end
end

f2 = function(n, s)
    info[s] = 0
end

f3 = function(n, s, l)
    --You should implement a better method to escape blank string
   if (info.data and l > 2) then
       arr:parse(ivec, s)
       ivec = ivec + 1
   end
end

xml = zeUtl.new("xml", "f1", "f2", "f3");

for line in io.lines("array.txt") do
    xml:parse(line)
end

arr:print()

--[[Outputs:
 1, 4, 7
 2, 5, 8
 3, 6, 9
]]