Xml Iter |
|
(这是 LazyKit 的一部分。)
此包提供了一些工具,用于遍历 XmlTree 的子元素。
返回一个遍历 tree
属性的迭代器,返回属性名称和值。请注意,这仅返回字符串类型的键。(LuaExpat? 使用数字键来标记从 DTD 默认的属性。)
计算 tree
的子元素数量;大致等同于 table.getn
。这是必要的,因为 table.getn(tree)
不会显式调用 tree.n
,而是使用 rawget(tree, "n")
。复杂的树实现可能需要使用元表调用来查找子元素数量。
返回一个遍历 tree
的迭代器,返回每个索引及其子元素。示例
parent = lazytree.parsestring("<p>a<z>cdef</z>b</p>") for i,x in xpairs(parent) do if type(x) == "string" then print("string:", x) else print("tag:", x.name) end end
打印
string: a tag: z string: b
请注意,它不会深入到子元素(因为 "cdef" 没有被打印)。
返回一个遍历 tree
的迭代器,忽略字符数据元素。它返回一个索引、子树和元素名称(可以忽略)。
for i,x in xnpairs(parent) do print("tag:", x.name) end for i,x,name in xnpairs(parent) do print("tag:", name) end
以上两种方法都会打印
tag: z
遍历 parent
的子元素,使用 ftable
中的函数定义。
parent
的每个子元素都会在 ftable
中查找。对于子元素 "<foo/>
",会调用函数 ftable.foo(child, parent)
。对于字符数据,会调用 ftable[""](str, parent)
。如果找到未知标签,则会调用函数 ftable[true](parent, child)
。
如果 ftable
中不存在这样的条目,则会忽略该子元素(除非设置了某些选项)。
如果处理程序返回一个真值,switch
会停止迭代并返回一个(可能不同的)真值以及任何第二个返回值。(与消费的交互待定,可能使用第一个返回值作为要跳出多少级的计数。)
示例
s = '<log><entry time="12:30"/><checkpoint/><entry time="12:35"/></log>' parent = lazytree.parsestring(s) ftable = { entry=function (entry, parent) print (entry.attr.time) end } xmliter.switch(parent, ftable)
打印
12:30 12:35
(请注意,由于我们不关心父元素,因此该函数可以声明为 "function (entry)
"。)
条目可能包含嵌套的 ftable
而不是函数;switch
(或 switch_c
)会使用嵌套的 ftable
递归调用。
示例
s = [[ <log> <entry id='0'> <time clock="12:50"/> <msg text="foo"/> <extra/> </entry> </log>]] parent = lazytree.parsestring(s) ftable = { entry={ time=function (time) print (time.attr.clock) end; msg=function (msg) print (msg.attr.text) end; } } xmliter.switch(parent, ftable)
打印
12:50 foo
为了帮助使用嵌套的 ftable,ftable[0](parent, [previous_parent])
在任何子节点被处理之前被调用,而 ftable[-1](parent, [previous_parent])
在所有子节点被处理之后被调用。
parent = lazytree.parsestring(s) ftable = { entry={ [0]=function (entry) print("id ", entry.attr.id) entry.message_txt = "(no message)" entry.time_txt = "(no time)" entry.level_txt = "(no level)" end; time=function (time, entry) entry.time_txt = time.attr.clock end; msg=function (msg, entry) entry.message_txt = msg.attr.text end; [-1]=function (entry) print("message", entry.message_txt, entry.time_txt, entry.level_txt) end; } } xmliter.switch(parent, ftable)
打印
id 0 message foo 12:50 (no level)
这利用了 XML 树不介意多余的表格条目这一事实(只要你避免使用 "n
"、"attr
" 和 "name
" 以及以下划线开头的键)。
然而,嵌套表格可能不是表达代码的最简洁方式。一种更简单的写法是
parent = lazytree.parsestring(s) ftable = { entry=function (entry) print("id", entry.attr.id) local v = xmlview.element(entry) local message_txt = "(no message)" local time_txt = "(no time)" local level_txt = "(no level)" if v.time then time_txt = v.time.attr.clock end if v.msg then message_txt = v.msg.attr.text end print("message", message_txt, time_txt, level_txt) end } xmliter.switch(parent, ftable)
任何使用 [0]
和 [-1]
的地方都可以用一个函数来重写,该函数执行 [0]
操作,递归调用 switch
,并执行 [-1]
操作。
可以通过将 [true]
操作设置为 ftable 本身来执行元素的递归搜索。例如
parent = lazytree.parsefile("xhtml-spec.xml") local count = 0 local ftable ftable = { a=function (a) if a.attr.href then count = count + 1 end -- uncomment to search for <a> elements inside other <a> elements -- xmliter.switch(a, ftable) end } ftable[true] = ftable xmliter.switch(parent, ftable) print(count)
(注意,我们不能写 "local ftable={... switch(ftable)
",因为 ftable
在自身范围内不可用。)
opts
表格控制处理的各种选项。
如果设置了 opts.no_chardata
,任何意外的字符数据(即,没有被 ftable[""]
条目处理的字符数据)都会导致错误。
如果设置了 opts.no_tags
,任何意外的子元素(那些在 ftable
中没有提到或没有被 ftable[true]
条目处理的子元素)都会导致错误。
如果设置了 opts.parent
,它将作为 parent
参数的父节点传递给函数。当递归调用 switch
时,如果新的 ftable 包含 [0]
或 [-1]
处理程序,这将很有用。