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是XML制作Misc,它是文档元素之后的标记。它“按原样”返回。DataStructure是一个xmlElement记录,即在其他有田nameparentsattributescontent这样的:

#xmlElement{name=Name, ... parents=Parents, ... attributes=Attrs, content=Content, ...}

元素的名称在name场。在parents字段是保存的父元素的名称。父元素是一个元组列表,每个元组中的第一个元素是父元素的名称。列表的顺序相反。

记录xmlAttribute在字段中保存属性的名称和值。namevalue元素的所有属性都是字段中的xmlAttribute列表。attributesxmlElement记录。

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文档的结构。

标记之间的空格字符为spacetab并被line feed标准化并作为xmlText记录返回。

错误

不成功的解析会导致错误,这可能是一个元组。{error,Reason}或出口:{'EXIT',Reason}.根据xml 1.0标准,fatal errorerror情况。致命错误在错误发生时,由符合标准的解析器检测到。五月被发现。这两个类型的错误都被这个版本的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&amp;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必须有一个"<"或一个"&"字符必须写成"&lt;",并"&amp;"分别。其他字符实体的存在类似于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&amp;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&amp;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元素则不正常。