Converting XML To C# Object From FTP

Reading a File over FTP is a common scenario. Best case scenario, the file is JSON and you can very easily convert it to a C# object using the serializer. What happens when it is XML? Converting it to a model requires a little more work. Here is how to read an XML file from an FTP folder and convert it to a C# object.

Let’s use this piece of XML as an example. This data is stored inside a file on the target FTP server. We want to download this data and access the data from a model we have created.

<Products>
	<Product>
		<id>123</id>
		<name>Test reading xml from ftp file using c#</name>
	</Product>
</Products>

This model is a representation of the XML above. We do not need to directly mimic the structure as we can define the root node as part of the serializer. Keep in mind that XML is case sensitive. The properties are lower case here to match the XML. I will go into detail below on a way to get around this if you would like upper case object properties.

public class Product
{
	public int id;
	public string name;
}

The process of fetching the file from the FTP server is fairly standard. The code below will prepare the request.

string ftpFilePath = "ftp://ftp.website.com/products.xml";
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpFilePath);
request.Method = WebRequestMethods.Ftp.DownloadFile;

request.Credentials = new NetworkCredential("username", "pass");

Converting an FTP Stream to a C# Model

This part is not as hard as it seems but it is prone to failure. You will need to make sure the XML you are reading matches the C#. Any change in letter case will cause it to fail.

Since the XML is a list of product nodes wrapped inside a “Products” node, we can pass the root attribute into the serializer to let it know that this is the list element and we can decode from there.

List<Product> productList = null;
XmlSerializer serializer = new XmlSerializer(typeof(List<Product>), new XmlRootAttribute("Products"));
try
{
	using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
	{
		Stream responseStream = response.GetResponseStream();
		using (StreamReader reader = new StreamReader(responseStream))
		{
			productList = (List<Product>)serializer.Deserialize(reader);
		}
	}
}
catch(Exception e)
{
	//handle failure if something goes wrong while reading of deserializing the data. 
}

Converting an XML string to a C# Object

Since we are already on the topic of reading XML, what if you already have the XML string and do not need to download if from anywhere? The code is more or less the same. We still deserialize the data using a stream reader. If you have a piece of XML that is stored in a string object, convert it to a stream and follow the same process.

The code below will show you exactly how to convert the same piece of XML to a C# object. For this example, assume the data lives in a string called “xmlString”.

using (StringReader stringReader = new StringReader(xmlString))
{
	productList = (List<Product>)serializer.Deserialize(stringReader);
}

Deserialize XML With Case Insensitive

Sadly, XML is case sensitive and this must be enforced. There is a handy workaround to this though. The document from the Microsoft website here explains how to do this. If we work it into this example, lets say the XML is all lower case.

The XML below will not deserialize to a list of C# objects. It shouldn’t throw an error but the resulting list will contain 0 elements. Converting the class to use lower case is one option but this looks ugly and can cause issues if you have naming convention standards.

<products>
	<product>
		<id>123</id>
		<name>Case sensitivity test</name>
	</product>
</products>

Decorating the class with attributes will allow you to instruct the serializer to map an XML node with a specific name to a class or property on a model you have defined. To implement it in this example, simply add the attribute to the class to instruct it to use the lower case node instead of the class name.

[XmlType(TypeName = "product")]
public class Product
{
	[XmlElement("id")]
	public int Id;
	[XmlElement("name")]
	public string Name;
}

This is a fairly simple example overall and should give you a good idea on how to get started. More complex XML objects can really be a pain to parse. It is unfortunate but it is what it is. A nasty XML format will make you appreciate how great JSON has been for data transfer and translation.

Deserializing Double Nested XML To C# Object

One of the most frustrating things I have found with XML formats are when they are double nested for no observable reason. An array lives within a higher-level node with no additional children within this parent. Let’s use the following piece of XML as an example. For some reason, there is an additional wrapper node around the array of products.

<wrapper>
	<products>
		<product>
			<id>123</id>
			<name>Test reading xml from ftp file using c#</name>
		</product>
	</products>
</wrapper>

What was once a fairly simple process has now become a lot more complicated. The deserialization code will no longer work correctly because there is a new root node. To deal with this, you will need to come up with a proper C# hierarchy of classes that directly match the XML. The following C# classes could be used to deserialize the XML above.

[XmlRoot(ElementName = "product")]
    public class Product
    {
        [XmlElement(ElementName = "id")]
        public string Id { get; set; }
        [XmlElement(ElementName = "name")]
        public string Name { get; set; }
    }

    [XmlRoot(ElementName = "products")]
    public class Products
    {
        [XmlElement(ElementName = "product")]
        public List<Product> Product { get; set; }
    }

    [XmlRoot(ElementName = "wrapper")]
    public class Wrapper
    {
        [XmlElement(ElementName = "products")]
        public Products Products { get; set; }
    }

To generate the C# models faster and easier, there is a fantastic and free online tool called Xml2Csharp that will do all the heavy lifting for you. Provide the XML and it will generate the classes. Just make sure the example has multiple items for it to detect that you are working with an XML array and not an object.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.