查看源代码 自定义函数

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.

事件函数、钩子函数和累加器函数之间的关系如下

  1. 首先为解析的实体调用带有“结束”事件的事件函数。
  2. 调用钩子函数,可能会重新格式化实体。
  3. 调用 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.

以下是扫描器当前正在写入的数据对象的摘要

上下文键 值定义
notationNotationName{system, SL} | {public, PIDL, SL}
elem_defElementName#xmlElement{content = ContentSpec}
parameter_entityPENamePEDef
entityEntityNameEntityDef

表 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 处理空格

以下示例程序说明了扫描文档的三种方法

  1. 默认扫描,不触及空格
  2. 标准化空格
  3. 标准化空格,然后删除仅包含一个空格的文本元素。
-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}]).