Reputation: 41
I am trying to deserialize an XML document into a List<>. My goal is the have the List<> have a public static method that returns itself populated from the XML document.
Here is the XML:
<?xml version="1.0"?>
<root>
<item>
<name>Blue</name>
</item>
<item>
<name>Red</name>
</item>
</root>
Here is the class for the item (BillingItem.cs):
using System;
using System.Xml.Serialization;
namespace DeserializeExample
{
[Serializable]
[XmlRoot("item")]
public class BillingItem
{
[XmlElement("name")]
public string Description { get; set; }
}
}
Here is the class that is the list (BillingItems.cs):
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace DeserializeExample
{
[Serializable]
[XmlRoot("root")]
public class BillingItems : List<BillingItem>
{
public static BillingItems GetBillingItems()
{
using (TextReader reader = new StreamReader("BillingItems.xml"))
{
XmlSerializer serializer = new XmlSerializer(typeof(BillingItems));
return (BillingItems)serializer.Deserialize(reader);
}
}
}
}
And here is the console app with an example of how I would like to be able to get the list (Program.cs):
namespace DeserializeExample
{
class Program
{
static void Main(string[] args)
{
BillingItems billingItems = BillingItems.GetBillingItems();
}
}
}
When the console app runs, the list is empty. I think it's related to how I am using the attributes, but I am unable to determine how to make this work.
I hope the example is simple, explained well, and that someone can help.
Upvotes: 3
Views: 2402
Reputation: 41
Ok, while I don't like this because I believe there is something in the XmlSerializer that will do this for me, ug, but in the meantime I am going to do the following.
Adding an encapsulating class with the list I really want:
using System.Xml.Serialization;
namespace DeserializeExample
{
[XmlRoot("root")]
public class BillingItemResult
{
[XmlElement("item")]
public BillingItems Items { get; set; }
}
}
Then in my BillItems.cs class the private static void method will get the encapsulating object and return it's list of billing items:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace DeserializeExample
{
[Serializable]
[XmlRoot("root")]
public class BillingItems : List<BillingItem>
{
public static BillingItems GetBillingItems()
{
using (TextReader reader = new StreamReader("BillingItems.xml"))
{
XmlSerializer serializer = new XmlSerializer(typeof(BillingItemResult));
BillingItemResult billingItemResult = (BillingItemResult)serializer.Deserialize(reader);
return billingItemResult.Items;
}
}
}
}
This allows the design and convention of the existing application to remain intact. Here is a sample from the CLI:
using System.Collections.Generic;
namespace DeserializeExample
{
class Program
{
static void Main(string[] args)
{
BillingItems billingItems = BillingItems.GetBillingItems();
}
}
}
While this works, I still believe there is something more simple that doesn't require the additional wrapping class.
Upvotes: 0
Reputation: 911
From your example it looks like serialization may not be necessary. If it isn't, consider parsing using LINQ-to-XML:
using System.Xml.Linq;
public class BillingItems : List<BillingItem>
{
// Constructor.
private BillingItems(List<BillingItem> items) : base(items) { }
public static BillingItems GetBillingItems()
{
var doc = XDocument.Load("BillingItems.xml");
var items = (from i in doc.Element("root").Elements("item")
select new BillingItem { Description = i.Element("name").Value })
.ToList();
return new BillingItems(items);
}
}
You may also want to consider using an IEnumerable<> rather than a List<> unless you plan on inserting/removing items.
EDIT: Modified the above code so that BillingItems inherits a List<>, per the OP's comment below.
Upvotes: 1
Reputation: 1063298
I would encapsulate the list - in part for simplicity (avoiding some gnarly overloads the the XmlSerializer
constructor), and in part because there are some glitches here on CF etc that make it unreliable. But:
public class BillingItem
{
[XmlElement("name")]
public string Description { get; set; }
}
[XmlRoot("root")]
public class MyResult
{
[XmlElement("item")]
public List<BillingItem> Items { get; set; }
}
(I have removed all unnecessary other attributes)
You then just return a MyResult
(named more appropriately, obviously), and it will serialize as per your example.
Upvotes: 3