1. xmerl
1 xmerl
1.1游戏攻略
特征
该xmerl
XML解析器能够根据XML 1.0标准的XML文档进行解析。默认情况下,它执行格式正确的解析(语法检查和格式良好的约束检查)。也可以使用xmerl
作为验证解析器(根据引用的DTD验证约束条件)。通过例如xmerl
_xs模块,可以将解析结果转换为其他格式,例如文本,HTML,XML等。
概述
本文档没有介绍XML。有许多可用的书籍可以从不同的角度描述XML。在www.W3.org
站点,您会发现XML 1.0 specification
以及其他相关规格。有一个站点是您可以找到关于xml和相关规范的教程是ZVON.org
...
但是,在这里,您将看到一些如何使用和可以使用xmerl的示例。用户界面的详细描述可在参考手册中找到。
xmerl中有两个已知的缺点:
- 它不能通过URL引用检索Internet上的外部实体,只能检索本地文件系统中的资源。
- xmerl可以解析Unicode编码的数据。但是,它在标记名、属性名和其他编码Unicode字符的标记名称上失败,而这些字符不是在ASCII上映射的。
通过解析XML文档,您将得到一个记录,显示文档的结构,作为返回值。记录还保存文档的数据。例如,xmerl在以下场景中使用非常方便:
您需要从XML文档中检索数据。您的Erlang软件可以通过从解析所接收的数据结构中提取数据来处理XML文档中的信息。
还可以使用xmerl对解析的XML进行进一步处理。如果要将XML文档的格式更改为HTML、文本或其他XML格式,则可以将其转换。在xmerl中有对此类转换的支持。
还可以将任意数据转换为XML。因此,例如,它很容易被人类阅读。在本例中,首先从数据中创建xmerl数据结构,然后将其转换为XML。
您可以在下面找到这三个用法示例。
1.2 xmerl用户界面数据结构
中定义了xmerl用于保存已解析数据的下列记录xmerl.hrl
成功解析的结果是一个元组{DataStructure,M}
。M
是XM
L制作M
isc,它是文档元素之后的标记。它“按原样”返回。DataStructure
是一个xmlElement
记录,即在其他有田name
,parents
,attributes
和content
这样的:
#xmlElement{name=Name,
...
parents=Parents,
...
attributes=Attrs,
content=Content,
...}
元素的名称在name
场。在parents
字段是保存的父元素的名称。父元素是一个元组列表,每个元组中的第一个元素是父元素的名称。列表的顺序相反。
记录xmlAttribute
在字段中保存属性的名称和值。name
和value
元素的所有属性都是字段中的xmlAttribute
列表。attributes
xmlElement记录。
大content
顶部元素的字段是显示文档结构和数据的记录列表。如果它是一个简单的文档,如:
<?xml version="1.0"?>
<dog>
Grand Danois
</dog>
解析结果将是:
#xmlElement{name = dog,
...
parents = [],
...
attributes = [],
content = [{xmlText,[{dog,1}],1,[],"\
Grand Danois\
",text}],
...
}
其中顶部元素的内容是:[{xmlText,[{dog,1}],1,[],"\ Grand Danois\ ",text}]
文本将在xmlText
记录。不过,文档通常更复杂,在这种情况下,顶层元素的内容将是一个嵌套结构,其中包含xmlElement记录,而这些记录可能具有复杂的内容。所有这些都反映了XML文档的结构。
标记之间的空格字符为space
,tab
并被line feed
标准化并作为xmlText记录返回。
错误
不成功的解析会导致错误,这可能是一个元组。{error,Reason}
或出口:{'EXIT',Reason}
.根据xml 1.0标准,fatal error
和error
情况。致命错误必
在错误发生时,由符合标准的解析器检测到。五月
被发现。这两个类型的错误都被这个版本的xmerl报告为致命错误,通常是作为退出。
1.3开始
在下面的示例中,我们使用了XML文件“Motorcycles.xml”和相应的DTD“Motorcycles.dtd”。xml看起来是这样的:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE motorcycles SYSTEM "motorcycles.dtd">
<motorcycles>
<bike year="2000" color="black">
<name>
<manufacturer>Suzuki</manufacturer>
<brandName>Suzuki VL 1500</brandName>
<additionalName>Intruder</additionalName>
</name>
<engine>V-engine, 2-cylinders, 1500 cc</engine>
<kind>custom</kind>
<drive>cardan</drive>
<accessories>Sissy bar, luggage carrier,V&H exhaust pipes</accessories>
</bike>
<date>2004.08.25</date>
<bike year="1983" color="read pearl">
<name>
<manufacturer>Yamaha</manufacturer>
<brandName>XJ 400</brandName>
</name>
<engine>4 cylinder, 400 cc</engine>
<kind>alround</kind>
<drive>chain</drive>
<comment>Good shape!</comment>
</bike>
</motorcycles>
和摩托车。dtd看起来:
<?xml version="1.0" encoding="utf-8" ?>
<!ELEMENT motorcycles (bike,date?)+ >
<!ELEMENT bike (name,engine,kind,drive, accessories?,comment?) >
<!ELEMENT name (manufacturer,brandName,additionalName?) >
<!ELEMENT manufacturer (#PCDATA)>
<!ELEMENT brandName (#PCDATA)>
<!ELEMENT additionalName (#PCDATA)>
<!ELEMENT engine (#PCDATA)>
<!ELEMENT kind (#PCDATA)>
<!ELEMENT drive (#PCDATA)>
<!ELEMENT comment (#PCDATA)>
<!ELEMENT accessories (#PCDATA)>
<!-- Date of the format yyyy.mm.dd -->
<!ELEMENT date (#PCDATA)>
<!ATTLIST bike year NMTOKEN #REQUIRED
color NMTOKENS #REQUIRED
condition (useless | bad | serviceable | moderate | good |
excellent | new | outstanding) "excellent" >
如果您想解析XML文件Motorcycles.xml,可以在Erlang shell中运行它,如下所示:
3> {ParsResult,Misc}=xmerl_scan:file("motorcycles.xml").
{{xmlElement,motorcycles,
motorcycles,
[],
{xmlNamespace,[],[]},
[],
1,
[],
[{xmlText,[{motorcycles,1}],1,[],"\
",text},
{xmlElement,bike,
bike,
[],
{xmlNamespace,[],[]},
[{motorcycles,1}],
2,
[{xmlAttribute,year,[],[],[],[]|...},
{xmlAttribute,color,[],[],[]|...}],
[{xmlText,[{bike,2},{motorcycles|...}],
1,
[]|...},
{xmlElement,name,name,[]|...},
{xmlText,[{...}|...],3|...},
{xmlElement,engine|...},
{xmlText|...},
{...}|...],
[],
".",
undeclared},
...
],
[],
".",
undeclared},
[]}
4>
如果您将xml文档作为字符串接收,则可以通过xmerl_scan:string/1
.文件/2和字符串/2都存在于第二个参数是解析器选项列表的地方,请参见reference manual
...
1.4示例:从xml内容中提取数据
在本例中,请考虑要检查XML文件中的特定数据的情况。例如,您需要检查每辆摩托车记录了多长时间。
查看DTD并观察与此DTD一致的XML文档的结构必须具有一个摩托车元素(根元素)。摩托车元素必须至少有一个自行车元素。每个自行车元素后,它可能是一个日期元素。日期元素的内容是#PCDATA(Parsed Character DATA),即原始文本。注意,如果#PCDATA必须有一个"<"或一个"&"字符必须写成"<",并"&"分别。其他字符实体的存在类似于HTML和SGML中的字符实体。
如果您成功地解析了XML文件,并对AS进行了验证,则如下所示:xmerl_scan:file('motorcycles.xml',[{validation,true}])
您知道XML文档是有效的,并且根据DTD具有结构。
因此,知道允许的结构很容易编写一个程序,它遍历数据结构并选择具有名称日期的xmlElements记录中的信息。
观察空白:标记之间的每个空格、制表符或行提要都会导致xmlText记录。
1.5示例:从任意数据创建xml
对于这项任务,有不止一条路可走。“蛮力”方法是创建所需的记录,并在适当元素的内容和属性字段中输入数据。
在xmerl中,通过“简单形式”格式对此有支持。您可以将数据放入一个简单的数据结构中,并将其输入xmerl:export_simple(Content,Callback,RootAttributes)
内容可能是简单形式和xmerl记录的混合,如xmlElement和xmlText。
这些类型有:
- 内容=元素
- 回调=ATOM%28%29
- RootAttributes=属性
元素是下列任何一种:
- {标记、属性、内容}
- {标记、内容}
- 标签
- IOString
- #xmlText{}
- #xmlElement{}
- #xmlPI{}
- #xmlComment{}
- #xmlDecl{}
简单的结构是{Tag, Attributes, Content}
,,,{Tag, Content}
或Tag
其中:
- Tag = atom()
- 属性={名称,值}#xmlAttribute{}
- Name = atom()
- Value = IOString | atom() | integer()
另见参考手册xmerl
如果您想添加有关2003年的黑色哈雷戴维森1200 cc Sportster摩托车的信息,该摩托车的形状在Motorcycles.xml文档中是新的,您可以将数据放入一个简单的数据结构中,如下所示:
Data =
{bike,
[{year,"2003"},{color,"black"},{condition,"new"}],
[{name,
[{manufacturer,["Harley Davidsson"]},
{brandName,["XL1200C"]},
{additionalName,["Sportster"]}]},
{engine,
["V-engine, 2-cylinders, 1200 cc"]},
{kind,["custom"]},
{drive,["belt"]}]}
为了将这些数据附加到Motorcycles.xml文档的末尾,您必须解析该文件并将数据添加到根元素内容的末尾。
{RootEl,Misc}=xmerl_scan:file('motorcycles.xml'),
#xmlElement{content=Content} = RootEl,
NewContent=Content++lists:flatten([Data]),
NewRootEl=RootEl#xmlElement{content=NewContent},
然后,您可以通过导出运行它。[医]简单/2功能:
{ok,IOF}=file:open('new_motorcycles.xml',[write]),
Export=xmerl:export_simple([NewRootEl],xmerl_xml),
io:format(IOF,"~s~n",[lists:flatten(Export)]),
结果是:
<?xml version="1.0"?><motorcycles>
<bike year="2000" color="black">
<name>
<manufacturer>Suzuki</manufacturer>
<brandName>Suzuki VL 1500</brandName>
<additionalName>Intruder</additionalName>
</name>
<engine>V-engine, 2-cylinders, 1500 cc</engine>
<kind>custom</kind>
<drive>cardan</drive>
<accessories>Sissy bar, luggage carrier,V&H exhaust pipes</accessories>
</bike>
<date>2004.08.25</date>
<bike year="1983" color="read pearl">
<name>
<manufacturer>Yamaha</manufacturer>
<brandName>XJ 400</brandName>
</name>
<engine>4 cylinder, 400 cc</engine>
<kind>alround</kind>
<drive>chain</drive>
<comment>Good shape!</comment>
</bike>
<bike year="2003" color="black" condition="new"><name><manufacturer>Harley Davidsson</manufacturer><brandName>XL1200C</brandName><additionalName>Sportster</additionalName></name><engine>V-engine, 2-cylinders, 1200 cc</engine><kind>custom</kind><drive>belt</drive></bike></motorcycles>
如果获得类似于原始文档的缩进和换行符很重要,则必须在适当的位置添加带有空格和换行符值的#xmlText{}记录。在引用DTD的地方保留原始Prolog也是必要的。如果是这样,则可以传递RootAttribute{prolog,Value}
到export_simple/3
下面的示例代码修复了上一个示例中的这些更改:
Data =
[#xmlText{value=" "},
{bike,[{year,"2003"},{color,"black"},{condition,"new"}],
[#xmlText{value="\
"},
{name,[#xmlText{value="\
"},
{manufacturer,["Harley Davidsson"]},
#xmlText{value="\
"},
{brandName,["XL1200C"]},
#xmlText{value="\
"},
{additionalName,["Sportster"]},
#xmlText{value="\
"}]},
{engine,["V-engine, 2-cylinders, 1200 cc"]},
#xmlText{value="\
"},
{kind,["custom"]},
#xmlText{value="\
"},
{drive,["belt"]},
#xmlText{value="\
"}]},
#xmlText{value="\
"}],
...
NewContent=Content++lists:flatten([Data]),
NewRootEl=RootEl#xmlElement{content=NewContent},
...
Prolog = ["<?xml version=\\"1.0\\" encoding=\\"utf-8\\" ?>
<!DOCTYPE motorcycles SYSTEM \\"motorcycles.dtd\\">\
"],
Export=xmerl:export_simple([NewRootEl],xmerl_xml,[{prolog,Prolog}]),
...
结果是:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE motorcycles SYSTEM "motorcycles.dtd">
<motorcycles>
<bike year="2000" color="black">
<name>
<manufacturer>Suzuki</manufacturer>
<brandName>Suzuki VL 1500</brandName>
<additionalName>Intruder</additionalName>
</name>
<engine>V-engine, 2-cylinders, 1500 cc</engine>
<kind>custom</kind>
<drive>cardan</drive>
<accessories>Sissy bar, luggage carrier,V&H exhaust pipes</accessories>
</bike>
<date>2004.08.25</date>
<bike year="1983" color="read pearl">
<name>
<manufacturer>Yamaha</manufacturer>
<brandName>XJ 400</brandName>
</name>
<engine>4 cylinder, 400 cc</engine>
<kind>alround</kind>
<drive>chain</drive>
<comment>Good shape!</comment>
</bike>
<bike year="2003" color="black" condition="new">
<name>
<manufacturer>Harley Davidsson</manufacturer>
<brandName>XL1200C</brandName>
<additionalName>Sportster</additionalName>
</name><engine>V-engine, 2-cylinders, 1200 cc</engine>
<kind>custom</kind>
<drive>belt</drive>
</bike>
</motorcycles>
1.6示例:将xml转换为HTML
假设您想要转换motorcycles.xml
文档到HTML。如果您希望得到的HTML文档的结构和标记与XML文档相同,则可以使用xmerl:export/2
功能。以下内容如下:
2> {Doc,Misc}=xmerl_scan:file('motorcycles.xml').
{{xmlElement,motorcycles,
motorcycles,
[],
{xmlNamespace,[],[]},
[],
1,
[],
[{xmlText,[{motorcycles,1}],1,[],"\
",text},
{xmlElement,bike,
...
3> DocHtml=xmerl:export([Doc],xmerl_html).
["<!DOCTYPE HTML PUBLIC \\"",
"-//W3C//DTD HTML 4.01 Transitional//EN",
"\\"",
[],
">\
",
[[["<","motorcycles",">"],
["\
",
[["<",
"bike",
[[" ","year","=\\"","2000","\\""],[" ","color","=\\"","black","\\""]],
">"],
...
会给出结果result_export.html
也许你想做些更有条理的人类阅读。假设您希望在开始时列出所有不同的品牌,并链接到每一组摩托车。你还想让所有的摩托车按品牌排序,然后在上面放一些艳丽的颜色。因此,您可以重新排列元素的顺序,并放入任意的HTML标记中。这是可以通过XSL Transformation (XSLT)
就像xmerl中的功能。
尽管下面的示例展示了将数据从XML转换为HTML的一种方法,但它也适用于转换到其他格式。
xmerl_xs
并不实现整个XSLT规范,而是基本功能。有关详细信息,请参阅reference manual
首先,关于xmerl的一些词[医]XS功能:
您需要编写模板函数,以便能够控制所需的输出类型。因此,如果您想封装一个bike元素<p>标记,您只需编写一个函数:
template(E = #xmlElement{name='bike'}) ->
["<p>",xslapply(fun template/1,E),"</p>"];
带着xslapply
您可以告诉XSLT处理器它应该按照什么顺序遍历XML结构。默认情况下,它是按前置顺序遍历的,但对于以下情况,我们会故意选择打破该顺序:
template(E = #xmlElement{name='bike'}) ->
["<p>",xslapply(fun template/1,select("bike/name/manufacturer")),"</p>"];
如果要输出xml元素或属性的内容,则将通过value_of
职能:
template(E = #xmlElement{name='motorcycles'}) ->
["<p>",value_of(select("bike/name/manufacturer",E),"</p>"];
在xmerl[医]Xs函数您可以提供选择%28 String%29调用,这是XPath
功能。有关详细信息,请参阅xmerl。[医]Xstutorial
...
现在,回到我们希望使输出更加有序的例子。使用模板:
template(E = #xmlElement{name='motorcycles'}) ->
[ "<head>\
<title>motorcycles</title>\
</head>\
",
"<body>\
",
\011 "<h1>Used Motorcycles</h1>\
",
\011 "<ul>\
",
\011 remove_duplicates(value_of(select("bike/name/manufacturer",E))),
\011 "\
</ul>\
",
\011 sort_by_manufacturer(xslapply(fun template/1, E)),
"</body>\
",
\011 "</html>\
"];
我们在顶部元素上进行匹配,并将内部部分嵌入到HTML主体中。然后提取所有摩托车品牌的字符串值,对它们进行排序,然后根据remove_duplicates(value_of(select("bike/name/manufacturer", E)))
我们还对顶部元素的子结构进行了处理,并将其传递给一个函数,该函数根据本例开头的任务表示按品牌对所有摩托车信息进行排序。
上的下一个模板匹配bike
要素:
template(E = #xmlElement{name='bike'}) ->
{value_of(select("name/manufacturer",E)),["<dt>",xslapply(fun template/1,select("name",E)),"</dt>",
"<dd><ul>\
",
"<li style="color:green">Manufacturing year: ",xslapply(fun template/1,select("@year",E)),"</li>\
",
"<li style="color:red">Color: ",xslapply(fun template/1,select("@color",E)),"</li>\
",
"<li style="color:blue">Shape : ",xslapply(fun template/1,select("@condition",E)),"</li>\
",
"</ul></dd>\
"]};
这将创建一个带有摩托车品牌和输出格式的元组。我们使用的品牌名称只用于分类用途。我们必须用“内置子句”结束模板函数。template(E) -> built_in_rules(fun template/1, E).
整个程序是电动自行车2html.erl:
%%%-------------------------------------------------------------------
%%% File : motorcycles2html.erl
%%% Author : Bertil Karlsson <bertil@localhost.localdomain>
%%% Description :
%%%
%%% Created : 2 Sep 2004 by Bertil Karlsson <bertil@localhost.localdomain>
%%%-------------------------------------------------------------------
-module(motorcycles2html).
-include_lib("xmerl/include/xmerl.hrl").
-import(xmerl_xs,
[ xslapply/2, value_of/1, select/2, built_in_rules/2 ]).
-export([process_xml/1,process_to_file/2,process_to_file/1]).
process_xml(Doc) ->
template(Doc).
process_to_file(FileName) ->
process_to_file(FileName,'motorcycles.xml').
process_to_file(FileName,XMLDoc) ->
case file:open(FileName,[write]) of
{ok,IOF} ->
{XMLContent,_} = xmerl_scan:file(XMLDoc),
TransformedXML=process_xml(XMLContent),
io:format(IOF,"~s",[TransformedXML]),
file:close(IOF
{error,Reason} ->
io:format("could not open file due to ~p.~n",[Reason])
end.
%%% templates
template(E = #xmlElement{name='motorcycles'}) ->
[ "<head>\n<title>motorcycles</title>\n</head>\n",
"<body>\n",
"<h1>Used Motorcycles</h1>\n",
"<ul>\n",
remove_duplicates(value_of(select("bike/name/manufacturer",E))),
"\n</ul>\n",
sort_by_manufacturer(xslapply(fun template/1, E)),
"</body>\n",
"</html>\n"];
template(E = #xmlElement{name='bike'}) ->
{value_of(select("name/manufacturer",E)),["<dt>",xslapply(fun template/1,select("name",E)),"</dt>",
"<dd><ul>\n",
"<li style=\"color:green\">Manufacturing year: ",xslapply(fun template/1,select("@year",E)),"</li>\n",
"<li style=\"color:red\">Color: ",xslapply(fun template/1,select("@color",E)),"</li>\n",
"<li style=\"color:blue\">Shape : ",xslapply(fun template/1,select("@condition",E)),"</li>\n",
"</ul></dd>\n"]};
template(E) -> built_in_rules(fun template/1, E).
%%%%%%%%%%% helper routines
%% sorts on the bike name element, unwraps the bike information and
%% inserts a line feed and indentation on each bike element.
sort_by_manufacturer(L) ->
Tuples=[X1||X1={_,_} <- L],
SortedTS = lists:keysort(1,Tuples),
InsertRefName_UnWrap=
fun([{[Name],V}|Rest],Name,F)->
[V|F(Rest,Name,F)];
([{[Name],V}|Rest],_PreviousName,F) ->
[["<a name=\"",Name,"\"></>"],V|F(Rest,Name,F)];
([],_,_) -> []
end,
SortedRefed=InsertRefName_UnWrap(SortedTS,no_name,InsertRefName_UnWrap),
% SortedTs=[Y||{X,Y}<-lists:keysort(1,Tuples)],
WS = "\n ",
Fun=fun([H|T],Acc,F)->
F(T,[H,WS|Acc],F
([],Acc,_F)->
lists:reverse([WS|Acc])
end,
if length(SortedRefed) > 0 ->
Fun(SortedRefed,[],Fun
true -> []
end.
%% removes all but the first of an element in L and inserts a html
%% reference for each list element.
remove_duplicates(L) ->
remove_duplicates(L,[]).
remove_duplicates([],Acc) ->
make_ref(lists:sort(lists:reverse(Acc))
remove_duplicates([A|L],Acc) ->
case lists:delete(A,L) of
L ->
remove_duplicates(L,[A|Acc]
L1 ->
remove_duplicates([A|L1],[Acc])
end.
make_ref([]) -> [];
make_ref([H]) when is_atom(H) ->
"<ul><a href=\"#"++atom_to_list(H)++"\">"++atom_to_list(H)++"</a></ul>";
make_ref([H]) when is_list(H) ->
"<ul><a href=\"#"++H++"\">\s"++H++"</a></ul>";
make_ref([H|T]) when is_atom(H) ->
["<ul><a href=\"#"++atom_to_list(H)++"\">\s"++atom_to_list(H)++",\n</a></ul>"
|make_ref(T)];
make_ref([H|T]) when is_list(H) ->
["<ul><a href=\"#"++H++"\">\s"++H++",\n</a></ul>"|make_ref(T)].
如果我们像这样运行:motorcycles2html:process_to_file('result_xs.html', 'motorcycles2.xml').
结果是result_xs.html
当输入文件的结构与以前的“摩托车”XML文件相同时,但是它有更多的%27自行车%27元素,而%27制造商%27元素则不正常。