非常教程

Erlang 20参考手册

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元素则不正常。

Erlang 20

Erlang 是一种通用的面向并发的编程语言,可应付大规模开发活动的程序设计语言和运行环境。

主页 https://www.erlang.org/
源码 https://github.com/erlang/otp
版本 20
发布版本 20.1