Not logged in. · Lost password · Register
Forum: MatriX RSS
Avatar
dodexahedron #1
Member since Mar 2015 · 7 posts
Group memberships: Members
Show profile · Link to this post
Subject: Arrays/Lists in Iq
Working on an IM distributor with queuing capability, and I'm not sure how to deal with collections in a class that I'm trying to send via Iq.

I have a class as follows:
  1. public class ImSession : XmppXElement
  2. {
  3.     public ImSession( )
  4.         : base( "x:Ams", "ImSession" )
  5.     {
  6.  
  7.     }
  8.  
  9.     public long Id
  10.     {
  11.         get { return GetAttributeLong( "Id" ); }
  12.         set { SetAttribute( "Id", value ); }
  13.     }
  14.  
  15.     public DateTimeOffset StartTime
  16.     {
  17.         get { return new DateTimeOffset( new DateTime( GetAttributeIso8601Date( "StartTime" ).Ticks, DateTimeKind.Utc ), TimeSpan.Zero ); }
  18.         set { SetAttributeIso8601Date( "StartTime", value.UtcDateTime ); }
  19.     }
  20.  
  21.     public DateTimeOffset LastActivity
  22.     {
  23.         get { return new DateTimeOffset( new DateTime( GetAttributeIso8601Date( "LastActivity" ).Ticks, DateTimeKind.Utc ), TimeSpan.Zero ); }
  24.         set { SetAttributeIso8601Date( "LastActivity", value.UtcDateTime ); }
  25.     }
  26.  
  27.     public int MessagesToClient
  28.     {
  29.         get { return GetAttributeInt( "MessagesToClient" ); }
  30.         set { SetAttribute( "MessagesToClient", value ); }
  31.     }
  32.  
  33.     public int MessagesToAgent
  34.     {
  35.         get { return GetAttributeInt( "MessagesToAgent" ); }
  36.         set { SetAttribute( "MessagesToAgent", value ); }
  37.     }
  38.  
  39.     public DateTimeOffset QueueStartTime
  40.     {
  41.         get { return new DateTimeOffset( new DateTime( GetAttributeIso8601Date( "QueueStartTime" ).Ticks, DateTimeKind.Utc ), TimeSpan.Zero ); }
  42.         set { SetAttributeIso8601Date( "QueueStartTime", value.UtcDateTime ); }
  43.     }
  44.  
  45.     public bool RonaIn
  46.     {
  47.         get { return GetAttributeBool( "RonaIn" ); }
  48.         set { SetAttribute( "RonaIn", value ); }
  49.     }
  50.  
  51.     public bool RonaOut
  52.     {
  53.         get { return GetAttributeBool( "RonaOut" ); }
  54.         set { SetAttribute( "RonaOut", value ); }
  55.     }
  56.  
  57.     public long OriginalConversationId
  58.     {
  59.         get { return GetAttributeLong( "OriginalConversationId" ); }
  60.         set { SetAttribute( "OriginalConversationId", value ); }
  61.     }
  62.  
  63.     public DateTimeOffset OriginalConversationStartTime
  64.     {
  65.         get { return new DateTimeOffset( new DateTime( GetAttributeIso8601Date( "OriginalConversationStartTime" ).Ticks, DateTimeKind.Utc ), TimeSpan.Zero ); }
  66.         set { SetAttributeIso8601Date( "OriginalConversationStartTime", value.UtcDateTime ); }
  67.     }
  68.  
  69.     public List<Message> Messages
  70.     {
  71.         get { return GetTag( "Messages" ); }
  72.         set {   SetTag( "Messages", value ); }
  73.     }
  74.  
  75.     public bool WasQueued
  76.     {
  77.         get { return GetAttributeBool( "WasQueued" ); }
  78.         set { SetAttribute( "WasQueued", value ); }
  79.     }
  80. }

I create an object and send it with:
_lient.IqFilter.SendIq( ronaIq, ( o, args ) => {/*don't care about response*/ } );

On the receiving side, it is handled in the OnIq event:

  1. if ( e.Iq.Type == IqType.set && e.Iq.Query is ImSession )
  2. {
  3.   ImSession session = (ImSession)e.Iq.Query;
  4.  //use the class to do business work
  5. }

This, however, doesn't work, because SetTag doesn't understand List<T>.
How can I deal with a collection in Iq?
This post was edited 2 times, last on 2015-03-11, 23:21 by Alex.
Avatar
dodexahedron #2
Member since Mar 2015 · 7 posts
Group memberships: Members
Show profile · Link to this post
I've tried passing that list through a JSON serializer (simplest one-line serialization available), and then base-64 encoding that value, like so:

  1. public List<Message> Messages
  2. {
  3.     get { return JsonConvert.DeserializeObject<List<Message>>( Encoding.UTF8.GetString( Convert.FromBase64String( GetTag( "Messages" ) ) ) ); }
  4.     set { SetTag( "Messages", Convert.ToBase64String( Encoding.UTF8.GetBytes( JsonConvert.SerializeObject( value ) ) ) ); }
  5. }

However, something gets mangled in transit, because the decoded string has extra unprintable characters that scare the Json deserializer off.
This post was edited 2 times, last on 2015-03-11, 23:26 by Alex.
Avatar
Alex #3
Member since Feb 2003 · 4296 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
can you post a sample of the Xml you are tying to build? Then I can show you how to achieve this with MatriX.
How does your Message object looks?

Alex
Avatar
dodexahedron #4
Member since Mar 2015 · 7 posts
Group memberships: Members
Show profile · Link to this post
Ideally, I would like to end up with something like this:

  1. <iq xmlns="jabber:client" id="MX_5" to="test1@place.net/A01" from="test2@place.net/A01" type="set">
  2.  <ImSession xmlns="x:Ams" LastActivity="2015-03-11T22:01:18.238Z" RonaIn="true" StartTime="2015-03-11T22:01:39.691Z" WasQueued="false" OriginalConversationId="11622" OriginalConversationStartTime="2015-03-11T18:01:18.184Z">
  3.     <ClientIndividualId>759139</ClientIndividualId>
  4.     <Messages>
  5.         <Message>
  6.             <!--All the message properties 1 -->
  7.         </Message>
  8.         <Message>
  9.             <!--All the message properties 2 -->
  10.         </Message>
  11.     </Messages>
  12.  </ImSession>
  13. </iq>

However, if I understand correctly, that's not technically legal?
That's why I am taking that Messages list and serializing it to a base-64 string.

Here's what I end up with:

  1. <iq xmlns="jabber:client" id="MX_5" to="test1@place.net/A01" from="test2@place.net/A01" type="set">
  2.  <ImSession xmlns="x:Ams" LastActivity="2015-03-11T22:01:18.238Z" RonaIn="true" StartTime="2015-03-11T22:01:39.691Z" WasQueued="false" OriginalConversationId="11622" OriginalConversationStartTime="2015-03-11T18:01:18.184Z">
  3.     <ClientIndividualId>759139</ClientIndividualId>
  4.     <Messages>BASE64DATA</Messages>
  5.  </ImSession>
  6. </iq>


To be completely honest, I don't actually care how the XML is serialized, so long as I can get that List<Message> across to the other side.  If it comes across as individual XML elements or some other way, I'm equally as happy.
This post was edited 2 times, last on 2015-03-13, 06:38 by Alex.
Avatar
Alex #5
Member since Feb 2003 · 4296 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
Its pretty easy to serialize your messages as a List and return them as a list.

with your given Xml structure I would create 2 more custom classes:

  1. public class Messages : XmppXElement
  2. {
  3.  public ImSession()
  4.       : base( "x:Ams", "Messages" )
  5.  {
  6.  }
  7.    
  8.  public IEnumerable<Message> All
  9.  {
  10.     get { return Elements<Message>(); }
  11.  }  
  12.  
  13.  public Message AddMessage(Message msg)
  14.  {
  15.       Add(msg);         
  16.       return msg;
  17.  }
  18. }
  19.  
  20. public class Message : XmppXElement
  21. {
  22.  public ImSession()
  23.       : base( "x:Ams", "Message" )
  24.  {
  25.  }
  26. }

of course you can also use any other Xml serializer, json serializer or use encodings like base64.
See here for example:
http://www.ag-software.net/matrix-xmpp-sdk/matrix-develope…

Alex
Avatar
dodexahedron #6
Member since Mar 2015 · 7 posts
Group memberships: Members
Show profile · Link to this post
Thanks for the example.

That message class was already Matrix.Xmpp.Client.Message.

So, I implemented the Messages class as suggested:

  1.     public class Messages : XmppXElement
  2.     {
  3.         public Messages( )
  4.             : base( "x:Ams", "Messages" )
  5.         {
  6.         }
  7.  
  8.         public IEnumerable<Message> All
  9.         {
  10.             get { return Elements<Message>( ); }
  11.         }
  12.  
  13.         public Message AddMessage( Message msg )
  14.         {
  15.             Add( msg );
  16.             return msg;
  17.         }
  18.     }


Then, I changed the Messages property of the ImSession class to look like this:

        public Messages Messages
        {
            get { return Element<Messages>( ); }
            set { Replace( value ); }
        }


This results in the following XML:
  1. <iq xmlns="jabber:client" id="MX_4" to="distributor@place.net/A" from="distributor@place.net/A" type="set">
  2.  <ImSession xmlns="x:Ams" ClientId="user@place.net" LastActivity="2015-03-13T18:03:40.380Z" RonaIn="true" StartTime="2015-03-13T18:04:15.179Z" WasQueued="true" OriginalConversationId="11635" OriginalConversationStartTime="2015-03-13T14:03:39.847Z">
  3.     <ClientIndividualId>759139</ClientIndividualId>
  4.     <Messages>
  5.       <message xmlns="" id="9646f1d8b66be532-user@place.net-746" to="distributor@place.net" type="chat" from="user@place.net/im-default">
  6.         <body>1</body>
  7.         <active xmlns="http://jabber.org/protocol/chatstates" />
  8.         <html xmlns="http://jabber.org/protocol/xhtml-im">
  9.           <body xmlns="http://www.w3.org/1999/xhtml">
  10.             <p>
  11.               <span>1</span>
  12.             </p>
  13.           </body>
  14.         </html>
  15.         <req xmlns="x:x-receipts" acct="true" recv="true" read="true" />
  16.         <key xmlns="x:fim" id="9646f1d8b66be532-user@place.net-746" timestamp="2015-03-13T18:03:39.847Z" />
  17.       </message>
  18.       <message xmlns="" id="9646f1d8b66be532-user@place.net-748" to="distributor@place.net" type="chat" from="user@place.net/im-default">
  19.         <body>2</body>
  20.         <active xmlns="http://jabber.org/protocol/chatstates" />
  21.         <html xmlns="http://jabber.org/protocol/xhtml-im">
  22.           <body xmlns="http://www.w3.org/1999/xhtml">
  23.             <p>
  24.               <span>2</span>
  25.             </p>
  26.           </body>
  27.         </html>
  28.         <req xmlns="x:x-receipts" acct="true" recv="true" read="true" />
  29.         <key xmlns="x:fim" id="9646f1d8b66be532-user@place.net-748" timestamp="2015-03-13T18:03:39.967Z" />
  30.       </message>
  31.       <message xmlns="" id="9646f1d8b66be532-user@place.net-753" to="distributor@place.net" type="chat" from="user@place.net/im-default">
  32.         <body>3</body>
  33.         <active xmlns="http://jabber.org/protocol/chatstates" />
  34.         <html xmlns="http://jabber.org/protocol/xhtml-im">
  35.           <body xmlns="http://www.w3.org/1999/xhtml">
  36.             <p>
  37.               <span>3</span>
  38.             </p>
  39.           </body>
  40.         </html>
  41.         <req xmlns="x:x-receipts" acct="true" recv="true" read="true" />
  42.         <key xmlns="x:fim" id="9646f1d8b66be532-user@place.net-753" timestamp="2015-03-13T18:03:40.966Z" />
  43.       </message>
  44.     </Messages>
  45.  </ImSession>
  46. </iq>




The problem I'm seeing is when I do the following, on the receiver side:

ImSession session = (ImSession)e.Iq.Query;
Console.WriteLine(session.ToString());  //Print out the XML above
List<Message> messages1 = session.Messages.All.ToList();  //This is empty
Console.WriteLine(session.Messages.All.Count);  // prints 0

Why is the collection empty?
If I had to guess, I'd assume the problem is with the xmlns="" in the message elements.
Shouldn't that be xmlns="jabber:client"?
It's the Matrix.Xmpp.Client.Message class, so without the right namespace, it's not technically the right element, is it?
Can I affect the namespace for those elements?  Should I have to?  Why did the namespace get reset to blank, instead of jabber:client?
Avatar
Alex #7
Member since Feb 2003 · 4296 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
yes the problem is the empty namespace. Its only a Matrix.Xmpp.Client.Message object when the message tag is in the jabber:client namespace.

  • How do you build this Xml?

Alex
This post was edited on 2015-03-13, 19:52 by Alex.
Avatar
dodexahedron #8
Member since Mar 2015 · 7 posts
Group memberships: Members
Show profile · Link to this post
That XML was taken from the OnReceiveXml event on the receiver.

It was built by Matrix, using the ImSession class posted above, which is wrapped in the ImSessionIq class, as below:

  1. //Essentially copied from the examples
  2. public class ImSessionIq : Iq
  3. {
  4.     public ImSessionIq( )
  5.     {
  6.         GenerateId(  );
  7.     }
  8.     public ImSession ImSession
  9.     {
  10.         get { return Element<ImSession>( ); }
  11.         set { Replace( value ); }
  12.     }
  13. }

This object is constructed, properties are set, and then it is sent with the following line:
  1. _client.IqFilter.SendIq( ronaIq, ( o, args ) => { } );


I have registered the relevant classes with:

  1. Factory.RegisterElement<Messages>( "x:Ams", "Messages" );
  2. Factory.RegisterElement<ImSession>( "x:Ams", "ImSession" );
This post was edited on 2015-03-13, 19:52 by dodexahedron.
Avatar
Alex #9
Member since Feb 2003 · 4296 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
Quote by dodexahedron:
That XML was taken from the OnReceiveXml event on the receiver.

yes, my question was how the sender built this Xml. Because the empty namespace was set either on the client side or injected on the server during routing (which should not happen).

Alex
Avatar
dodexahedron #10
Member since Mar 2015 · 7 posts
Group memberships: Members
Show profile · Link to this post
How about we take it to the chat room?
I imagine we can hash it out a lot quicker that way.
Avatar
dodexahedron #11
Member since Mar 2015 · 7 posts
Group memberships: Members
Show profile · Link to this post
For posterity:
We met up in the support chat room.
We took a look at the XML that was being sent and received.
The XML that was sent had the correct namespace, but the XML that was received had the empty namespace.
Openfire must be stripping the namespace.
Close Smaller – Larger + Reply to this post:
Verification code: VeriCode Please enter the word from the image into the text field below. (Type the letters only, lower case is okay.)
Smileys: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Special characters:
Forum: MatriX RSS