Convert XML data to object and back using serializationSaving XML to a class object gives greater flexibility and maintainability. Moreover, it’s the preferred way of returning data from web services as compared to using DataSets. Saving XML to a class object is called serialization and reading back using the reverse process is named as deserialization.
Process\sample
Let's take an example of an XSD named Emp.XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="Emp">
<xs:complexType>
<xs:sequence>
<xs:element name="EmpInfo" type="EmpInfo"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="EmpInfo">
<xs:sequence>
<xs:element name="Code" type="requiredString"/>
<xs:element name="FirstName" type="requiredString"/>
<xs:element name="LastName" type="requiredString"/>
<xs:element name="Destination" type="requiredString"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="requiredString">
<xs:annotation>
<xs:documentation>template for required strings</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="optionalString">
<xs:annotation>
<xs:documentation>template for optional strings</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:minLength value="0"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
Now, to convert a class from XSD, we would run the following command:xsd C:\Emp.xsd /t:lib /l:cs /c
from the Visual Studio .NET 2003 command prompt. This would create a class file named Emp.
EMP Class//------------------------------------------------------------------------------
//
// This code was generated by a tool.
// Runtime Version: 1.1.4322.2032
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// //------------------------------------------------------------------------------//
// This source code was auto-generated by xsd, Version=1.1.4322.2032.
//
using System.Xml.Serialization;///
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public class Emp {
///
public EmpInfo EmpInfo;
}
///
public class EmpInfo {
///
public string Code;
///
public string FirstName;
///
public string LastName;
///
public string Destination;
}
Now, we could save an employee XML data to this class using the function: ///
/// Returns an emp xsd class object based on the xml data passed
///
/// xml data of employee
///
public Emp EmpObject(string xml)
{
StringReader stream = null;
XmlTextReader reader = null;
try
{
// serialise to object
XmlSerializer serializer = new XmlSerializer(typeof(Emp));
stream = new StringReader(xml); // read xml data
reader = new XmlTextReader(stream); // create reader
// covert reader to object
return (Emp)serializer.Deserialize(reader);
}
catch
{
return null;
}
finally
{
if(stream != null) stream.Close();
if(reader != null) reader.Close();
}
}
And to get the XML based on the Emp class object, use the following function: ///
/// Returns the xml as string based on emp object values
///
/// object that would be converted into xml
///
public string EmpXml(Emp emp)
{
MemoryStream stream = null;
TextWriter writer = null;
try
{
stream = new MemoryStream(); // read xml in memory
writer = new StreamWriter(stream, Encoding.Unicode) ;
// get serialise object
XmlSerializer serializer = new XmlSerializer(typeof(Emp));
serializer.Serialize(writer, emp); // read object
int count = (int) stream.Length; // saves object in memory stream
byte[] arr = new byte[count];
stream.Seek(0, SeekOrigin.Begin);
// copy stream contents in byte array
stream.Read(arr, 0, count);
UnicodeEncoding utf = new UnicodeEncoding(); // convert byte array to string
return utf.GetString(arr).Trim();
}
catch
{
return string.Empty;
}
finally
{
if(stream != null) stream.Close();
if(writer != null) writer.Close();
}
}
Remoting ProblemApplications that used to work well in Framework 1.0 give System.Runtime.Serialization.SerializationException when run under Framework 1.1. The exception has this format:
System.Runtime.Serialization.SerializationException: Because of security restrictions, the type (type name) cannot be accessed. ---> System.Security.SecurityException: Request failed.
This issue is documened at http://www.gotdotnet.com/team/changeinfo/Backwards1.0to1.1/default.aspx#00000153 (Secure Serialization in .NET Remoting - Backwards Breaking Changes from version 1.0 to 1.1)
A level of security has been added to Serialization. Specifically, there are now two levels of serialization security: Low (Default) and Full.
Low Level Security:
• Remoting infrastructure objects. These are the types needed to make remoting work at a basic level. • Primitive types, and reference and value types that are composed of primitive types. • Reference and value types that are marked with the SerializableAttribute attribute but do not implement the ISerializable interface. • System-provided types that implement ISerializable with a reduced permission set. • Custom types that implement ISerializable. • Types that implement the ILease interface. • ObjRef objects used for activation (to support client-activated objects).
Full Level Security:
• ObjRef objects passed as parameters. • Objects that implement the ISponsor interface. • Objects that are inserted between the proxy and client pipeline by the IContributeEnvoySink interface.
In version 1.1 of the .Net Framework, a new security feature was added to the .Net Remoting infrastructure called Type Filtering. This feature, which is enabled by default, limits the type of objects that may be marshaled by a Remoting client to a Remoting server. Type Filtering effectively prohibits the server from deserializing instances of CLR types that may serve as vectors of attack. This feature is part of Microsoft’s commitment to be secure by default.
The following outlines several of the threats that are mitigated by use of this feature (we are not going to give out details about how to exploit these threats), specifically when typeFilterLevel is set to Low:
Exposing a Remoting server with a method or member that takes a delegate. Threat: Delegates may be used to invoke methods on other types that have the same signature
Exposing a Remoting server with a method or member that takes a MarshalByRefObject type. Threat: The serialized form of a MarshalByRefObject contains a mutable URL
Exposing a Remoting server with a method or member that takes an ISerializable type. Threat: Deserialization of an ISerializable type causes the Remoting infrastructure to run the type’s code.
Exposing a Remoting server with a method or member that takes an type found in the GAC on the server machine in an assembly without APTCAThreat: Types in the GAC are well known to anyone with the .Net Framework installed.
Exposing a Remoting server that enables remote clients to register sponsors Threat: There is no limit on the number of sponsors that can be registered.
You can set this functionality programmatically or using the config file:
Programmatic Solution:
IDictionary props = new Hashtable(); props["typeFilterLevel"] = "Full"; BinaryServerFormatterSinkProvider formatterProvider = new BinaryServerFormatterSinkProvider(props, null);
Here's the VB.NET code (just in case!)
If you are in VS 2003:
Dim h_Table As IDictionaryh_Table = New Hashtable()h_Table("port") = 52000Dim obj_FormatProvider As New BinaryServerFormatterSinkProvider()obj_FormatProvider.TypeFilterLevel = Runtime.Serialization.Formatters.TypeFilterLevel.FullDim chnl As New TcpChannel(h_Table, Nothing, obj_FormatProvider)ChannelServices.RegisterChannel(chnl)
If you are in VS 2002:
Dim h_Table As IDictionary
h_Table = New Hashtable()h_Table("port") = 52000Dim h_Table2 As IDictionaryh_Table2 = New Hashtable()h_Table2("typeFilterLevel") = "Full"Dim obj_FormatProvider2 As New BinaryServerFormatterSinkProvider(h_Table2, Nothing)Dim chnl2 As New TcpChannel(h_Table, Nothing, obj_FormatProvider2)ChannelServices.RegisterChannel(chnl2)
Config File Solution:
To use a configuration file to set the serialization level, you must explicitly specify the typeFilterLevel attribute of the
element. Although this is typically done on the server side, you must also specify this attribute to control the serialization level for any channel on the client registered to listen for a callback. The following example sets the serialization level to Full for both the SoapFormatter and BinaryFormatter in this application domain.
See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconautomaticdeserializationinnetremoting.asp for more info.
Your feedback is welcome.