aSmack - Пакет в XML с использованием Packet Listener выводит нулевые элементы

Я использую aSmack в своем приложении для Android для связи с моим сервером XMPP, и я включил отладку для Smack, чтобы видеть все входящие/исходящие XML. Моя проблема здесь в том, что я использую PacketListener для получения ответа сервера для пакета, который я отправил, но когда я вызываю метод toXML() пакета, я получаю странный вывод.
Классы и более подробная информация ниже.

На моем сервере есть реализация XEP-0136, и, поскольку у aSmack еще нет кода для него, я создаю пакет IQ и отправляю его, и он работает, как и ожидалось, как вы можете видеть ниже в моем XML-пакете, разработанном в XEP-0136, получение списка коллекций ( http://xmpp.org/extensions/xep-0136.html#manage-list )

Xml-пакет отправлен на сервер

06-13 14:11:21.769: D/SMACK(3018): 02:11:21 PM SENT (1079273464): 
<iq id="[email protected]/Smack/Conversations" type="get">
  <list with="[email protected]" xmlns="urn:xmpp:archive">
    <set xmlns="http://jabber.org/protocol/rsm">
      <max>30</max>
    </set>
  </list>
</iq>

Чтобы создать этот XML, я получил файлы схемы XML, разработанные в XEP-0136, и с помощью библиотеки SimpleXML я сопоставил все элементы, и это код, который я использую для создания и отправки пакета:

XMPPService.java

private static final int MAX_LIST = 30;

public void getConversations(String email, BaseActivity activity)
{
    if (isAuthenticated())
    {
        String packetId = connection.getUser() + "/Conversations";
        Set set = new Set();
        set.setMax(MAX_LIST);

        List list = new List();
        list.setWith(email);
        list.setSet(set);

        final IQ iq = new IQ();
        iq.setList(list);
        iq.setType(IQType.get);
        iq.setId(packetId);

        PacketIDFilter filter = new PacketIDFilter(packetId);
        connection.addPacketListener(new ChatListListener(activity), filter);
        sendPacket(iq);
    }
}

public void sendPacket(IQ iq)
{
    if (isAuthenticated())
    {
        connection.sendPacket(new IQPacket(iq));
    }
}

IQPacket.java

public class IQPacket extends Packet {

 private IQ iq;

 public IQPacket(IQ iq)
 {
    this.iq = iq;
 }

 public IQPacket(Packet packet, IQ iq)
 {
    super(packet);
    this.iq = iq;
 }

 @Override
 public String toXML()
 {
    StringWriter writer = new StringWriter();
    Serializer serializer = new Persister();
    try
    {
        serializer.write(iq, writer);
        return writer.getBuffer().toString();
    } catch (Exception e)
    {
        Log.e("COMPANY", "Error serializing xml", e);
    }
    return null;
 }

}

Как я уже сказал, эта часть работает, моя проблема заключается в прослушивателе, когда я вызываю метод toXML() для полученного пакета, я не могу получить важную информацию о чатах, но вывод отладки Smack выводит мне все информация, которую я ожидаю, как вы можете видеть ниже:

Smack Debug для полученного XML

06-13 14:11:21.989: D/SMACK(3018): 02:11:21 PM RCV  (1079273464): 
<iq type="result" id="[email protected]/Smack/Conversations" to="[email protected]/Smack">
  <list xmlns="urn:xmpp:archive">
    <chat with="[email protected]" start="2013-06-10T13:19:25.000Z"/>
    <chat with="[email protected]" start="2013-06-10T13:36:50.876Z"/>
    <set xmlns="http://jabber.org/protocol/rsm">
      <first index="0">2</first>
      <last>3</last>
      <count>9</count>
    </set>
  </list>
</iq>

Кроме того, этот XML является ожидаемым ответом, так как все эти элементы отображаются как JavaBeans, но это то, что я получаю, когда получаю пакет в своем ChatListener и вызываю метод toXML():

06-13 14:11:22.009: I/System.out(3018): 
<iq id="[email protected]/Smack/Conversations" to="[email protected]/Smack" type="result">nullnullnullnullnullnull2nullnull3nullnull9nullnull</iq>

ChatListListener.java

public class ChatListListener implements PacketListener {

 private BaseActivity activity;

 public ChatListListener(BaseActivity activity)
 {
    this.activity = activity;
 }

 @Override
 public void processPacket(Packet packet)
 {
    activity.notifyPacketReceived();
    System.out.println(packet.toXML());
 }
}

Пакет взят из org.jivesoftware.smack.packet.Packet, поэтому это пакет по умолчанию из библиотеки aSmack.

Итак, мой вопрос: чем я отличаюсь от отладчика Smack? Я посмотрел его код и увидел, что он также вызывает метод toXML() из пакета и добавляет ReceiveListener. Моя идея заключается в том, что после вызова toXML() я могу использовать SimpleXML для преобразования его в свой IQ.java, который я сопоставил, и начать использовать его информацию.

ИЗМЕНИТЬ

Добавление дополнительной информации. Итак, после просмотра кода Smack и того, как он обрабатывает полученный пакет, я понял, что, возможно, мне следует использовать IQProvider. Итак, я зарегистрировал свой IQProvider

ProviderManager.getInstance().addIQProvider("list", "urn:xmpp:archive", new ListIQProvider());

И после этого я ставлю точку останова на метод parseIQ(XmlPullParser arg0) моего IQProvider, и пакет действительно отправляется моему провайдеру, но все равно содержит все эти нулевые элементы. Я сейчас немного растерялся, потому что мне нужно, чтобы это продолжало работать, я буду продолжать исследовать исходный код Smack.


person Davi Alves    schedule 13.06.2013    source источник


Ответы (2)


После долгих исследований и поиска источников Smack я нашел решение. Шаги: добавьте IQProvider, добавьте PacketListener для вашего пакета, отправьте пакет. Дождитесь пакета на вашем IQProvider, проанализируйте его и получите ответ на свой прослушиватель.

Таким образом, addIQProvider и addPacketListener были правы, дело в том, что мне нужно выполнить полный анализ XML в моем ListIQProvider, это звучит просто, но мне потребовалось некоторое время, чтобы понять это.

public class ListIQProvider implements IQProvider {

 public ListIQProvider()
 {
 }

 @Override
 public IQ parseIQ(XmlPullParser parser) throws Exception
 {
    Logger.d(String.format("Received iq packet, namespace[%s], name[%s]", parser.getNamespace(), parser.getName()));
    ListIQ iq = new ListIQ();
    ListIQ.Set set = new Set();
    boolean done = false;

    String with = "", start = "";
    while (!done)
    {
        int eventType = parser.next();
        if (eventType == XmlPullParser.START_TAG)
        {
            if (parser.getName().equals("chat"))
            {
                with = parser.getAttributeValue("", "with");
                start = parser.getAttributeValue("", "start");
                iq.addChat(new Chat(with, start));
            }
            else if (parser.getName().equals("first"))
            {
                int index = parseInt(parser.getAttributeValue("", "index"));
                set.setIndexAtt(index);
                int first = parseInt(parser.nextText());
                set.setFirst(first);
            }
            else if (parser.getName().equals("last"))
            {
                int last = parseInt(parser.nextText());
                set.setLast(last);
            }
            else if (parser.getName().equals("count"))
            {
                int count = parseInt(parser.nextText());
                set.setCount(count);
            }
        }
        else if (eventType == XmlPullParser.END_TAG)
        {
            if (parser.getName().equals("list"))
            {
                iq.setSet(set);
                done = true;
            }
        }
    }

    return iq;
 }

 private int parseInt(String integer)
 {
    return Integer.parseInt((integer != null ? integer : "0"));
 }
}

После этого все, что мне нужно было сделать в моем ChatListListener, — это преобразовать Packet в мой класс ListIQ. Вот и все. Дело в том, что Packet, полученный моим ChatListListener, — это тот же самый Packet, который возвращается методом parseIQ в моем ListIQProvider. Итак, в этом вопросе/ответе у нас есть почти все, что нужно для XEP-0136, или хотя бы начать его использовать. Поскольку я не нашел в Интернете хорошего и простого источника, который мог бы мне помочь, я делюсь своим здесь. Под ним находится класс ListIQ:

public class ListIQ extends IQ {

 private List<Chat> chats;

 private Set set;

 public ListIQ()
 {
    this.chats = new ArrayList<ListIQ.Chat>();
 }

 public Set getSet()
 {
    return set;
 }

 public void setSet(Set set)
 {
    this.set = set;
 }

 public void addChat(Chat chat)
 {
    chats.add(chat);
 }

 public List<Chat> getChats()
 {
    return chats;
 }

 @Override
 public String getChildElementXML()
 {
    StringBuilder builder = new StringBuilder("<list xmlns=\"urn:xmpp:archive\">");
    for (Chat chat : chats)
    {
        builder.append(chat.toXml());
    }
    builder.append(set.toXml());
    builder.append("</list>");
    return builder.toString();
 }

 public static class Chat {
    private String with;
    private String start;

    public Chat()
    {
    }

    public Chat(String with, String start)
    {
        this.with = with;
        this.start = start;
    }

    public String getWith()
    {
        return with;
    }

    public void setWith(String with)
    {
        this.with = with;
    }

    public String getStart()
    {
        return start;
    }

    public void setStart(String start)
    {
        this.start = start;
    }

    public String toXml()
    {
        StringBuilder builder = new StringBuilder("<chat with=\"");
        builder.append(with).append("\"");
        builder.append(" start=\"");
        builder.append(start);
        builder.append("\"/>");
        return builder.toString();
    }

 }

 public static class Set {
    private int last;
    private int count;
    private int indexAtt;
    private int first;

    public Set()
    {
    }

    public int getLast()
    {
        return last;
    }

    public void setLast(int last)
    {
        this.last = last;
    }

    public int getCount()
    {
        return count;
    }

    public void setCount(int count)
    {
        this.count = count;
    }

    public int getIndexAtt()
    {
        return indexAtt;
    }

    public void setIndexAtt(int indexAtt)
    {
        this.indexAtt = indexAtt;
    }

    public int getFirst()
    {
        return first;
    }

    public void setFirst(int first)
    {
        this.first = first;
    }

    public String toXml()
    {
        StringBuilder builder = new StringBuilder("<set xmlns=\"http://jabber.org/protocol/rsm\">");
        builder.append("<first index=\"").append(indexAtt).append("\">").append(first).append("</first>");
        builder.append("<last>").append(last).append("</last>");
        builder.append("<count>").append(count).append("</count>");
        builder.append("</set>");
        return builder.toString();
    }
 }

}
person Davi Alves    schedule 17.06.2013
comment
можете ли вы ответить stackoverflow.com/ вопросов/22566411/ Я тоже сталкиваюсь с той же проблемой - person No_Rulz; 22.03.2014
comment
Спасибо за полный исходник, это именно то, что мне было нужно. Поэтому сейчас люди предпочитают JSON или это просто ужасный дизайн? - person sbaar; 30.08.2014
comment
Без проблем. На самом деле у XEP хороший дизайн, просто реализовать его самостоятельно хлопотно. Я не думаю, что JSON будет соответствовать требованиям XMPP, я думаю, имеет смысл использовать XML, а также протокол старше, чем JSON. - person Davi Alves; 01.09.2014

Привет, у меня тоже была такая же проблема, и я могу сказать, что этот код работает, я бы просто изменил способ построения запроса для списка на сервер. (по крайней мере на моем сервере)

final IQ iq = new IQ()
    {

        @Override public String getChildElementXML()
        {

            return "<list xmlns='urn:xmpp:archive'/>";

        }
    };

    iq.setType(IQ.Type.GET);

    PacketTypeFilter filter = new PacketTypeFilter(Packet.class);


    connection.addPacketListener(new PacketListener()
    {
        @Override public void processPacket(Packet packet)
        {
            Log.i(TAG, packet.toXML());
        }
    }, filter);

    connection.sendPacket(iq);

как вы сказали ранее, это кажется довольно простым, но в документации не совсем понятно, как построить объект IQ. Я надеюсь, что это помогает.

person pleonasmik    schedule 12.02.2014
comment
Но вы все равно не сможете разобрать ответ. Вы просто делаете возможным отправить запрос на получение чатов. - person Davi Alves; 13.02.2014
comment
Конечно нет, для обработки ответа вам нужно зарегистрировать провайдера, как вы это сделали. Я просто попытался сделать ваш пост немного лучше, но ваш код является важной частью, поэтому я разработал классы, которые расширяются от IQProvider для анализа чата (и сообщения), совершенно разные. Тем не менее, я дал вам +1, потому что ваш пост мне очень помог. - person pleonasmik; 14.02.2014
comment
Эй, я пытался, но я получаю ошибку 503, служба недоступна. ‹iq id=hsWL2-4 [email protected] [email protected] type=error›‹код ошибки=503 type=CANCEL› ‹service-unavailable xmlns=urn: ietf:params:xml:ns:xmpp-stanzas/›‹/error›‹/iq› - person Hardik; 06.03.2014
comment
@Hardik, похоже, сервер не работает или архив сообщений недоступен? - person pleonasmik; 22.04.2014