Friday, July 19, 2013

Object seriliazitation with Type attributes in the nodes.

Here is a problem that I had while consuming a service in C#.

What I needed to do:
I got a service from a vendor. This service is an XML post to a PHP page (btw, I hate these).  This vendor provided me with a sample XML that would be the package sent. I need to populate every node with my data and make the call. I would much rather have an object to do this, so I created one. I serialized my object and prepared for the posing of it. When comparing my result with the provided XML I was missing attributes on my object so I started looking on how to add them.

My vendor provided XML was similar to this:

<?xml version="1.0" encoding="utf-16"?>
<user>
  <FirstName Type="string">Soy</FirstName>
  <LastName Type="string">Nerdito</LastName>
  <IsPaying Type="boolean">True</IsPaying>
</user>

My Serialized user object looked like this:

<?xml version="1.0" encoding="utf-16"?>
<user>
  <FirstName>Soy</FirstName>
  <LastName >Nerdito</LastName>
  <IsPaying>True</IsPaying>
</user>

This looks simple enough to add the type to the node on my object so when serialized they are made identical. Duh! could not find a good solution for this. Most posts online were talking about doing a replace on the serialized string of each node to add the type. This requires me to add code to that section every time I create a new object property. Well I'm lazy and did not wanted to do that, so something else was needed.

The Solutions:
Manipulate the XML attributes that I had on my object to allow the serializer to do its job. Well, that was not an easy find either. So here is what I did.
.NET allows you to do this, add attributes to your nodes, but your objects must be constructed a certain way. So let the fun begin.

The Code:
First you need to create an object that will be the type for each one of your properties. Why? because natives will not cut it when the serializer gets a hold of them. So I decided to create a generic object that will accommodate anything I throw at it and it will take the type of the value all by itself (lazy, remember!).

Namespaces:
 You need these.
using System.Xml.Serialization;
using System.Xml;

Each of my nodes will be of this type:


    public class SingleNode<T>
    {
        public SingleNode(){}

        public SingleNode(T val)
        {
            value = val.ToString();
            //This is like so to remove the System. definition from the Type
            Type = val.GetType().ToString().Replace("System.""").ToLower();
        }

        [XmlText]
        public string value;

        [XmlAttribute("Type")]
        public string Type;
    }

 The XMLText and XMLAttribute define what the serializer will do when working with this. My constructor is generic so I should be able to pass any object type, I want to do ENUMS also but have not tried yet.

Now that you have the base object lets create User:


    [XmlRoot("user")]
    public class user
    {
        [XmlElement("FirstName")]
        public SingleNode FName = null;

        [XmlElement("LastName")]
        public SingleNode LName = null;

        [XmlElement("IsPaying")]
        public SingleNode IsPaying = null;
    }


The XMLRoot defines the name for the root node once is serialized.  The XMLElement attributes defines the node name after is serialized. Note that this is not the property name during design time and run time. You must reference the object in your code as user.FName, user.LName and so on. Each of the properties are defaulted to NULL but you can create the object instance there is you so pleased and assign a default value.

To create an instance of this is quite simple:


            user test = new user();
            test.FName = new SingleNode<string>("Soy");
            test.LName = new SingleNode<string>("Nerdito");
            test.IsPaying = new SingleNode<bool>(true);


You can see the instantiation assigns the object type because the constructor is generic.

The serializer:
If you do not have it, here is how to serialize it.
            XmlSerializerNamespaces xns = new XmlSerializerNamespaces();
            xns.Add(string.Emptystring.Empty); 
            
            //xml format settings
            XmlWriterSettings xmlSettings = new XmlWriterSettings();
            xmlSettings.Indent = true;
            xmlSettings.NewLineOnAttributes = false;
            xmlSettings.NamespaceHandling = NamespaceHandling.OmitDuplicates; 
            XmlSerializer mySerializer = new  XmlSerializer(typeof(user));
            StringWriter ms = new StringWriter();
            XmlWriter xw = XmlWriter.Create(msxmlSettings);
 
            mySerializer.Serialize(xwtestxns); 
            string serializedString = ms.ToString();
            ms.Close();

Conclusion:
This is it, simple enough to create the objects when you need to add a particular attribute. The attribute does not have to be the data type, you can change this to be anything you need it to be. .NET can add a DataType but if you service does not "play" with that, here you have an alternative. 

Would there be other ways of doing this, maybe but I could not find it. Please let me know if you have any improvements.

Why with all the new ways of creating web services people still write services that work with a post and do not have a WSDL or at least provide XSDs is beyond me, but hey not everyone can be a Nerdito.