dkokkinos
dkokkinos

Reputation: 411

xml rpc response map to object C#

I have the following

`<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
   <params>
      <param>
         <value>
            <struct>
               <member>
                  <name>1</name>
                  <value>
                     <struct>
                        <member>
                           <name>imageID</name>
                           <value>
                              <string>40087</string>
                           </value>
                        </member>
                        <member>
                           <name>imageURL</name>
                           <value>                         <string>http://local.server.gr/public_html/main/phpthumb/phpThumb.php?id=40087</string>
                           </value>
                        </member>
                     </struct>
                  </value>
               </member>
               <member>
                  <name>2</name>
                  <value>
                     <struct>
                        <member>
                           <name>imageID</name>
                           <value>
                              <string>40088</string>
                           </value>
                        </member>
                        <member>
                           <name>imageURL</name>
                           <value>
                              <string>http://local.server.gr/public_html/main/phpthumb/phpThumb.php?id=40088</string>
                           </value>
                        </member>
                     </struct>
                  </value>
               </member>
               <member>
                  <name>3</name>
                  <value>
                     <struct>
                        <member>
                           <name>imageID</name>
                           <value>
                              <string>40089</string>
                           </value>
                        </member>
                        <member>
                           <name>imageURL</name>
                           <value>
                              <string>http://local.server.gr/public_html/main/phpthumb/phpThumb.php?id=40089</string>
                           </value>
                        </member>
                     </struct>
                  </value>
               </member>
            </struct>
         </value>
      </param>
   </params>
</methodResponse>`

xmlrpc response from a server.

I have tried different types of objects that accept and successfully serialize this xml but not with success. I cannot understand the "name" element that changes integer values. Any help is appreciated.

Upvotes: 1

Views: 2357

Answers (3)

dkokkinos
dkokkinos

Reputation: 411

After considering your answers, the dynamic keyword make me think of this solution that is not perfect but does its work. I make return type of the method dynamic and then analyse with the visual studio debugger the dynamic object at run time. Then i construct this class:

public class ImageListResult
{
    public List<ImageSlot> Slots { get; set; }
    public ImageListResult(dynamic res)
    {
        this.Slots = new List<ImageSlot>();
        try {
            int keys = res.Keys.Count;
            int values = res.Values.Count;

            if (keys == values)
            {
                for (int i = 0; i < keys; i++)
                {
                    ImageSlot slot = new ImageSlot()
                    {
                        Index = int.Parse(res.Keys[i])
                    };

                    int subCount = res.Values[i].Keys.Count;
                    for (int j = 0; j < subCount; j++)
                    {
                        string k = res.Values[i].Keys[j];
                        string v = res.Values[i].Values[j];
                        if (k.ToLower().Trim() == "imageid")
                        {
                            slot.ImageId = v;
                        }
                        else if(k.ToLower().Trim() == "imageurl")
                        {
                            slot.ImageUrl = v;
                        }
                    }

                    this.Slots.Add(slot);
                }
            }
        }catch(Exception ex)
        {

        }
    }

    public class ImageSlot
    {
        /// <summary>
        /// το index του image slot, απο το 1 εως το 25
        /// </summary>
        public int Index { get; set; } 

        public string ImageId { get; set; }

        public string ImageUrl { get; set; }
    }

}

so i successfully managed to deserialize the response. Its not ideal but its OK. If anyone think of a better solution feel free to post it, thanks for your answers

Upvotes: 0

jdweng
jdweng

Reputation: 34421

Try xml linq. Works real nice in this case

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;


namespace ConsoleApplication33
{

    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {

            XDocument doc = XDocument.Load(FILENAME);
            var results = doc.Descendants("struct").FirstOrDefault().Elements("member").Select(x => new {
                name = (int)x.Element("name"),
                imageID = x.Descendants("member").Where(y => (string)y.Element("name") == "imageID").Select(z => (int)z.Descendants("string").FirstOrDefault()).FirstOrDefault(),
                imageURL = x.Descendants("member").Where(y => (string)y.Element("name") == "imageURL").Select(z => (string)z.Descendants("string").FirstOrDefault()).FirstOrDefault(),

            }).ToList();
        }
    }


}

Into a class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;


namespace ConsoleApplication33
{

    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {

            XDocument doc = XDocument.Load(FILENAME);
            Image.images = doc.Descendants("struct").FirstOrDefault().Elements("member").Select(x => new Image() {
                name = (int)x.Element("name"),
                imageID = x.Descendants("member").Where(y => (string)y.Element("name") == "imageID").Select(z => (int)z.Descendants("string").FirstOrDefault()).FirstOrDefault(),
                url = x.Descendants("member").Where(y => (string)y.Element("name") == "imageURL").Select(z => (string)z.Descendants("string").FirstOrDefault()).FirstOrDefault(),

            }).ToList();
        }
    }
    public class Image
    {
        public static List<Image> images { get; set; }
        public int name { get; set; }
        public int imageID { get; set; }
        public string url { get; set; }
    }


}

Upvotes: 0

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131591

XML-RPC was created by Microsoft and superseded by SOAP before the first .NET version in 2002. The trouble with it is that you have no idea what the actual objects' schema is. XML-RPC is similar to Json, it serializes everything as name/value items that could be used to reconstruct an object's properties. You have no idea though what all those properties are.

What you can do, is deserialize the elements so that you can read them at least. Luckily, XML-RPC was used by Frontpage and its usage survived with SharePoint. This means there is a ready-made schema you can use here.

You can generate C# classes from it using the old xsd.exe program. Save the schema to a file (eg xmlrpc.xsd), ensure there are no whitespaces before the tag and use the following line to generate the classes :

xsd xmlrpc.xsd /c /nx:XlmRpcNS

where XmlRpcNS is the namespace you want to use for the generated classes. Add the generated file to your project (by default it's xmlrpc.cs) and use them to deserialize the response, eg:

using (var reader = new StringReader(response))
{
    var sz = new XmlSerializer(typeof(methodResponse));
    var methodResponse= (methodResponse)sz.Deserialize(reader);
    var content = (methodResponseParams)methodResponse.Item;
    var structContent = (StructType)content.param.value.Item;
    //...
}

The problem is that xsd.exe generates an object Item property for each choice element, which is why you need to cast to the proper type.

Another option is to use dynamic to avoid the casts. You lose Intellisens this way though:

dynamic methodResponse= (methodResponse)sz.Deserialize(reader);
var members = methodResponse.Item.param.value.Item;

Upvotes: 1

Related Questions