How to deserialize an XML document into a List<> while overriding the item names
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&开发者_开发问答gt;
</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.
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.
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.
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.
精彩评论