C# Xml File Put It Back Into the Object
This article uses practical examples in order to demonstrate how to deserialize simple as well as complex (nested) XML into C# objects. The article presented two generic functions which allows XML to be deserialized into a C# Object, and converts a C# object into XML. In order to demonstrate how to use these functions, two examples were presented on how to work with simple XML data and complex hierarchical data. The article also mentioned the limitation of not being able to deserialize interafce types and nullable types, and also provided examples on how to overcome these drawbacks of the XmlSerializer.
- Download PopulateObjectFromXML.zip - 12.3 MB
Introduction
This article uses practical examples in order to demonstrate how to deserialize simple as well as complex (nested) XML into C# objects. For completeness, the article also demonstrates how to do the reverese i.e to convert a simple or complex C# object into XML.
Background
In some cases it can be much easier to work with a business object instance or a list of business object instances in C# than it is to traverse an XMLDocument object containing business information. The source of the data may be from a 3rd party web service, a file exported from a legacy system, or even a SQL Server table column holding XML information. This article shows a quick and simple way of converting the XML to objects and back to XML again.
Using the code
The sample code which accompanies this article can be downloaded here.
The Serializer.cs
class contains two methods, namely Deserialize
and Serialize
. The Deserialize method receives a string containing the xml to deserialize and returns an object of type T
. Conversely, the Serialize method receives an object of type T
and converts this into xml.
In both cases we are working with an object of type T
which allows the client code to pass in objects of different types, making the two routines generic for serialization.
using System; using System.Collections; using System.IO; using System.Xml.Serialization; namespace TestHarness { public class Serializer { public T Deserialize<T>(string input) where T : class { System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T)); using (StringReader sr = new StringReader(input)) { return (T)ser.Deserialize(sr); } } public string Serialize<T>(T ObjectToSerialize) { XmlSerializer xmlSerializer = new XmlSerializer(ObjectToSerialize.GetType()); using (StringWriter textWriter = new StringWriter()) { xmlSerializer.Serialize(textWriter, ObjectToSerialize); return textWriter.ToString(); } } } }
Example 1
In this example we will convert a simple xml file holding customer details into a C# object instance.
The xml data looks something like:
< Customer > < CustomerID >ALFKI< /CustomerID > < CompanyName >Alfreds Futterkiste< /CompanyName > < ContactName >Maria Anders< /ContactName > < ContactTitle >Sales Representative< /ContactTitle > < Address >Obere Str. 57< /Address > < City >Berlin< /City > < PostalCode >12209< /PostalCode > < Country >Germany< /Country > < Phone >030-0074321< /Phone > < Fax >030-0076545< /Fax > < /Customer >
The following code snippet reads xml data from a file and creates a customer object by calling the Deserialize
method, and passing it a customer type.
Serializer ser = new Serializer(); string path = string.Empty; string xmlInputData = string.Empty; string xmlOutputData = string.Empty; path = Directory.GetCurrentDirectory() + @" \Customer.xml"; xmlInputData = File.ReadAllText(path); Customer customer = ser.Deserialize<Customer>(xmlInputData); xmlOutputData = ser.Serialize<Customer>(customer)
The Serialize method converts a customer object to XML.
Example 2
The above diagram shows a Customer object which has one or more Order object instances. Each Order instance can have one or more Order Detail instances, and a given Order Detail instance can have one or more Product instances.
The abbreviated XML structure which is derived from the above object model is shown below. Please refer to the sample code for the full XML.
< Customer > < CustomerID >ALFKI< /CustomerID > < CompanyName >Alfreds Futterkiste< /CompanyName > < ContactName >Maria Anders< /ContactName > < ContactTitle >Sales Representative< /ContactTitle > < Address >Obere Str. 57< /Address > < City >Berlin< /City > < PostalCode >12209< /PostalCode > < Country >Germany< /Country > < Phone >030-0074321< /Phone > < Fax >030-0076545< /Fax > < Orders > < Order > < OrderID >10643< /OrderID > < CustomerID >ALFKI< /CustomerID > < EmployeeID >6< /EmployeeID > < OrderDate >1997-08-25T00:00:00< /OrderDate > < RequiredDate >1997-09-22T00:00:00< /RequiredDate > < ShippedDate >1997-09-02T00:00:00< /ShippedDate > < ShipVia >1< /ShipVia > < Freight >29.4600< /Freight > < ShipName >Alfreds Futterkiste< /ShipName > < ShipAddress >Obere Str. 57< /ShipAddress > < ShipCity >Berlin< /ShipCity > < ShipPostalCode >12209< /ShipPostalCode > < ShipCountry >Germany< /ShipCountry > < Order_Details > < Order_Detail > < OrderID >10643< /OrderID > < ProductID >28< /ProductID > < UnitPrice >45.6000< /UnitPrice > < Quantity >15< /Quantity > < Discount >2.5000000e-001< /Discount > < Product > < ProductID >28< /ProductID > < ProductName >Rössle Sauerkraut< /ProductName > < SupplierID >12< /SupplierID > < CategoryID >7< /CategoryID > < QuantityPerUnit >25 - 825 g cans< /QuantityPerUnit > < UnitPrice >45.6000< /UnitPrice > < UnitsInStock >26< /UnitsInStock > < UnitsOnOrder >0< /UnitsOnOrder > < ReorderLevel >0< /ReorderLevel > < Discontinued >1< /Discontinued > < /Product > < /Order_Detail > < Order_Detail > < OrderID >10643< /OrderID > < ProductID >39< /ProductID > < UnitPrice >18.0000< /UnitPrice > < Quantity >21< /Quantity > < Discount >2.5000000e-001< /Discount > < Product > < ProductID >39< /ProductID > < ProductName >Chartreuse verte< /ProductName > < SupplierID >18< /SupplierID > < CategoryID >1< /CategoryID > < QuantityPerUnit >750 cc per bottle< /QuantityPerUnit > < UnitPrice >18.0000< /UnitPrice > < UnitsInStock >69< /UnitsInStock > < UnitsOnOrder >0< /UnitsOnOrder > < ReorderLevel >5< /ReorderLevel > < Discontinued >0< /Discontinued > < /Product > < /Order_Detail > < /Order_Details > < /Order > < /Orders > < /Customer >
The code below is similar to the snippet above, and deserializes nested Xml into a complex C# object.
Serializer ser = new Serializer(); string path = string.Empty; string xmlInputData = string.Empty; string xmlOutputData = string.Empty; path = Directory.GetCurrentDirectory() + @" \CustOrders.xml"; xmlInputData = File.ReadAllText(path); Customer customer2 = ser.Deserialize<Customer>(xmlInputData); xmlOutputData = ser.Serialize<Customer>(customer2);
The screenshot below shows the contents of the customer2 object, and the data which has been populated through deserialization. The customer, order, order details, and product have been applied to the complex object instance.
Points of Interest
Interface Types
1. The XmlSerializer
is unable to serialize interface types such as ICollection. In the Customer.cs
file if the following line is changed from :
public List<Order> Orders { get; set; }
to
public ICollection<Order> Orders { get; set; }
the following error is displayed :
Cannot serialize member TestHarness.Customer.Orders of type System.Collections.Generic.ICollection`1[[TestHarness.Order, TestHarness, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.
In order to resolve this error the ICollection
return type would need to be changed to a List type :
public List<Order> Orders { get; set; }
or an array :
public Order[] Orders { get; set; }
Nullable Types
2. When a property is declared as a nullable type such as an integer, and the value from the input xml is a blank, the nullable type is not recognised, and an attempt is made to save the null value to an int, resulting in the following error:
Input string was not in a correct format.
In order to reproduce this error using the sample code, it is necessary to change the following line in the Order.cs file :
public int ShipVia { get; set; }
to a nullable int :
public int? ShipVia { get; set; }
In the CustOrders.xml
file please change the following on line 20 from :
< shipvia >1< /shipvia >
to a blank value as shown here...
< shipvia > < /shipvia >
Running the application would reproduce this error :
Input string was not in a correct format.
In order to get around the problem of deserializing nullable types, the following code can be used in the Order.cs
file.
[ XmlIgnore] public int? ShipVia { get; set; } [XmlElement(" ShipVia")] public string ShipVia_Proxy { get { return ShipVia.HasValue ? ShipVia.ToString() : string.Empty; } set { if ( !string.IsNullOrEmpty(value)) { ShipVia = Int32.Parse(value); } } }
The ShipVia
property is prefixed with the [XmlIgnore]
to exclude the property from serialization. Instead, the ShipVia_Proxy
string property is be used as an alternative to store a value whether it is a null or a none null.
The ShipVia_Proxy
is prefixed with [XmlElement("ShipVia")]
attribute in order to indicate to the XmlSerializer that the ShipVia
element is to be mapped to ShipVia_Proxy
instead of the ShipVia
nullable property. The overall effect is that if the ShipVia
XML element is empty, a null is applied to theShipVia
object property.
This is by no means the best approach for deserializing nullable types, however the solution is simple and quick to implement.
Conclusion
The article presented two generic functions which allows XML to be deserialized into a C# Object, and converts a C# object into XML. In order to demonstrate how to use these functions, two examples were presented on how to work with simple xml data and complex hierarchical data. The article also mentioned the limitation of not being able to deserialize interafce types and nullable types, and also provided examples on how to overcome these drawbacks of the XmlSerializer.
History
- v 1.0 - 18:12GMT 2017-01-02
C# Xml File Put It Back Into the Object
Source: https://www.codeproject.com/Articles/1163664/Convert-XML-to-Csharp-Object
0 Response to "C# Xml File Put It Back Into the Object"
Post a Comment