Xml Iter

lua-users home
wiki

xmliter: 遍历 XML 树

(这是 LazyKit 的一部分。)

此包提供了一些工具,用于遍历 XmlTree 的子元素。

xattrpairs(tree)

返回一个遍历 tree 属性的迭代器,返回属性名称和值。请注意,这仅返回字符串类型的键。(LuaExpat? 使用数字键来标记从 DTD 默认的属性。)

xmliter.getn(tree)

计算 tree 的子元素数量;大致等同于 table.getn。这是必要的,因为 table.getn(tree) 不会显式调用 tree.n,而是使用 rawget(tree, "n")。复杂的树实现可能需要使用元表调用来查找子元素数量。

xpairs(tree)

返回一个遍历 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" 没有被打印)。

xnpairs(tree)

返回一个遍历 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 

通用过滤

xmliter.switch(parent, ftable, [opts])

遍历 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] 处理程序,这将很有用。


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2004 年 2 月 29 日凌晨 12:30 GMT (差异)