将XDocument用作XmlSerializer.Deserialize的源?

45

我希望调用XmlSerializer.Deserialize方法并传递一个XDocument对象作为参数。该方法可以接收一个StreamXmlReaderTextReader对象。

那么,我能否从XDocument生成其中一种对象而不实际将其转储到某个中介存储中(如MemoryStream)?

看起来我需要的是一个能够与XDocument一起使用的XmlReader实现,但我找不到这样的实现。


请参见https://dev59.com/AscIz4gBFxS5KdRj0leg。 - James Skemp
5个回答

55

您可以使用XDocument.CreateReader()来创建一个XmlReader,用于读取XDocument的内容。

同样地,以下方式也可行。

XmlReader GetReader(XDocument doc)
{
    return doc.Root.CreateReader();
}

20
这里有一个实用工具,可以将对象序列化和反序列化为XDocument格式。
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;
    }
}

5
< p >(Steve Guidi答案的附录)

据我所知,在不通过像XML内容的字符串表示那样的中间存储移动XML内容的情况下,没有可以轻松使用与XDocument 相关的XmlReader实现,并且支持例如System.Xml.XmlNodeReader 支持的所有类型。

XDocument.CreateReader 返回的读取器(它是一个 System.Xml.Linq.XNodeReader ,是一个内部类)是一个XmlReader ,适用于大多数Xml文档,但不适用于具有二进制数据元素的文档,因为它的实现不支持Base64或BinHex数据:

不支持Base64和BinHex数据。如果您尝试检索这些类型的数据(例如,通过调用ReadElementContentAsBase64),则读者将引发NotSupportedException异常。

对于该阅读器,XDocument.CreateReader().CanReadBinaryContentfalse,而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)
        }
    }
}

然而,将XML作为字符串提取并将其作为TextReader传递到序列化器中是可行的:
        using (var reader = new StringReader(document.ToString()))

如果有另外一种方法可以将包含二进制数据的XDocument反序列化而不需要先将其转换为字符串,我也会很感兴趣。


我本来希望找到这个问题的解决方案。我的一些 XML 内容中包含二进制数据,这会使得一切变得更加缓慢。将其转换成字符串然后再转回去似乎有些不可思议。我想我可能会尝试协商放弃 byte[] 内容而不是为此付出性能损失。 - Jim

0

我最喜欢 @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
    }
}

0

只是想提醒一下,在创建XmlReader之后,即:

XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
XmlReader reader = xmlDocumentToDeserialize.CreateReader();

那么你应该调用:

reader.MoveToContent();

因为否则读取器不会“指向”第一个节点,导致出现空的读取器!然后您可以安全地调用Deserialize:
MyObject myObject = (MyObject)serializer.Deserialize(reader);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接