<

Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x

Serialization in .NET - 3

Published on
16,816 Points
6,316 Views
5 Endorsements
Last Modified:
Awarded
In my previous two articles we discussed Binary Serialization and XML Serialization. In this article we will try to know more about SOAP (Simple Object Access Protocol) serialization.

What is SOAP serialization?

If you have gone through my first article on serialization you will have an idea on what serialization is all about. Serialization is basically conversion of an object into a format which then can be easily transmitted or saved in any format. In SOAP serialization the object is converted to SOAP format (SOAP is based on XML standards). The SOAP XML must conform to the World Wide Web Consortium standards. In SOAP serialization all the member variables (even private members) are serialized to SOAP format. SOAP was developed to bridge the communication gap between the various applications running on different platforms. SOAP can be used in scenarios where you want to transfer your objects across different applications running on different platform/same platform. As long as the objects are serialized to SOAP standards they can be deserialized in any applicaiton running on the same platform or a different platform. Without further delay, let's see some code in action.


First up let's see how to serialize an object to SOAP format. We will take our good old Car class which most of you are very familiar with. The Car class is pasted below.

public class Car 
{        
    public string Color 
    { get; set; } 
    public string Model 
    { get; set; } 
    public int NoOfDoors 
    { get; set; } 
    public int Price 
    { get; set; } 
    public string CubicCentimeter 
    { get; set; }        
    public CarType Type 
    { get; set; } 
}

Open in new window


We will make use of the above car class for SOAP serialization. The code to serialize a class to SOAP format is similar to XML serialization with a small difference. The code is pasted below.
Car bmw3Series = new Car{Model = "BMW 3 Series", Color = "Blue", NoOfDoors = 4,  CubicCentimeter="3500cc"}; 
using (System.IO.Stream soapStream = new System.IO.FileStream("SoapFormat.xml", System.IO.FileMode.OpenOrCreate)) 
{ 
    System.Xml.Serialization.SoapReflectionImporter soapRefImp = new System.Xml.Serialization.SoapReflectionImporter(); 
    System.Xml.Serialization.XmlTypeMapping xmlTypeMapping = 
        soapRefImp.ImportTypeMapping(typeof(Car)); 
    System.Xml.Serialization.XmlSerializer xmlSerializer = 
        new System.Xml.Serialization.XmlSerializer(xmlTypeMapping); 
    xmlSerializer.Serialize(soapStream, bmw3Series); 
}

Open in new window


To serialize an object into SOAP format one can make use of XmlSerilizer class. When we initialize the XmlSerializer class for SOAP serialization instead of passing the type of the object we pass an instance of XmlTypeMapping class. This is the subtle difference. As you can see from the above code we are instantiating XmlTypeMapping object by passing the type of the Car object to the ImportTypeMapping method of SoapReflectionImporter class. SoapReflectionImporter class can be used whenever you want to serialize objects to SOAP formats.

The SOAP XML generated from the above code for the Car class is pasted below.

<?xml version="1.0" ?> 
<Car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="id1"> 
  <Color xsi:type="xsd:string">Blue</Color> 
  <Model xsi:type="xsd:string">BMW 3 Series</Model> 
  <NoOfDoors xsi:type="xsd:int">4</NoOfDoors> 
  <Price xsi:type="xsd:int">0</Price> 
  <CubicCentimeter xsi:type="xsd:string">3500cc</CubicCentimeter> 
</Car>

Open in new window


Another way to serialize objects is by making use of SoapFormatter class. To make use of the SoapFormatter class one has to add a reference to the “System.Runtime.Serialization.Formatters.Soap.dll” assembly. The sample code is pasted below.

using (System.IO.Stream soapStream = new System.IO.FileStream("SoapFormatter.xml", System.IO.FileMode.Create)) 
{ 
    System.Runtime.Serialization.Formatters.Soap.SoapFormatter soapFormatter = 
        new System.Runtime.Serialization.Formatters.Soap.SoapFormatter(); 
    soapFormatter.Serialize(soapStream, bmw3Series); 
}

Open in new window


The SOAP xml generated from the above code is pasted below.

<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema- 
instance" xmlns:xsd="http://www.w3.org/2001/ 
XMLSchema" Xmlns:SOAP-ENC="http://schemas. 
xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV= 
"http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap 
/encoding/clr/1.0" SOAP-ENV:encodingStyle= 
"http://schemas.xmlsoap.org/soap/encoding/"> 
<SOAP-ENV:Body> 
<a1:Car id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/ 
nsassem/SoapSerialization/SerializationSample% 
2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral% 
2C%20PublicKeyToken%3Dnull"> 
<_x003C_Color_x003E_k__BackingField id="ref-3">Blue</_x003C_Color_x003E_k__ 
BackingField> 
<_x003C_Model_x003E_k__BackingField id="ref-4">BMW 3 Series</_x003C_Model_x003E_k__BackingField> 
<_x003C_NoOfDoors_x003E_k__BackingField>4 
</_x003C_NoOfDoors_x003E_k__BackingField> 
<_x003C_Price_x003E_k__BackingField>0 
</_x003C_Price_x003E_k__BackingField> 
<_x003C_CubicCentimeter_x003E_k__BackingField id="ref-5">3500cc</_x003C_CubicCentimeter_ 
x003E_k__BackingField> 
</a1:Car> 
</SOAP-ENV:Body> 
</SOAP-ENV:Envelope>

Open in new window


I would not recommend using SoapFormatter class as this class is obsolete from .NET 3.5 version. Also SoapFormatter class doesn’t support serialization of generic collections.

That's pretty much how objects can be serialized to SOAP format. As in the case with XML serialization there may be situation where you would like to control the way objects are serialized to SOAP format. This can be very much useful when you are using webservices where you would like to take control of the way your objects are serialized. The following are some of the attributes which can be applied to control object serialization.

SoapAttribute

One can apply “SoapAttribute” to convert the property or field as a XML attribute rather than as a XML node which is the normal behavior. If you want to change the attribute name, and don’t want it to be same as that of the property name, you can make use of the “AttributeName” property along with “SoapAttribute”. To provide a namespace to the xml attribute you can make use of the “Namespace” property. When you provide namespace it has to abide by w3 guidelines. As a general practice, we provide urls as namespace. If you want to provide the data type to the attribute again, that can be specified with the help DataType property. Sample code is pasted below.

public class Car 
{        
   [System.Xml.Serialization.SoapAttribute (AttributeName="CarColor", DataType="string", Namespace="http://car")] 
    public string Color 
    { get; set; } 
    public string Model 
    { get; set; } 
    public int NoOfDoors 
    { get; set; } 
    public int Price 
    { get; set; } 
    public string CubicCentimeter 
    { get; set; }           
}

Open in new window


The serialized XML after applying the “SoapAttribute” is pasted below.

<?xml version="1.0"?> 
<Car xmlns:xsi="http://www.w3.org/2001/XMLSchema-inst 
ance"xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="id1"d1p1:CarColor="Blue" xmlns:d1p1="http://car"> 
  <Model xsi:type="xsd:string">BMW 3 Series</Model> 
  <NoOfDoors xsi:type="xsd:int">4</NoOfDoors> 
  <Price xsi:type="xsd:int">0</Price> 
  <CubicCentimeter xsi:type="xsd:string"> 
3500cc</CubicCentimeter> 
</Car>

Open in new window


From the above pasted XML you can see how the Color property has been added as a SOAP attribute to the Car node.

SoapElement

SoapElement can be used to control the xml element’ attributes. Using the SoapElement attribute on a public property/field will convert the property/field to a XML element. The properties that can be used with SoapElement are ElementName, DataType,  and IsNullable.

With ElementName one can provide a Name to the element, if you don’t want the SOAP XML element name to be same as that of the property/field name.

DataType will help you to specify the SOAP XML schema data type.

IsNullable specifies whether a SOAP XML element node should be generated if the property/field is null. If the IsNullable value is true then an empty XML element with xsi:nil attribute set to true will be generated for properties/fields having null value, else no XML element node will be generated. As we have learned in XML serialization, if IsNullable is used in SOAP serialization it won’t throw any error even if it is used along with value type objects. In SOAP serialization if you have set IsNullable to true then for value type it will create a node with a default value assigned to it. For example, if you have used IsNullable for int data type and boolean data type then the SOAP xml will have nodes with 0 and false as their default value, if they are not initialized. Sample code with these implementation and the SOAP XML is pasted below.

public class Car 
{ 
    [System.Xml.Serialization.SoapElement 
(ElementName = "CarColor", IsNullable = true)] 
        public string Color 
        { get; set; } 
        [System.Xml.Serialization.SoapElement (ElementName="CarModel")] 
        public string Model 
        { get; set; } 
        public int NoOfDoors 
        { get; set; } 
        [System.Xml.Serialization.SoapElement (ElementName = "CarPrice", IsNullable = true)] 
        public int Price 
        { get; set; }        
        public string CubicCentimeter 
        { get; set; }           
}

//Car object to be serialized. 
Car bmw3Series = new Car{Model = "BMW 3 Series", NoOfDoors = 4, CubicCentimeter="3500cc"};

//Serialized SOAP XML 
<?xml version="1.0"?> 
<Car xmlns:xsi="http://www.w3.org/2001/XMLSchema 
- instance" xmlns:xsd="http://www.w3.org/2001/ 
XMLSchema" id="id1"> 
  <CarColor xsi:nil="true" /> 
  <CarModel xsi:type="xsd:string">BMW 3 Series</CarModel> 
  <NoOfDoors xsi:type="xsd:int">4</NoOfDoors> 
  <CarPrice xsi:type="xsd:int">0</CarPrice> 
  <CubicCentimeter xsi:type="xsd:string">3500cc</CubicCentimeter> 
</Car>

Open in new window


From the above xml one can see there is “xsi:nill” XML attribute set to true for “CarColor” node as we have applied “IsNullable” true. Also notice “CarPrice” node having “0” as its value. Since we have used “IsNullable,” it is generating a node with a default value.

SoapEnum

Can be used to describe how Enum needs to be serialized. It has only one property named Name, so that you can specify what should be the name of the particular Enum value. Sample code is pasted below.

public enum CarType 
{ 
    [System.Xml.Serialization.SoapEnum(Name = "CompactCar")] 
    SmallCar, 
   [System.Xml.Serialization.SoapEnum(Name= "CompactSedan")] 
    CS, 
    Sedan, 
    SportsCar, 
    Suv 
}

Open in new window


If the “Name” property is specified then the enum’ member will have the name specified in the “Name” property else it will default to the member name.

SoapIgnore

If you want a public field or property not to be serialized then you can make use SoapIgnore attribute. This will prevent a particular public field or property from being serialized. SoapIgnore doesn’t have any property. The serialized XML and SoapIgnore attribute applied to Color property of the Car class is pasted below.

public class Car 
{ 
    [System.Xml.Serialization.SoapIgnore] 
    public string Color 
    { get; set; } 
    [System.Xml.Serialization.SoapElement (ElementName="CarModel")] 
    public string Model 
    { get; set; } 
    public int NoOfDoors 
    { get; set; } 
    [System.Xml.Serialization.SoapElement(ElementName = "CarPrice", IsNullable = true)] 
    public int Price 
    { get; set; }        
    public string CubicCentimeter 
    { get; set; }           
}

//Car object which needs to be serialized 
Car bmw3Series = new Car{Model = "BMW 3 Series", 
NoOfDoors = 4, Color="Blue", CubicCentimeter="3500cc"};

//Serialized XML with SoapIgnore attribute applied. 
<?xml version="1.0"?> 
<Car xmlns:xsi="http://www.w3.org/2001/ 
XMLSchema-instance" xmlns:xsd="http://www.w3.org 
/2001/XMLSchema" id="id1"> 
  <CarModel xsi:type="xsd:string">BMW 3 Series</CarModel> 
  <NoOfDoors xsi:type="xsd:int">4</NoOfDoors> 
  <CarPrice xsi:type="xsd:int">0</CarPrice> 
  <CubicCentimeter xsi:type="xsd:string">3500cc</CubicCentimeter> 
</Car>500cc</CubicCentimeter> 
</Car>

Open in new window


From the above pasted SOAP XML one can note that Color attribute is missing because of the usage of SoapIgnore.

SoapInclude

SoapInclude can be used to specify the derived classes to be included in the SOAP format. For example, you have Car class and other classes like “CompactCar”, “Sedan” etc are deriving from Car class then you can make use SoapInclude. Suppose you have a class called CarCollection, where you have a property called CarInstance which is a generic collection of base type Car. When the CarCollection object is serialized one has to tell the compiler what classes the collection can hold or derives from. If one is not specifying the inherited classes using the SoapInclude attribute, the serialization process throws the following error.

InvalidOperationException outer exception: "There was an error generating the XML document."
InvalidOperationException inner exception: "The type SoapSerialization.CompactCar was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."

To avoid the above error you can use SoapInclude attribute. A sample code is pasted below.

//Derived classes. 
public class CompactCar : Car 
{ 
    public string SomeProperty 
        { get; set; } 
}

public class Sedan : Car 
{ 
    public int SedanCapacity 
        { get; set; } 
} 
\*Class has a generic collection of type car. One has to use SoapInclude to tell the serializer that the class can have classes of type CompactCar and Sedan.*/ 
[System.Xml.Serialization.SoapInclude(typeof(CompactCar)), 
        System.Xml.Serialization.SoapInclude(typeof(Sedan))]    
public class CarCollection 
{          
        public System.Collections.Generic.List<Car> CarInstance 
        { get; set; } 
}

Open in new window


Sometimes using the SoapInclude attribute may not solve the problem rather it can throw a new error. The error with inner exception is pasted below.

System.InvalidOperationException: "There was an error generating the XML document."
System.InvalidOperationException: "Token StartElement in state Epilog would result in an invalid XML document."

To solve this you need to add a start element and end element before and after the serialized SOAP xml using the XmlTextWriter’ WriteStartElement and WriteEndElement methods, respectively. The logic behind this is to add a root node. The SOAP serialization code is modified and pasted below.

using (System.IO.Stream soapStream = new System.IO.FileStream("SoapFormat.xml", 
    System.IO.FileMode.Create)) 
{ 
    using (System.Xml.XmlTextWriter xmlWriter = new System.Xml.XmlTextWriter(soapStream, System.Text.Encoding.UTF8)) 
    { 
        System.Xml.Serialization.SoapReflectionImporter soapRefImp = 
            new System.Xml.Serialization.SoapReflectionImporter(); 
        System.Xml.Serialization.XmlTypeMapping xmlTypeMapping = 
            soapRefImp.ImportTypeMapping(typeof(CarCollection)); 
        System.Xml.Serialization.XmlSerializer xmlSerializer = 
            new System.Xml.Serialization.XmlSerializer(xmlTypeMapping); 
        xmlWriter.WriteStartElement("carRoot"); 
        xmlSerializer.Serialize(xmlWriter, carCol);                     
        xmlWriter.WriteEndElement(); 
    }                
}

Open in new window


Deserializing a SOAP XML

Deserializing an SOAP XML is pretty simple and similar to the serialization logic. You need to create an instance of “SoapReflectionImporter” class and use the “ImportTypeMapping” method of “SoapReflectionImporter” to create an instance of “XmlTypeMapping” class. Once these things are done, create an instance of “XmlSerializer” class by passing the “XmlTypeMapping” object as one of the parameters. After this call, use the Deserialize method of the “XmlSerializer” class. The sample code is pasted below.

System.Xml.Serialization.SoapReflectionImporter soapReflImp = 
                        new System.Xml.Serialization.SoapReflectionImporter(); 
System.Xml.Serialization.XmlTypeMapping xmlTypeMap = 
        soapReflImp.ImportTypeMapping(typeof(CarCollection)); 
System.Xml.Serialization.XmlSerializer xmlSerial = 
        new System.Xml.Serialization.XmlSerializer(xmlTypeMap); 
using (System.Xml.XmlTextReader xmlTextReader = new System.Xml.XmlTextReader("SoapFormat.xml")) 
{ 
    /*Needed to read the root element. In the serialization code we have added the "carRoot" element.*/ 
    xmlTextReader.ReadStartElement("carRoot"); 
    CarCollection carColl = (CarCollection)xmlSerial.Deserialize(xmlTextReader); 
    xmlTextReader.ReadEndElement(); 
}

Open in new window


One can see from the above code that we are using “ReadStartElement” method of “XmlTextReader” class. This is done because we have manually added the “carRoot” element by making use of “WriteStartElement” method of “XmlTextWriter” while serializing the object. If “ReadStartElement” method is not used then the following error will be thrown.

System.InvalidOperationException: "There is an error in XML document (1, 2)."
Inner exception - System.InvalidOperationException: "<carRoot xmlns=''> was not expected."

So that’s about SOAP serialization. Next we will see how to write custom serializers so that we can take control of serialization, till then try to learn more.

The original article with comments and discussion can be found here.
5
Comment
Author:Sandeep P R
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
1 Comment
 
LVL 18

Expert Comment

by:WaterStreet
Voted Yes, above, for this article being helpful for me in understanding about serialization
0

Featured Post

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Join & Write a Comment

This is my first video review of Microsoft Bookings, I will be doing a part two with a bit more information, but wanted to get this out to you folks.
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month