Упс, это эрланг!Вот решение erlang, использующее xmerl
, который является встроенным модулем синтаксического анализа xlan для erlang:
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>
my.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>
Чтобы получить тег body (или любой другой вложенный тег) в сообщении:
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 возвращает charlist (строка в одинарных кавычках) вместострока в двойных кавычках (что, вероятно, то, что вы хотите).Если вы добавите 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"
Я неузнать, есть ли лучший способ, но чтобы получить все атрибуты тега, вы можете сделать это:
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
output:
[
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
output:
%{
from: 'coven@muc.shakespeare.example/secondwitch',
to: 'hag66@shakespeare.example/pda',
type: 'groupchat'
}