Per previous blogs:
- A Simple XML State Machine Accepting SAX Events to Build xmerl Compitable XML Tree: icalendar demo
- Parse JSON to xmerl Compitable XML Tree via A Simple XML State Machine
I wrote a simple xml state machine that receives SAX events to build xmerl compitable XML tree.
This time, it's a simple POET (Plain Old Erlang Term) state machine, which receives SAX events to build the data in form of List and Tuple.
%%% A state machine which receives sax events and builds a Plain Old Erlang Term
-module(poet_sm).
-export([state/2]).
-export([test/0
]).
-record(poetsmState, {
name = undefined,
attributes = [],
content = [],
parents = []
}).
receive_events(Events) -> receive_events(Events, undefined).
receive_events([], _States) -> {ok, [], []};
receive_events([Event|T], States) ->
case state(Event, States) of
{ok, TopObject} ->
{ok, TopObject, T};
{error, Reason} ->
{error, Reason};
States1 ->
receive_events(T, States1)
end.
state({startDocument}, _StateStack) ->
State = #poetsmState{},
[State];
state({endDocument}, StateStack) ->
%io:fwrite(user, "endDocument, states: ~p~n", [StateStack]),
case StateStack of
{ok, TopObject} -> {ok, TopObject};
_ -> {error, io:fwrite(user, "Bad object match, StateStack is: ~n~p~n", [StateStack])}
end;
state({startElement, _Uri, LocalName, _QName, Attrs}, StateStack) ->
%io:fwrite(user, "startElement~n", []),
%% pop current State
[State|_StatesPrev] = StateStack,
#poetsmState{parents=Parents} = State,
{_Pos, Attributes1} = lists:foldl(
fun ({Name, Value}, {Pos, AccAttrs}) ->
Pos1 = Pos + 1,
Attr = {atom_to_list(Name), to_poet_value(Value)},
{Pos1, [Attr|AccAttrs]}
end, {0, []}, Attrs),
Parents1 = [{LocalName, 0}|Parents],
%% push new state of Attributes, Content and Parents to StateStack
NewState = #poetsmState{name = LocalName,
attributes = Attributes1,
content = [],
parents = Parents1},
[NewState|StateStack];
state({endElement, _Uri, LocalName, _QName}, StateStack) ->
%% pop current State
[State|StatesPrev] = StateStack,
#poetsmState{name=Name,
attributes=Attributes,
content=Content,
parents=Parents} = State,
%io:fwrite(user, "Element end with Name: ~p~n", [Name]),
if LocalName == undefined -> %% don't care
undefined;
LocalName /= Name ->
throw(lists:flatten(io_lib:format(
"Element name match error: ~p should be ~p~n", [LocalName, Name])));
true -> undefined
end,
%% composite a new object
[_|_ParentsPrev] = Parents,
Object =
if Attributes == [] ->
{Name, lists:reverse(Content)};
true ->
{Name, lists:reverse(Attributes), lists:reverse(Content)}
end,
%io:fwrite(user, "object: ~p~n", [Object]),
%% put Object to parent's content and return new state stack
case StatesPrev of
[_ParentState|[]] -> %% reached the top now, return final result
{ok, Object};
[ParentState|Other] ->
#poetsmState{content=ParentContent} = ParentState,
ParentContent1 = [Object|ParentContent],
%% update parent state and backward to it:
ParentState1 = ParentState#poetsmState{content = ParentContent1},
%io:fwrite(user, "endElement, state: ~p~n", [State1]),
[ParentState1|Other]
end;
state({characters, Characters}, StateStack) ->
%% pop current State
[State|StatesPrev] = StateStack,
#poetsmState{name=Name,
content=Content,
parents=Parents} = State,
[{Parent, Pos}|ParentsPrev] = Parents,
Pos1 = Pos + 1,
Value = to_poet_value(Characters), %parents = [{Parent, Pos1}|ParentsPrev]},
Content1 = [Value|Content],
Parents1 = [{Parent, Pos1}|ParentsPrev],
UpdatedState = State#poetsmState{content = Content1,
parents = Parents1},
[UpdatedState|StatesPrev].
to_poet_value(Name) when is_atom(Name) ->
to_poet_value(atom_to_list(Name));
to_poet_value(Chars) when is_list(Chars) ->
%% it's string, should convert to binary, since list in poet means array
list_to_binary(Chars);
to_poet_value(Value) ->
Value.
test() ->
Events = [
{startDocument},
{startElement, [], feed, [], [{link, "http://lightpole.net"}, {author, "Caoyuan"}]},
{characters, "feed text"},
{startElement, [], entry, [], [{tag, "Erlang, Function"}]},
{characters, "Entry1's text"},
{endElement, [], entry, []},
{startElement, [], entry, [], []},
{characters, "Entry2's text"},
{endElement, [], entry, []},
{endElement, [], feed, []},
{endDocument}
],
%% Streaming:
{ok, Poet1, _Rest} = receive_events(Events),
io:fwrite(user, "Streaming Result: ~n~p~n", [Poet1]).
The result will be something like:
{feed,[{"link",<<"http://lightpole.net">>},{"author",<<"Caoyuan">>}],
[<<"feed text">>,
{entry,[{"tag",<<"Erlang, Function">>}],[<<"Entry1's text">>]},
{entry,[<<"Entry2's text">>]}]}
The previous iCal and JSON examples can be parsed to POET by modifing the front-end parser a bit.
发表评论
最近加入圈子
最新评论
-
ErlyBird 0.16.0 Released
Others->Erlang...
-- by dcaoyuan -
ErlyBird 0.16.0 Released
为什么我在NB内导入完成后,在New Project里面无法看到 create ...
-- by fatdong -
新的Scala for NetBeans提 ...
因为NetBeans的几个基础模块在Trunk里有与6.1不兼容的的变化,所以现 ...
-- by dcaoyuan -
新的Scala for NetBeans提 ...
另外,dcaoyuan大叔有空时能否简单说一下为NetBeans开发某一门语言的 ...
-- by 自言200801 -
新的Scala for NetBeans提 ...
重新更新以后就装不起来啦,错误如下:====================== ...
-- by 自言200801







评论排行榜