From: Mikael K. <mik...@cr...> - 2002-11-08 15:21:06
|
fredag 08 november 2002 15:12 skrev Claes Wikstrom: > > Another use would be to manage an XSLT tranformation for XML file, an= d > > various things like that. > > Mikke ?? Ok you asked for it, long reply warning. (If you think it is useful you could add it to the yaws examples maybe) I have been playing around, not with pure XSLT, but with an XSLT lookalik= e in=20 pure Erlang, why walk over the bridge for water :-). (For pure XSLT there= are adapters to the Sablotron C++ transformer available for inets so I guess = these could be adapted for yaws.) I also get a feeling that using the pattern match in Erlang for template = match=20 can give *excellent* performance. Still only a feeling though..... Since both Erlang and XSLT are kind of functional languages the basic thi= ngs=20 map rather well. I give you an example of using xmerl (by Ulf Wiger) and = yaws=20 together for "on the fly" transform. Basically xmerl parses an xml string/file and returns a record of: -record(xmlElement, { =09=09 name, =09=09 parents =3D [], =09=09 pos, =09=09 attributes =3D [], =09=09 content =3D [], =09=09 language =3D [], =09=09 expanded_name =3D [], =09=09 nsinfo =3D [],=09% {Prefix, Local} | [] =09=09 namespace =3D #xmlNamespace{} =09=09 }). Were content is a mixed list of yet other xmlElement records and/or xmlTe= xt=20 (or other node types). XSLT also makes use of XPath which is also impleme= nted=20 in xmerl. Example of one of the first XSLT sheets I picked from "XSLT 2nd Edition" = ,=20 Michael Kay, WROX: Original stylesheet: <xsl:stylesheet xmlns:xsl=3D"http://www.w3.org/1999/XSL/Transform" version=3D"1.0"> <xsl:template match=3D"poem"> <html> <head> <title><xsl:value-of select=3D"title" /></title> </head> <body> <xsl:apply-templates select=3D"title" /> <xsl:apply-templates select=3D"author" /> <xsl:apply-templates select=3D"stanza" /> <xsl:apply-templates select=3D"date" /> </body> </html> =20 </xsl:template> <xsl:template match=3D"title"> <div align=3D"center"><h1><xsl:value-of select=3D"." /></h1></div> </xsl:template> <xsl:template match=3D"author"> <div align=3D"center"><h2>By <xsl:value-of select=3D"." /></h2></div> </xsl:template> <xsl:template match=3D"date"> <p><i><xsl:value-of select=3D"." /></i></p> </xsl:template> <xsl:template match=3D"stanza"> <p><xsl:apply-templates select=3D"line" /></p> </xsl:template> <xsl:template match=3D"line"> <xsl:if test=3D"position() mod 2 =3D 0">  </xsl:if> <xsl:value-of select=3D"." /><br /> </xsl:template> </xsl:stylesheet> -------------------------------------------------------------------------= ---------------- yaws xsl transform script: <erl> -include("/usr/local/lib/erlang/lib/xmerl-0.18/inc/xmerl.hrl"). -import(xserl, [ xslapply/2, value_of/1, select/2, foldxml/3 ]). process_xml(Doc)-> =09template(Doc). template(E =3D #xmlElement{name=3D'poem'})-> [ "<html>" "<head>" "<title>", value_of(select("title",E)), "</title>" "</head>" "<body>", xslapply( fun template/1, select("title", E)), %% lists:map can be called instead of xslapply when you pull with s= elect lists:map( fun template/1, select("author", E)), xslapply( fun template/1, select("stanza", E)), xslapply( fun template/1, select("date", E)), "</body>" "</html>" ]; template(E =3D #xmlElement{name=3D'title'}) -> ["<div align=3D\"center\"><h1>",=20 value_of(select(".", E)), "</h1></div>"]; template(E =3D #xmlElement{name=3D'author'}) -> ["<div align=3D\"center\"><h2>By ", %% value_of(select(".", E)), The one below does the same thing %% but faster value_of(E#xmlElement.content),=20 "</h2></div>"]; template(E =3D #xmlElement{name=3D'date'}) -> ["<p><i>", value_of(select(".", E)), "</i></p>"]; template(E =3D #xmlElement{name=3D'stanza'}) -> {Lines,LineNo} =3D lists:mapfoldl(fun template_pos/2, 1, select("line= ", E)), ["<p>", Lines, "</p>"]. %% Since all values are pulled and all tags are processed, the default %% rules do not need invokation. %%=20 %% template(E)-> %% built_in_rules( fun template/1, E). indent_line(0)->"  "; indent_line(_)->"". template_pos(E =3D #xmlElement{name=3D'line'}, P) -> {[indent_line(P rem 2), value_of(E#xmlElement.content), "<br />"], P = + 1=20 }. %% template_pos(E, P)->=20 %% {built_in_rules( fun template/1, E), P + 1}. =20 %% Not necessary here as template_pos/2 only is called %% for line children, but good practice for general case %% ---------------- yaws stuff ------------------------- process_doc(A, [{doc,Docname}])-> case xmerl_scan:file(A#arg.docroot ++ "/" ++ Docname, =09=09=09 [{fetch_fun, fun(DTDSpec,S) -> {ok,S} end}, =09=09=09 {space,normalize}]) of =09{error, Result} -> =09 io:fwrite("Error ~p~n", [Result]), =09 perr(); =09{B,_} -> =09 C =3D process_xml(B), =09 {content,"text/html",C} end; process_doc(A, _) -> perr(). perr()-> {ehtml,{html,[], =09 [ =09 {head,[],{title,[],"Error"}}, =09 {body,[],[ =09=09 {h1,[],"Error"}, =09=09 {p,[],"No or Erroneous Document name"} =09=09 ]} =09 ] =09 } }. =20 out(A) ->=20 process_doc(A, yaws_api:parse_query(A)). </erl> ------------------------------ Functions used: xslapply(Fun, EList) when list(EList) -> lists:map( Fun, EList); xslapply(Fun, E =3D #xmlElement{})-> lists:map( Fun, E#xmlElement.content). %% value_of concatenates all text nodes within the tree value_of(E)-> lists:reverse(foldxml(fun value_of1/2, [], E)). value_of1(#xmlText{}=3DT1, Accu)-> [T1#xmlText.value|Accu]; value_of1(E, Accu) -> Accu. %% xmerl_xpath does the job. select(Str,E)-> xmerl_xpath:string(Str,E). %% The default fallback behaviour, template funs should %% end with: %% template(E)->built_in_rules(fun template/1, E). built_in_rules(Fun, E =3D #xmlElement{})-> lists:map(Fun, E#xmlElement.content); built_in_rules(Fun, E =3D #xmlText{}) -> E#xmlText.value; built_in_rules(Fun, E =3D #xmlAttribute{}) -> E#xmlAttribute.value; built_in_rules(Fun, E) ->[]. %% foldxml %% Fun is fun(#xmlElement, OldAccu) -> NewAccu =20 foldxml(Fun, Accu0, #xmlElement{content=3DC}=3DE) -> Accu1 =3D Fun(E, Accu0), foldxml(Fun, Accu1, C); foldxml(Fun, Accu, List) when list(List) -> AFun =3D fun(E,A) -> foldxml(Fun, A, E) end, lists:foldl(AFun, Accu, List); foldxml(Fun, Accu, E) -> Fun(E, Accu). |