Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从Erlang中的MucSub事件中提取嵌套的ejabberd消息元素_Erlang_Ejabberd - Fatal编程技术网

如何从Erlang中的MucSub事件中提取嵌套的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> 试验 其他结构示例:

我想在ejabberd数据包中找到消息元素。 数据包本身是一个消息元素,但有时(延迟消息或其他情况)实际消息嵌套在数据包内:

正常信息:

<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中获取嵌套消息。我会再看一次你的答案,如果它能让我知道如何做到这一点,我会竖起大拇指。再次感谢您投入的时间。