Not logged in. · Lost password · Register
Forum: MatriX and XmppDotNet RSS
Avatar
martinbach #1
Member since Mar 2009 · 60 posts
Group memberships: Members
Show profile · Link to this post
Subject: How to use my own type in a stanza ?
Hi Alex,

I try to use my own type 'PersonAddress' in a stanza. My Problem is this part of code (you can find it at the end of my listing below):

        // !!! Problem: Error "Cannot implicitly convert type 'string' to 'XMPPWebService.Contracts.Elements.PersonPersonAddress"
        public PersonAddress Address
        {
            get { return GetTag<PersonAddress>(); }
            set { SetTag("Adresse"); }
        }


How should the code look like ?
What I have to to to use my own type ?

Here is my used contract:

namespace XMPPWebService.Contracts.Elements
{
    public class Person : XmppXElement
    {
        public class PersonAddress : XmppXElement
        {
            public PersonAddress()
                : base("Address")
            {
                this.SetTag("Address");
            }

            public string Street
            {
                get { return GetTag("Street"); }
                set { SetTag("Street", value); }
            }

            public int No
            {
                get { return GetTagInt("No"); }
                set { SetTag("No", value); }
            }

            public PersonAddress(string Street, int No)
                : base("Address")
            {
                this.Street = Street;
                this.No = No;
            }
        }


        public Person()
            : base("Person")
        {
            this.SetTag("Person");
        }

        public string Lastname
        {
            get { return GetTag("Lastname"); }
            set { SetTag("Lastname", value); }
        }

        public int Age
        {
            get { return GetTagInt("Age"); }
            set { SetTag("Age", value); }
        }


        // !!! Problem !!!:
        // Error "Cannot implicitly convert type 'string' to 'XMPPWebService.Contracts.Elements.PersonPersonAddress"
        public PersonAddress Address
        {
            get { return GetTag<PersonAddress>(); }
            set { SetTag("Adresse"); }
        }

        public Person(string Name, int Age)
            : base("Person")
        {
            this.Name = Name;
            this.Age = Age;
        }
    }
}
Avatar
Alex #2
Member since Feb 2003 · 4449 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
it works exactly the same way as it does on agsXMPP. Instead of Element you have to derive your custom objects from XmppXElement.
You also have to register your types in the factory. And you should use namespaces for your types.
I suggest to use xelement.Element<...> to get nested types instead of xelement.GetTag<...>

Alex
Avatar
martinbach #3
Member since Mar 2009 · 60 posts
Group memberships: Members
Show profile · Link to this post
Hi Alex,

get {Element<PersonAddress>();}
seems to work now, thanks.

What about the set {...} in my context?

I tried strange things like this:

               SetTag("Adresse", string.Concat(         SetTag("Strasse", value.Strasse).ToString(),
                                                                    SetTag("Hausnr", value.Hausnr).ToString()        ));

or more simple like this:

            set { SetTag("Adresse"); }

but nothing
returned what it should below the Tag <Address>.

This should be re result:

<Person>
    <Lastname>Bach<Lastname>
    <Age>42</Age>          
    <Address>
        <Strasse>Wallstreet</Strasse>
        <Hausnr>45</Hausnr>
    </Address>
</Person>


Could you please give me a hint ?

Thank you again.

Martin
Avatar
martinbach #4
Member since Mar 2009 · 60 posts
Group memberships: Members
Show profile · Link to this post
Hi Alex,

I still have problems to use my own object type in a stanza:

This should be the result:

<Person>                                       // class Person
    <Lastname>Bach<Lastname>            // type string
    <Age>42</Age>                             // type int
    <Address>                                     // class/type PersonAddress
        <Strasse>Wallstreet</Strasse>         // type string
        <Hausnr>45</Hausnr>                     // type int
    </Address>
</Person>

But I can't solve it.

Trying:
        public PersonAddress Address
        {
            get { return Element<PersonAddress>(); }
            set { SetTag("Adresse"); }
        }
as you told me results in:

<Person>
    <Lastname>Bach<Lastname>
    <Age>42</Age>          
    <Address />   
</Person>


Trying:
        public PersonAddress Address
        {
            get { return Element<PersonAddress>(); }
            set { Element<PersonAddress>; }
        }

results in

<Person>
    <Lastname>Bach<Lastname>
    <Age>42</Age>          
</Person> 


Using just PersonAddress
results correct as:

<Address>
     <Strasse>Wallstreet</Strasse>
      <Hausnr>45</Hausnr>
</Address>

What is my problem ? I've no idea now.

Please give me a hint.

Thanks,
Martin
Avatar
Alex #5
Member since Feb 2003 · 4449 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
you should use namespace. Here are the classes I created which use the namespace "ag:software"

  1. public class Person : XmppXElement
  2. {
  3.     public Person()
  4.         : base("ag:software", "Person")
  5.     {
  6.     }
  7.  
  8.     public string Lastname
  9.     {
  10.         get { return GetTag("Lastname"); }
  11.         set { SetTag("Lastname", value); }
  12.     }
  13.  
  14.     public int Age
  15.     {
  16.         get { return GetTagInt("Age"); }
  17.         set { SetTag("Age", value); }
  18.     }
  19.  
  20.     public Adress Adress
  21.     {
  22.         get { return Element<Adress>(); }
  23.         set { Replace<Adress>(value); }
  24.     }
  25. }
  26.  
  27. public class Adress : XmppXElement
  28. {
  29.     public Adress()
  30.         : base("ag:software", "Adress")
  31.     {
  32.     }
  33.  
  34.     public string Strasse
  35.     {
  36.         get { return GetTag("Strasse"); }
  37.         set { SetTag("Strasse", value); }
  38.     }
  39.  
  40.     public int Hausnr
  41.     {
  42.         get { return GetTagInt("Hausnr"); }
  43.         set { SetTag("Hausnr", value); }
  44.     }
  45. }

small sample how to build the required xml with these classes:

  1. public void Test()
  2. {
  3.     Person person = new Person()
  4.                         {
  5.                             Lastname = "Bach",
  6.                             Age = 42,
  7.                             Adress = new Adress() {Strasse = "Wallstreet", Hausnr = 45}
  8.                         };
  9.  
  10.     Console.WriteLine(person.ToString());
  11. }

Regards,
Alex
Avatar
martinbach #6
Member since Mar 2009 · 60 posts
Group memberships: Members
Show profile · Link to this post
Hi Alex,

geat :-))))  it works now.

The missing statement was "Replace":
set { Replace<Address>(value); }

And I also use namespaces now.

My try-and-error has an end now (related to that topic).


A question:

Will I find some hints/description in the documentation of the lib's release to get to know what method I have to use and in what circumstances (what is the idea of the method, poperty, ...)  ? With the content in the MatriX for Silverlight docu it's very hard for me to figure out what to use.

Nevertheless your support is really good, fast and friendly.

Thank you very much, Alex.

Martin
Avatar
Alex #7
Member since Feb 2003 · 4449 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
there is the xml documentation, I am working on examples, but they are not ready yet. So I think the best is to post your questions here.

Alex
Avatar
johnm60 #8
Member since Jun 2010 · 32 posts
Group memberships: Members
Show profile · Link to this post
In reply to post #5
Hi there

My first post here. I have a similar problem to martinbach, except that I am trying to return a collection of results from a custom Iq. I can't work out what is the correct format of an Iq that is a collection. To relate it to the Person and Address sample, how could this be changed is a person has a collection of Addresses?

Just to be clear, what I want to do is send an Iq and get a response that is a collection and to then iterate through the collection. Going back to my domain, the request would look like this:

  1. public class NearestVehicles : XmppXElement
  2.     {
  3.         public NearestVehicles()
  4.             : base("cgi-recovery:geoqueries", "nearestvehicles")
  5.         {
  6.         }
  7.  
  8.         public string Eastings
  9.         {
  10.             get { return GetTag("Eastings"); }
  11.             set { SetTag("Eastings", value); }
  12.         }
  13.  
  14.         public string Northings
  15.         {
  16.             get { return GetTag("Northings"); }
  17.             set { SetTag("Northings", value); }
  18.         }
  19. }

In the develop's guide, the response is the same type as the request, so we could add some extra properties to the request, which will get used in the response:

  1. public class NearestVehicles : XmppXElement
  2.     {
  3.         public NearestVehicles()
  4.             : base("cgi-recovery:geoqueries", "nearestvehicles")
  5.         {
  6.         }
  7.  
  8.         public string Eastings
  9.         {
  10.             get { return GetTag("Eastings"); }
  11.             set { SetTag("Eastings", value); }
  12.         }
  13.  
  14.         public string Northings
  15.         {
  16.             get { return GetTag("Northings"); }
  17.             set { SetTag("Northings", value); }
  18.         }
  19.  
  20.         public string VehicleName
  21.         {
  22.             get { return GetTag("VehicleName"); }
  23.             set { SetTag("VehicleName", value); }
  24.         }
  25.  
  26.         public string OfficeName
  27.         {
  28.             get { return GetTag("OfficeName"); }
  29.             set { SetTag("OfficeName", value); }
  30.         }
  31.  
  32.         public string Distance
  33.         {
  34.             get { return GetTag("Distance"); }
  35.             set { SetTag("Distance", value); }
  36.         }
  37.     }

This of course works for a single result, but if my query is to return a collection, what is the best pattern? Can I use the above and somehow (how?) construct more than one response, or if the correct pattern to have some form of collection in the Iq response, such as:
  1. class NearestVehiclesIq : Iq
  2.     {
  3.         public NearestVehiclesIq()
  4.         {
  5.             GenerateId();
  6.         }
  7.  
  8.         public VehicleCollection NearestVehicles
  9.         {
  10.             get { return Element<VehicleCollection>(); }
  11.             set { Replace(value); }
  12.         }
  13.     }
  14.  
  15. public class VehicleCollection : XmppXElement
  16.     {
  17.         List<NearestVehicles> lst;
  18.  
  19.         public VehicleCollection(): base("cgi-recovery:geoqueries", "nearestvehicles")
  20.         {
  21.             lst = new List<NearestVehicles>();
  22.         }
  23.  
  24.         public void Add(NearestVehicles vehicle)
  25.         {
  26.             lst.Add(vehicle);
  27.         }
  28.  
  29.         public List<NearestVehicles> Vehicles { get { return lst; } }
  30. }

The above code is not correct (otherwise I would not be asking :-))

I have only been looking at Matrix for a week or two, so please excuse any nativity.

Thanks
John
Avatar
Alex #9
Member since Feb 2003 · 4449 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
Hi John,

I removed some stuff from your classes to make it easier to understand for other programmers as well.

The Xml with a collection of vehicles will look like this:
  1. <Vehicles xmlns='cgi-recovery:geoqueries'>
  2.    <Vehicle>
  3.       <VehicleName>bus001</VehicleName>
  4.       <Distance>150</Distance>
  5.    </Vehicle>
  6.    <Vehicle>
  7.       <VehicleName>bus002</VehicleName>
  8.       <Distance>100</Distance>
  9.    </Vehicle>
  10.    <Vehicle>
  11.       <VehicleName>bus003</VehicleName>
  12.       <Distance>200</Distance>
  13.    </Vehicle>
  14. </Vehicles>

lets first create the object for a single vehicle. The Xml of a single vehicle which inherited your namespace looks like this:
  1. <Vehicle xmlns='cgi-recovery:geoqueries'>
  2.    <VehicleName>bus003</VehicleName>
  3.    <Distance>200</Distance>
  4. </Vehicle>

this is the code of the vehicle class:
  1. public class Vehicle : XmppXElement
  2. {
  3.     public Vehicle()
  4.         : base("cgi-recovery:geoqueries", "Vehicle")
  5.     {
  6.     }
  7.  
  8.     public string VehicleName
  9.     {
  10.         get { return GetTag("VehicleName"); }
  11.         set { SetTag("VehicleName", value); }
  12.     }
  13.  
  14.     public int Distance
  15.     {
  16.         get { return GetTagInt("Distance"); }
  17.         set { SetTag("Distance", value); }
  18.     }
  19. }

now we create the object for the vehicles collection (the <Vehicles/> node):
It contains a AddVehicle member to add new vehicles and the GetVehicles member to get all vehicles in the collection. Add more members as you need them.

  1. public class Vehicles : XmppXElement
  2. {
  3.     public Vehicles()
  4.         : base("cgi-recovery:geoqueries", "Vehicles")
  5.     {
  6.     }
  7.  
  8.     public void AddVehicle(Vehicle vehicle)
  9.     {
  10.         Add(vehicle);
  11.     }
  12.  
  13.     public IEnumerable<Vehicle> GetVehicles()
  14.     {
  15.         return Elements<Vehicle>();
  16.     }
  17. }

You have to register both classes in the factory.

Now we build a VehiclesIq class to build the custom Iq requests:

  1. public class VehiclesIq : Iq
  2. {
  3.     public VehiclesIq()
  4.     {
  5.         GenerateId();
  6.         Add(new Vehicles());
  7.     }
  8.  
  9.     public Vehicles Vehicles
  10.     {
  11.         get { return Element<Vehicles>(); }
  12.         set { Replace(value); }
  13.     }
  14. }

now we can use the VehiclesIq class to send a request to retrieve the vehicles.
Xml of the request :
  1. <iq id="MX_1" type="get" to="user@server.com/resource">
  2.  <Vehicles xmlns="cgi-recovery:geoqueries" />
  3. </iq>

the c# code to build this request:
  1. var iq = new VehiclesIq {Type = IqType.get, To = "user@server.com/resource"};

The receiving client now checks in the Iq handler if it gets an incoming request of type Vehicles with the following code and builds the response:
  1. private void xmppClient_OnIq(object sender, IqEventArgs e)
  2. {
  3.     if (e.Iq.Query is Vehicles && e.Iq.Type == IqType.get)
  4.     {
  5.         var resIq = new VehiclesIq
  6.                         {
  7.                             To = e.Iq.From,
  8.                             Id = e.Iq.Id,
  9.                             Type = IqType.result
  10.                         };
  11.  
  12.         resIq.Vehicles.Add(new Vehicle
  13.                                {
  14.                                    VehicleName = "bus001",
  15.                                    Distance = 100
  16.                                });
  17.  
  18.         resIq.Vehicles.Add(new Vehicle
  19.         {
  20.             VehicleName = "bus002",
  21.             Distance = 120
  22.         });
  23.  
  24.         resIq.Vehicles.Add(new Vehicle
  25.         {
  26.             VehicleName = "bus003",
  27.             Distance = 50
  28.         });
  29.  
  30.         xmppClient.Send(resIq);
  31.     }
  32. }

let me know if this helps you.

Alex
Avatar
johnm60 #10
Member since Jun 2010 · 32 posts
Group memberships: Members
Show profile · Link to this post
Hi Alex

Thanks for the rapid response. I have now got there.

What I was struggling with was the service consumer side and how to get the response and iterate over the vehicles collection. The important part that I was stuck on being that Vehicles is defined with:

  1. public IEnumerable<Vehicle> GetVehicles()
  2. {
  3.         return Elements<Vehicle>();
  4. }

Which allows one to later iterate over a collection

  1. foreach (Vehicle v in list.GetVehicles())
  2. {
  3.         // do something here
  4.         System.Diagnostics.Debug.WriteLine("Vehicle " + v.VehicleName + " found");
  5. }

Thanks very much.
John
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: