如何从Erlang中的MucSub事件中提取嵌套的ejabberd消息元素
我想在ejabberd数据包中找到消息元素。 数据包本身是一个消息元素,但有时(延迟消息或其他情况)实际消息嵌套在数据包内: 正常信息:如何从Erlang中的MucSub事件中提取嵌套的ejabberd消息元素,erlang,ejabberd,Erlang,Ejabberd,我想在ejabberd数据包中找到消息元素。 数据包本身是一个消息元素,但有时(延迟消息或其他情况)实际消息嵌套在数据包内: 正常信息: <message from="hag66@shakespeare.example" to="coven@muc.shakespeare.example" type="groupchat"> <body>Test</body> </message> 试验 其他结构示例:
<message from="hag66@shakespeare.example"
to="coven@muc.shakespeare.example"
type="groupchat">
<body>Test</body>
</message>
试验
其他结构示例:
<message from="coven@muc.shakespeare.example"
to="hag66@shakespeare.example/pda">
<event xmlns="http://jabber.org/protocol/pubsub#event">
<items node="urn:xmpp:mucsub:nodes:messages">
<item id="18277869892147515942">
<message from="coven@muc.shakespeare.example/secondwitch"
to="hag66@shakespeare.example/pda"
type="groupchat"
xmlns="jabber:client">
<archived xmlns="urn:xmpp:mam:tmp"
by="muc.shakespeare.example"
id="1467896732929849" />
<stanza-id xmlns="urn:xmpp:sid:0"
by="muc.shakespeare.example"
id="1467896732929849" />
<body>Hello from the MUC room !</body>
</message>
</item>
</items>
</event>
</message>
MUC室的你好!
在第二个示例中,我希望找到内部消息元素。
第二种情况的结构并不总是相同的。所以我需要遍历数据包,并尝试找到任何名为message的子元素。
它不能是两个消息子元素,所以如果我找到了第一个,我就不需要再继续了。如果没有名为message的子元素,我想返回原始数据包
这是我到目前为止的代码:
get_message(Packet) ->
Els = xmpp:get_els(Packet),
Found =
case Els of
[] ->
<<>>;
_ ->
El = find_file(Els, fun(El) ->
ElementName = io_lib:format("~s",[xmpp:get_name(El)]),
string:equal(ElementName,"message") end, <<>>),
Fe =
case El of
<<>> ->
Elements = xmpp:get_els(El),
lists:foreach(fun(Element) ->
FoundElement = get_message(Element),
case FoundElement of
<<>> ->
ok;
_ ->
% stop foreach and return FoundElement
FoundElement
end
end, Elements);
_ ->
El
end,
Fe
end,
Found.
find_file(L, Condition, Default) ->
case lists:dropwhile(fun(E) -> not Condition(E) end, L) of
[] -> Default;
[F | _] -> F
end.
get_消息(数据包)->
Els=xmpp:获取Els(数据包),
发现=
个案
[] ->
;
_ ->
El=查找文件(Els,乐趣(El)->
ElementName=io_lib:format(“~s”,[xmpp:get_name(El)],
string:equal(ElementName,“message”)end,),
Fe=
案例El
->
元素=xmpp:get_els(El),
列表:foreach(乐趣(元素)->
FoundElement=获取消息(元素),
案例要素
->
好啊
_ ->
%停止foreach并返回FoundElement
基本要素
结束
结束,要素);
_ ->
埃尔
完,,
铁
完,,
建立
查找文件(L、条件、默认)->
案例列表:dropwhile(乐趣(E)->非条件(E)结束,L)的
[]->违约;
[F |]->F
结束。
哎呀,这是erlang!下面是一个使用xmerl
的erlang解决方案,它是erlang的内置xml解析模块:
xml.xml:
<message from="coven@muc.shakespeare.example"
to="hag66@shakespeare.example/pda">
<event xmlns="http://jabber.org/protocol/pubsub#event">
<items node="urn:xmpp:mucsub:nodes:messages">
<item id="18277869892147515942">
<message from="coven@muc.shakespeare.example/secondwitch"
to="hag66@shakespeare.example/pda"
type="groupchat"
xmlns="jabber:client">
<archived xmlns="urn:xmpp:mam:tmp"
by="muc.shakespeare.example"
id="1467896732929849" />
<stanza-id xmlns="urn:xmpp:sid:0"
by="muc.shakespeare.example"
id="1467896732929849" />
<body>Hello from the MUC room !</body>
</message>
</item>
</items>
</event>
</message>
如果您已经将消息设置为字符串,则还有一个名为xmerl\u scan:string/1
的函数,例如:
{ParsedMessage, _RemainingText = ""} = xmerl_scan:string(Message)
您还需要该文件
在该功能中:
get_message() ->
Messages = xmerl_xpath:string("//message", get_doc()),
lists:last(Messages).
消息
将是一个包含以下内容的列表:
lists:last()
将返回嵌套消息,或者在没有嵌套消息时返回根消息
在外壳中:
~/erlang_programs/xmerl$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> Msg = my:get_message().
{xmlElement,message,message,[],
{xmlNamespace,'jabber:client',[]},
[{item,2},{items,2},{event,2},{message,1}],
2,
[{xmlAttribute,from,[],[],[],
[{message,2},{item,2},{items,2},{event,2},{message,1}],
1,[],"coven@muc.shakespeare.example/secondwitch",false},
{xmlAttribute,to,[],[],[],
[{message,2},{item,2},{items,2},{event,2},{message,1}],
2,[],"hag66@shakespeare.example/pda",false},
{xmlAttribute,type,[],[],[],
[{message,2},{item,2},{items,2},{event,2},{message,1}],
3,[],"groupchat",false},
{xmlAttribute,xmlns,[],[],[],
[{message,2},{item,2},{items,2},{event,2},{message,1}],
4,[],"jabber:client",false}],
[{xmlText,[{message,2},
{item,2},
{items,2},
{event,2},
{message,1}],
1,[],"\n ",text},
{xmlElement,archived,archived,[],
{xmlNamespace,'urn:xmpp:mam:tmp',[]},
[{message,2},{item,2},{items,2},{event,2},{message,1}],
2,
[{xmlAttribute,xmlns,[],[],[],
[{archived,2},{message,...},{...}|...],
1,[],
[...],...},
{xmlAttribute,by,[],[],[],
[{archived,...},{...}|...],
2,[],...},
{xmlAttribute,id,[],[],[],[{...}|...],3,...}],
[],[],".",undeclared},
{xmlText,[{message,2},
{item,2},
{items,2},
{event,2},
{message,1}],
3,[],"\n ",text},
{xmlElement,'stanza-id','stanza-id',[],
{xmlNamespace,'urn:xmpp:sid:0',[]},
[{message,2},{item,2},{items,2},{event,2},{message,1}],
4,
[{xmlAttribute,xmlns,[],[],[],[{...}|...],1,...},
{xmlAttribute,by,[],[],[],[...],...},
{xmlAttribute,id,[],[],[],...}],
[],[],".",undeclared},
{xmlText,[{message,2},
{item,2},
{items,2},
{event,2},
{message,1}],
5,[],"\n ",text},
{xmlElement,body,body,[],
{xmlNamespace,'jabber:client',[]},
[{message,2},{item,2},{items,2},{event,2},{message,1}],
6,[],
[{xmlText,[{body,...},{...}|...],1,[],...}],
[],".",undeclared},
{xmlText,[{message,2},
{item,2},
{items,2},
{event,2},
{message,1}],
7,[],"\n ",text}],
[],".",undeclared}
2> Attrs = my:get_attributes(Msg).
[{xmlAttribute,from,[],[],[],
[{message,2},{item,2},{items,2},{event,2},{message,1}],
1,[],"coven@muc.shakespeare.example/secondwitch",false},
{xmlAttribute,to,[],[],[],
[{message,2},{item,2},{items,2},{event,2},{message,1}],
2,[],"hag66@shakespeare.example/pda",false},
{xmlAttribute,type,[],[],[],
[{message,2},{item,2},{items,2},{event,2},{message,1}],
3,[],"groupchat",false}]
3> my:convert_to_map(Attrs).
#{from => "coven@muc.shakespeare.example/secondwitch",
to => "hag66@shakespeare.example/pda",type => "groupchat"}
4>
要获取消息中的正文标记(或任何其他嵌套标记),请执行以下操作:
get_body(Message) ->
[Body] = xmerl_xpath:string(".//body", Message),
Body.
要获取消息的所有直接子标记,请执行以下操作:
get_direct_children(Message) ->
xmerl_xpath:string("./*", Message).
要获取标记的单个属性的值,请执行以下操作:
get_attribute(Attr, Node) ->
% {xmlObj,string,"coven@muc.shakespeare.example"}
{xmlObj, string, Value} = xmerl_xpath:string("string(./@" ++ Attr ++ ")", Node),
Value.
==长生不老药溶液=== 您可以使用解析“数据包”: 第一个
xpath()
调用返回一个(l)列表,即所有匹配项,而不仅仅是第一个匹配项。该列表将包含一个或两个消息标记,具体取决于数据包Enum.at(-1)
将返回列表中的最后一个消息标记,它将是嵌套的消息标记,或者在没有嵌套消息标记时返回根消息标记。第二个xpath()
调用返回消息标记的from
属性,在嵌套数据包的情况下,该属性生成:
'coven@muc.shakespeare.example/secondwitch'
我注意到SweetXml返回一个字符列表(单引号字符串),而不是双引号字符串(这可能是您想要的)。如果将s
添加到第二个xpath()
调用,则返回值将是双引号字符串:
|> xpath(~x"//@from"s)
输出:
~/elixir_programs/xml_example$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> XmlExample.sweet("./lib/xml.xml")
"coven@muc.shakespeare.example/secondwitch"
[
from: 'coven@muc.shakespeare.example/secondwitch',
to: 'hag66@shakespeare.example/pda',
type: 'groupchat'
]
%{
from: 'coven@muc.shakespeare.example/secondwitch',
to: 'hag66@shakespeare.example/pda',
type: 'groupchat'
}
我不知道是否有更好的方法,但要获得标记的所有属性,可以执行以下操作:
def sweet(path) do
File.read!(path)
|> xpath(~x"//message"l)
|> Enum.at(-1)
|> xpath(~x"./@*"le)
|> Enum.map(fn {:xmlAttribute,name,_,_,_,_list,_,_,value,_} ->
{name, value}
end)
end
输出:
~/elixir_programs/xml_example$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> XmlExample.sweet("./lib/xml.xml")
"coven@muc.shakespeare.example/secondwitch"
[
from: 'coven@muc.shakespeare.example/secondwitch',
to: 'hag66@shakespeare.example/pda',
type: 'groupchat'
]
%{
from: 'coven@muc.shakespeare.example/secondwitch',
to: 'hag66@shakespeare.example/pda',
type: 'groupchat'
}
在这方面:
xpath(~x"./@*"le)
/
在当前标记中搜索,当前标记是Enum.at(-1)返回的标记,@*
选择所有属性。再一次,需要使用l
使xpath()
返回所有匹配项(如果忘记l
!,会非常令人沮丧),并且e
代表“实体”,这会导致xpath()为每个属性返回“实体”,如下所示:
[
{:xmlAttribute, :from, [], [], [],
[message: 2, item: 2, items: 2, event: 2, message: 1], 1, [],
'coven@muc.shakespeare.example/secondwitch', false},
{:xmlAttribute, :to, [], [], [],
[message: 2, item: 2, items: 2, event: 2, message: 1], 2, [],
'hag66@shakespeare.example/pda', false},
{:xmlAttribute, :type, [], [], [],
[message: 2, item: 2, items: 2, event: 2, message: 1], 3, [],
'groupchat', false}
]
然后,代码模式匹配元组以挑选每个属性的名称
及其值
如果希望获取地图中的所有属性:
def sweet(path) do
attr_entities = File.read!(path)
|> xpath(~x"//message"l)
|> Enum.at(-1)
|> xpath(~x"./@*"le)
for {:xmlAttribute,name,_,_,_,_list,_,_,value,_} <- attr_entities, into: %{} do
{name, value}
end
end
结果证明我不需要做所有这些计算。这是一个名为unwrap_mucsub_message的方法,它完全满足我的需要
get_message(Packet) ->
case misc:unwrap_mucsub_message(Packet) of
#message{} = Msg ->
Msg;
_ ->
Packet
end.
听起来你对Ejabberd还不熟悉。Ejabberd使用自己的XML库fast_XML。IIRC可以使用fxml:get_subtags/2Wow来完成。。非常感谢你提供的所有这些细节。。但是我这里的数据包已经使用Ejabberd附带的fast_xml库进行了解析,我只是不知道如何从已经解析的xml中获取嵌套消息。我会再看一次你的答案,如果它能让我知道如何做到这一点,我会竖起大拇指。再次感谢您投入的时间。