Reputation: 61502
I would like to invoke XmlSerializer.Deserialize
passing it an XDocument
. It can take a Stream
, an XmlReader
or a TextReader
.
Can I generate one of the above from XDocument
without actually dumping the XDocument
into some intermediate store, such as a MemoryStream
?
It seems that what I'm after is an implementation of XmlReader
that works with an XDocument
. I can't find one though.
Upvotes: 45
Views: 34724
Reputation: 1691
I like @Simon_Weaver 's answer the best. Based on that this is my summary:
using System;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace XDocSerialization
{
[TestClass]
public class Tests
{
[TestMethod]
public void Tests_SerializeToXDoc()
{
var sheep = new Animal
{
Name = "Sheep", Legs = 4, Nutrition = Nutrition.Herbivore
};
var xdoc = sheep.SerializeToXDoc();
var ser = "<Animal " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n " +
"<Name>Sheep</Name>\r\n <Legs>4</Legs>\r\n " +
"<Nutrition>Herbivore</Nutrition>\r\n</Animal>";
Assert.AreEqual(xdoc.ToString(), ser);
Assert.IsInstanceOfType(xdoc, typeof(XDocument));
}
[TestMethod]
public void Tests_DeserializeFromXDoc()
{
var Sheep = new Animal
{
Name = "Sheep", Legs = 4, Nutrition = Nutrition.Herbivore
};
var des = Sheep.SerializeToXDoc().DeserializeFromXDoc<Animal>();
Assert.AreEqual(des.Name, Sheep.Name);
Assert.AreEqual(des.Nutrition, Sheep.Nutrition);
Assert.AreEqual(des.Legs, Sheep.Legs);
Assert.AreNotSame(des, Sheep);
}
}
public static class ExtensionMethods
{
public static T DeserializeFromXDoc<T>(this XDocument source)
{
if (source == null || source.Root == null)
return default(T);
using (var reader = source.Root.CreateReader())
return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
public static XDocument SerializeToXDoc<T>(this T source)
{
if (source == null)
return null;
var doc = new XDocument();
using (var writer = doc.CreateWriter())
new XmlSerializer(typeof(T)).Serialize(writer, source);
return doc;
}
}
[Serializable]
public class Animal
{
public string Name { get; set; }
public int Legs { get; set; }
public Nutrition Nutrition { get; set; }
}
public enum Nutrition
{
Herbivore,
Carnivore,
Omnivore
}
}
Upvotes: 0
Reputation: 177163
(Appendix to Steve Guidi's answer)
As far as I can see there is no implementation of XmlReader
you can easily use with XDocument
without moving the XML content through an intermediate store like a string representation of the XML and that supports all types that for example the System.Xml.XmlNodeReader
supports.
The reader returned by XDocument.CreateReader
(which is a System.Xml.Linq.XNodeReader
, an internal class) is a XmlReader
and works for most Xml document but not with documents that have binary data elements because its implementation does not support Base64 or BinHex data:
Base64 and BinHex data are not supported. If you attempt to retrieve these types of data (for example, by calling ReadElementContentAsBase64), the reader will throw NotSupportedException.
For this reader XDocument.CreateReader().CanReadBinaryContent
is false
in contrast to the System.Xml.XmlNodeReader
.
For example this program throws an exception:
public class SomeTest
{
public byte[] BinaryTest { get; set; }
}
class Program
{
static void Main(string[] args)
{
XDocument document = new XDocument(
new XElement("SomeTest",
new XElement("BinaryTest", "VGVzdA==")));
using (var reader = document.CreateReader())
{
var serializer = new XmlSerializer(typeof(SomeTest));
var someTest = serializer.Deserialize(reader) as SomeTest;
// NotSupportedException here (as inner exception)
}
}
}
However, extracting the XML as string
and passing it as TextReader
into the serializer works:
using (var reader = new StringReader(document.ToString()))
I would be interested as well if there is another way to deserialize an XDocument
that includes binary data without converting it into a string first.
Upvotes: 5
Reputation: 146110
Here's a utility to serialize and deserialize objects to/from XDocument.
XDocument doc = SerializationUtil.Serialize(foo);
Foo foo = SerializationUtil.Deserialize<Foo>(doc);
Here's the class:
public static class SerializationUtil
{
public static T Deserialize<T>(XDocument doc)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (var reader = doc.Root.CreateReader())
{
return (T)xmlSerializer.Deserialize(reader);
}
}
public static XDocument Serialize<T>(T value)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
XDocument doc = new XDocument();
using (var writer = doc.CreateWriter())
{
xmlSerializer.Serialize(writer, value);
}
return doc;
}
}
Upvotes: 20
Reputation: 5313
Just thought I should add that after the XmlReader is created, i.e.:
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
XmlReader reader = xmlDocumentToDeserialize.CreateReader();
then you should call:
reader.MoveToContent();
because otherwise the reader will not "point" to the first node, causing the appearance of an empty reader! Then you can safely call Deserialize:
MyObject myObject = (MyObject)serializer.Deserialize(reader);
Upvotes: 0
Reputation: 20200
You can use XDocument.CreateReader()
to create an XmlReader
that reads the contents of the XDocument
.
Equivalently, the following will work too.
XmlReader GetReader(XDocument doc)
{
return doc.Root.CreateReader();
}
Upvotes: 57