查看源代码 自定义函数
1 描述
XML 处理器提供了许多用于自定义的钩子。这些钩子被定义为函数对象,并且可以由调用者提供。
以下是可用的自定义函数。如果它们还可以访问自己的状态变量,则此状态的访问函数将在括号内标识
- 事件函数 (
xmerl_scan:event_state/[1,2]
) - 钩子函数 (
xmerl_scan:hook_state/[1,2]
) - 获取函数 (
xmerl_scan:fetch_state/[1,2]
) - 延续函数 (
xmerl_scan:cont_state/[1,2]
) - 规则函数 (
xmerl_scan:rules_state/[1,2]
) - 累加器函数
- 关闭函数
对于上述所有状态访问函数,带一个参数的函数(例如 event_state(GlobalState)
)将读取状态变量,而带两个参数的函数(例如: event_state(NewEventState, GlobalState)
)将修改它。
对于每个函数,描述以在 Option_list
中指定该函数的语法开始。 一般形式为 {Tag, Fun}
或 {Tag, Fun, LocalState}
。 第二种形式可用于初始化相关的状态变量。
1.1 用户状态
所有自定义函数都可以自由访问“用户状态”变量。当然,必须小心协调此状态的使用。建议那些实际上对“全局”用户状态没有任何贡献的函数,改用它们自己的状态变量。另一种选择(例如在 xmerl_eventp.erl
中使用)是让自定义函数共享其中一个局部状态(在 xmerl_eventp.erl
中,延续函数和获取函数都访问 cont_state。)
访问用户状态的函数
-
xmerl_scan:user_state(GlobalState)
-
xmerl_scan:user_state(UserState, GlobalState)
1.2 事件函数
{event_fun, fun()} | {event_fun, fun(), EventState}
事件函数在解析实体的开始和结束时被调用。它具有以下格式和语义
fun(Event, GlobalState) ->
EventState = xmerl_scan:event_state(GlobalState),
EventState2 = foo(Event, EventState),
GlobalState2 = xmerl_scan:event_state(EventState2, GlobalState)
end.
1.3 钩子函数
{hook_fun, fun()} | {hook_fun, fun(), HookState}
当处理器解析完一个完整的实体时,将调用钩子函数。格式和语义
fun(Entity, GlobalState) ->
HookState = xmerl_scan:hook_state(GlobalState),
{TransformedEntity, HookState2} = foo(Entity, HookState),
GlobalState2 = xmerl_scan:hook_state(HookState2, GlobalState),
{TransformedEntity, GlobalState2}
end.
事件函数、钩子函数和累加器函数之间的关系如下
- 首先为解析的实体调用带有“结束”事件的事件函数。
- 调用钩子函数,可能会重新格式化实体。
- 调用 acc 函数,以便(可选)将重新格式化的实体添加到其父元素的内容中。
1.4 获取函数
{fetch_fun, fun()} | {fetch_fun, fun(), FetchState}
调用获取函数以获取外部资源(例如 DTD)。
获取函数可以使用三个不同的返回值进行响应
Result ::=
{ok, {file, Filename}, NewGlobalState} |
{ok, {string, String}, NewGlobalState} |
{ok, not_fetched, NewGlobalState}
格式和语义
fun(URI, GlobalState) ->
FetchState = xmerl_scan:fetch_state(GlobalState),
Result = foo(URI, FetchState). % Result being one of the above
end.
1.5 延续函数
{continuation_fun, fun()} | {continuation_fun, fun(), ContinuationState}
当解析器遇到字节流末尾时,将调用延续函数。格式和语义
fun(Continue, Exception, GlobalState) ->
ContState = xmerl_scan:cont_state(GlobalState),
{Result, ContState2} = get_more_bytes(ContState),
case Result of
[] ->
GlobalState2 = xmerl_scan:cont_state(ContState2, GlobalState),
Exception(GlobalState2);
MoreBytes ->
{MoreBytes2, Rest} = end_on_whitespace_char(MoreBytes),
ContState3 = update_cont_state(Rest, ContState2),
GlobalState3 = xmerl_scan:cont_state(ContState3, GlobalState),
Continue(MoreBytes2, GlobalState3)
end
end.
1.6 规则函数
{rules, ReadFun : fun(), WriteFun : fun(), RulesState} |
{rules, Table : ets()}
规则函数负责将扫描器信息存储在规则数据库中。用户提供的规则函数可以选择将信息存储在 mnesia 中,或者可能存储在 user_state(RulesState) 中。
存在以下模式
- 如果用户未指定选项,则扫描器将创建一个 ets 表,并使用内置函数来读取和写入数据。扫描器完成后,将删除 ets 表。
- 如果用户通过
{rules, Table}
选项指定一个 ets 表,则扫描器将使用此表。扫描器完成后,它不会删除该表。 - 如果用户指定读取和写入函数,则扫描器将改为使用它们。
读取和写入函数的格式如下
WriteFun(Context, Name, Definition, ScannerState) -> NewScannerState.
ReadFun(Context, Name, ScannerState) -> Definition | undefined.
以下是扫描器当前正在写入的数据对象的摘要
上下文 | 键 值 | 定义 |
---|---|---|
notation | NotationName | {system, SL} | {public, PIDL, SL} |
elem_def | ElementName | #xmlElement{content = ContentSpec} |
parameter_entity | PEName | PEDef |
entity | EntityName | EntityDef |
表 1:扫描器数据对象
其中
ContentSpec ::= empty | any | ElemContent
ElemContent ::= {Mode, Elems}
Mode ::= seq | choice
Elems ::= [Elem]
Elem ::= '#PCDATA' | Name | ElemContent | {Occurrence, Elems}
Occurrence ::= '*' | '?' | '+'
注意:当 <Elem> 未用 <Occurrence> 包裹时,(Occurrence = once)是隐含的。
1.7 累加器函数
{acc_fun, fun()}
调用累加器函数以累加实体的内容。在解析非常大的文件时,可能不希望这样做。在这种情况下,可以提供一个简单不累加的 acc 函数。
请注意,甚至可以在累加之前修改已解析的实体,但这必须谨慎进行。 xmerl_scan 执行元素的后期处理以进行命名空间管理。因此,为了使其正常工作,该元素必须保持其原始结构。
acc 函数具有以下格式和语义
%% default accumulating acc fun
fun(ParsedEntity, Acc, GlobalState) ->
{[ParsedEntity|Acc], GlobalState}.
%% non-accumulating acc fun
fun(ParsedEntity, Acc, GlobalState) ->
{Acc, GlobalState}.
1.8 关闭函数
当文档(主文档或外部 DTD)已完全解析时,将调用关闭函数。当使用 xmerl_scan:file/[1,2]
启动 xmerl_scan 时,该文件将被完整读取,并在解析开始之前立即关闭,因此当调用关闭函数时,它实际上不需要关闭该文件。在这种情况下,关闭函数将是修改状态变量的好地方。
格式和语义
fun(GlobalState) ->
GlobalState1 = .... % state variables may be altered
2 示例
有关更多示例,请参见 xmerl_test.erl
。
2.1 处理空格
以下示例程序说明了扫描文档的三种方法
- 默认扫描,不触及空格
- 标准化空格
- 标准化空格,然后删除仅包含一个空格的文本元素。
-module(tmp).
-include("xmerl.hrl").
-export([file1/1, file2/1, file3/1]).
file1(F) -> xmerl_scan:file(F).
file2(F) -> xmerl_scan:file(F, [{space,normalize}]).
file3(F) -> Acc = fun(#xmlText{value = " ", pos = P}, Acc, S) -> {Acc, P,
S}; % new return format (X, Acc, S) -> {[X|Acc], S} end, xmerl_scan:file(F,
[{space,normalize}, {acc_fun, Acc}]).