我希望调用XmlSerializer.Deserialize
方法并传递一个XDocument
对象作为参数。该方法可以接收一个Stream
、XmlReader
或TextReader
对象。
那么,我能否从XDocument
生成其中一种对象而不实际将其转储到某个中介存储中(如MemoryStream
)?
看起来我需要的是一个能够与XDocument
一起使用的XmlReader
实现,但我找不到这样的实现。
我希望调用XmlSerializer.Deserialize
方法并传递一个XDocument
对象作为参数。该方法可以接收一个Stream
、XmlReader
或TextReader
对象。
那么,我能否从XDocument
生成其中一种对象而不实际将其转储到某个中介存储中(如MemoryStream
)?
看起来我需要的是一个能够与XDocument
一起使用的XmlReader
实现,但我找不到这样的实现。
您可以使用XDocument.CreateReader()
来创建一个XmlReader
,用于读取XDocument
的内容。
同样地,以下方式也可行。
XmlReader GetReader(XDocument doc)
{
return doc.Root.CreateReader();
}
XDocument doc = SerializationUtil.Serialize(foo);
Foo foo = SerializationUtil.Deserialize<Foo>(doc);
这是一个类:
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;
}
}
据我所知,在不通过像XML内容的字符串表示那样的中间存储移动XML内容的情况下,没有可以轻松使用与XDocument
相关的XmlReader
实现,并且支持例如System.Xml.XmlNodeReader
支持的所有类型。
XDocument.CreateReader
返回的读取器(它是一个 System.Xml.Linq.XNodeReader
,是一个内部类)是一个XmlReader
,适用于大多数Xml文档,但不适用于具有二进制数据元素的文档,因为它的实现不支持Base64或BinHex数据:
不支持Base64和BinHex数据。如果您尝试检索这些类型的数据(例如,通过调用ReadElementContentAsBase64),则读者将引发NotSupportedException异常。
对于该阅读器,XDocument.CreateReader().CanReadBinaryContent
为false
,而System.Xml.XmlNodeReader
则相反。
例如,此程序会抛出异常:
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)
}
}
}
using (var reader = new StringReader(document.ToString()))
如果有另外一种方法可以将包含二进制数据的XDocument
反序列化而不需要先将其转换为字符串,我也会很感兴趣。
我最喜欢 @Simon_Weaver 的回答。基于他的回答,这是我的总结:
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
}
}
只是想提醒一下,在创建XmlReader之后,即:
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
XmlReader reader = xmlDocumentToDeserialize.CreateReader();
那么你应该调用:
reader.MoveToContent();
MyObject myObject = (MyObject)serializer.Deserialize(reader);