开发者

Using the .NET serializer to serialize XML to a .NET class

I have an XML file:

<?xml version="1.0" encoding="开发者_JS百科UTF-8"?>
<MyProducts>
    <Product Name="P1" />
    <Product Name="P2" />
</MyProducts>

And a C# object:

Public Class Product
{
    [XmlAttribute]
    Public Name {get; set;}
}

Using the .NET Serializer class now can I Deserialize the XML file into an IList without creating a MyProducts object?

So I want to somehow select only the Product elements before I serialize


There's a quick-and-dirty way to accomplish what you want - simply replace "MyProducts" with something the BCL classes are happy with - ArrayOfProduct:

string xml = @"<?xml version='1.0' encoding='UTF-8;'?> 
  <MyProducts> 
      <Product Name='P1' /> 
      <Product Name='P2' /> 
  </MyProducts>";
//secret sauce:
xml = xml.Replace("MyProducts>", "ArrayOfProduct>");
IList myProducts;
using (StringReader sr = new StringReader(xml))
{
    XmlSerializer xs = new XmlSerializer(typeof(List<Product>));
    myProducts = xs.Deserialize(sr) as IList;
}
foreach (Product myProduct in myProducts)
{
    Console.Write(myProduct.Name);
}

Of course, the right way would be to transform the XML document to replace the MyProducts nodes appropriately - instead of using a string replace - but this illustrates the point.


If you don't want to create a collection class for your products, you can mix some LINQ to XML with the XmlSerializer:

public static IEnumerable<T> DeserializeMany<T>(
    string fileName, string descendentNodeName = null) {
  descendentNodeName = descendentNodeName ?? typeof(T).Name;
  var serializer = new XmlSerializer(typeof(T));
  return
    from item in XDocument.Load(fileName).Descendants(descendentNodeName)
    select (T)serializer.Deserialize(item.CreateReader());
}

Then, to get your list:

var products = XmlSerializerUtils.DeserializeMany<Product>(fileName).ToList();


I'm not sure you're going to have much success using the XML serializer to accomplish what you need. It may be simpler for you to manually parse out the XML and map them explicitly, e.g.

        XDocument xml = XDocument.Parse(@"<?xml version=""1.0"" encoding=""UTF-8""?>
                                            <MyProducts>
                                                <Product Name=""P1"" /> 
                                                <Product Name=""P2"" /> 
                                            </MyProducts>");

        foreach(var product in xml.Descendants(XName.Get("Product")))
        {
            var p = new Product { Name = product.Attribute(XName.Get("Name")).Value };
           // Manipulate your result and add to your collection.
        }

        ...

        public class Product
        {
           public string Name { get; set; }
        }

If you're using a file, which you most likely are for your XML, just replace the Parse method on the XDocument w/ Load and the appropriate signature.


I don't think you can instruct the serializer to spit out a IList. The serializer can create a MyProduct collection object and fill it with Products. Which sounds like what you want to do.

You can also use LINQ to query the XML document and create a list of IEnumerable as well.

// load from stream if that is the case 
// this just uses a file for demonstration purposes
XDocument doc = XDocument.Load("location_of_source.xml");

// select all Product nodes from the root node and create a new Product object using
// object initialization syntax
var listOfProduct = doc.Descendants("Product")
                       .Select(p => new Product { Name = p.Attribute("Name").Value});


While it's novel to not create the class, doing so saves you a lot of code... You don't even have to use it except when you deserialize.

//Products declaration
[XmlRoot(ElementName = "MyProducts")]
public class MyProducts : List<Product>
{
}

public class Product
{
    [XmlAttribute]
    public string Name { get; set; }
}

...

[Test]
public void DeserializeFromString()
{
    var xml = @"<?xml version='1.0' encoding='UTF-8;'?>  
      <MyProducts>  
        <Product Name='P1' />  
        <Product Name='P2' />  
      </MyProducts>";

    IList<Product> obj = Serialization<MyProducts>.DeserializeFromString(xml);

    Assert.IsNotNull(obj);
    Assert.AreEqual(2, obj.Count);
}

...

//Deserialization library
public static T DeserializeFromString(string serialized)
{
    var byteArray = Encoding.ASCII.GetBytes(serialized);
    var memStream = new MemoryStream(byteArray);
    return Deserialize(memStream);
}

public static T Deserialize(Stream stream)
{
    var xs = new XmlSerializer(typeof(T));
    return (T) xs.Deserialize(stream);
}


The problem is that IList is not serializable so you'd have to implement your custom XmlSerializer - walk through xml nodes etc, you can however deserialize your collection into List using out of the box XmlSerializer.

Dont forget to add default constructor to your Product Class.

        XmlSerializer serializer = new XmlSerializer(typeof(List<Product>));
        FileStream stream = new FileStream(fileName, FileMode.Open); 
        var product = serializer.Deserialize(sream) as List<Product>;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜