Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / XML

Convert XML to C# Object

Rate me:
Please Sign up or sign in to vote.
4.61/5 (36 votes)
4 Jan 2017CPOL4 min read 329.3K   12.7K   46   5
Deserialize nested XML to complex C# objects or convert a complex C# object to hierarchical XML.
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.

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.

C#
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.

simple object diagram

The xml data looks something like:

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>
</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;

// EXAMPLE 1
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.

Watch window on simple object data

Example 2

Complex object diagram

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.

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;

// EXAMPLE 2
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.

Watch window on the complex object data

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 :

XML
<shipvia>1</shipvia>

to a blank value as shown here...

XML
<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 ShipViaproperty 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 the ShipVia 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
Kuv Patel is a Senior Software Developer and Solutions Architect, specializing in .NET and Javascript frameworks development using MVC, ASP.NET, C#, WCF, Web API, Entity Framework, React, Angular.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Member 47447522-Oct-19 4:13
Member 47447522-Oct-19 4:13 
QuestionGreat Artical Pin
can hoang quy14-Jan-18 17:31
can hoang quy14-Jan-18 17:31 
QuestionList<T> not populated Pin
FBCode9-Nov-17 6:04
FBCode9-Nov-17 6:04 
SuggestionExtendedXmlSerializer Pin
Wojciech Nagórski5-Jan-17 0:39
Wojciech Nagórski5-Jan-17 0:39 
GeneralRe: ExtendedXmlSerializer Pin
Kuv Patel13-Jan-17 10:42
Kuv Patel13-Jan-17 10:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.