yaws + xmerl -> On the fly XSLT lookalike transforms in Erlang

Mikael Karlsson <>
Fri Oct 25 20:05:12 CEST 2002


Hi,

I implemented a small set of functions that makes it possible
to write XSLT "lookalike" (a can't find a better word) transforms
in Erlang. They actually uses xmerl and just adds "syntactic sugar"
to make the Erlang "stylesheets" look more like XSLT stylesheets.

I have been able to use this in yaws scripts and perform transformations
directly upon http GET requests.

Check http://213.64.169.138:8001/

for examples.
NOTE, my IP is dynamically set by dhcp from my ISP so I can not promise
it will last so many days. But it will do until I get a fixed one for the 
examples.

Below is the xserl.erl module that collects all the functions.

Cheers
Mikael

%%% File    : xserl.erl
%%% Author  :  Mikael Karlsson
%%% Description : XSLT lookalike transformation in Erlang
%%%               
%%%              

-module(xserl).
-vsn('0.1').
-date('02-10-18').
-author('').

-export([xslapply/2, value_of/1, select/2, built_in_rules/2 ]).
-export([mapxml/2, foldxml/3, mapfoldxml/3]).
-include("xmerl.hrl").


%% Wrapper to make things look similar to xsl:apply-templates
%% I am actually a bit reluctant to use this function, since
%% when you want to look like xslt you actually hide away how
%% simple it is to implement in Erlang. Including some
%% performance loss.
xslapply(Fun, EList) when list(EList) ->
    lists:map( Fun, EList);
xslapply(Fun, E = #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{}=T1, 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 = #xmlElement{})->
    lists:map(Fun, E#xmlElement.content);
built_in_rules(Fun, E = #xmlText{}) ->
    E#xmlText.value;
built_in_rules(Fun, E = #xmlAttribute{}) ->
    E#xmlAttribute.value;
built_in_rules(Fun, E) ->[].

%%% -------------------- Utilities -------------------------
%%% funs working on the xmerl tree
%%%

%% mapxml
%% Fun is fun(Old#xmlElement) -> New#xmlElement

mapxml(Fun, #xmlElement{}= E) ->
    C1 = Fun(E),
    C2 = mapxml(Fun,lists:flatten(C1#xmlElement.content)),
    C1#xmlElement{content=C2};
mapxml(Fun, List) when list(List) ->
    AFun = fun(E) -> mapxml(Fun, E) end,
    lists:map(AFun, List);
mapxml(Fun, E) ->
    Fun(E).


%% foldxml
%% Fun is fun(#xmlElement, OldAccu) -> NewAccu

foldxml(Fun, Accu0, #xmlElement{content=C}=E) ->
    Accu1 = Fun(E, Accu0),
    foldxml(Fun, Accu1, C);
foldxml(Fun, Accu, List) when list(List) ->
    AFun = fun(E,A) -> foldxml(Fun, A, E) end,
    lists:foldl(AFun, Accu, List);
foldxml(Fun, Accu, E) ->
    Fun(E, Accu).


%% mapfoldxml
%% Fun is fun(Old#xmlElement, OldAccu) -> {New#xmlElement, NewAccu}

mapfoldxml(Fun, Accu0, #xmlElement{}=E) ->
    {C1,Accu1} = Fun(E, Accu0),
    {C2,Accu2} = mapfoldxml(Fun, Accu1, lists:flatten(C1#xmlElement.content)),
    {C1#xmlElement{content=C2},Accu2};
mapfoldxml(Fun, Accu, List) when list(List) ->
    AFun = fun(E,A) -> mapfoldxml(Fun, A, E) end,
    lists:mapfoldl(AFun, Accu, List);
mapfoldxml(Fun, Accu, E) ->
    Fun(E,Accu).




More information about the erlang-questions mailing list