哎呀,这是二郎!这是一个使用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>
我的.erl:
-module(my).
-compile(export_all).
-include_lib("./xmerl.hrl").
get_doc() ->
{ParsedDoc, _Rest} = xmerl_scan:file("./message.xml"),
ParsedDoc.
get_message() ->
Messages = xmerl_xpath:string("//message", get_doc()),
%io:format("~p~n", [Messages]),
lists:last(Messages).
get_attributes(Node) ->
xmerl_xpath:string("./@*", Node).
convert_to_map(Attrs) ->
lists:foldl(
fun({xmlAttribute,Name,_,_,_,_List,_,_,Value,_}, Acc) ->
Acc#{Name => Value}
end,
#{}, % initial value for Acc
Attrs
).
如果您已经将消息作为字符串,还有一个名为 xmerl_scan:string/1 的函数,例如:
{ParsedMessage, _RemainingText = ""} = xmerl_scan:string(Message)
您还需要文件xmerl.hrl。
在这个函数中:
get_message() ->
Messages = xmerl_xpath:string("//message", get_doc()),
lists:last(Messages).
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.
===灵丹妙药===
您可以使用SweetXml 来解析您的“数据包”:
defmodule XmlExample do
import SweetXml
def sweet(path) do
File.read!(path)
|> xpath(~x"//message"l)
|> Enum.at(-1)
|> xpath(~x"//@from")
end
end
第一个 xpath() 调用返回一个 (l)ist,即所有匹配项,而不仅仅是第一个匹配项。该列表将包含一个或两个消息标签——取决于数据包。 Enum.at(-1) 将返回列表中的最后一个消息标签,该标签要么是嵌套消息标签,要么是没有嵌套消息标签时的根消息标签。第二个xpath() 调用返回消息标签的from 属性,在嵌套数据包的情况下会产生:
'coven@muc.shakespeare.example/secondwitch'
我注意到 SweetXml 返回一个字符列表(单引号字符串)而不是双引号字符串(这可能是您想要的)。如果在第二个xpath() 调用中添加s,则返回值将是双引号字符串:
|> 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"
我不知道是否有更好的方法,但是要获取标签的所有属性,您可以这样做:
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
输出:
[
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}
]
然后代码模式匹配元组以挑选出每个属性的name及其value。
如果您希望获取地图中的所有属性:
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
输出:
%{
from: 'coven@muc.shakespeare.example/secondwitch',
to: 'hag66@shakespeare.example/pda',
type: 'groupchat'
}