【问题标题】:Serialize an object to XElement and Deserialize it in memory将对象序列化为 XElement 并在内存中反序列化
【发布时间】:2012-01-12 11:58:43
【问题描述】:

我想将一个对象序列化为 XML,但我不想将它保存在磁盘上。我想将它保存在 XElement 变量中(用于与 LINQ 一起使用),然后反序列化回我的对象​​。

我该怎么做?

【问题讨论】:

  • 请考虑更改接受的答案,因为我在 Abdul 的回复中添加了评论。不要让其他人使用可接受的解决方案,他们最好使用完美的解决方案(Surjit Samra 或 Eren Ersönmez)。
  • 接受的答案不是一个很好的答案 - 它浪费内存分配并假设为 Ascii 编码。 Eren 的回答简单、有效、高性能:stackoverflow.com/a/28872669/344638

标签: c# xml linq serialization c#-4.0


【解决方案1】:

您可以使用这两种扩展方法在 XElement 和您的对象之间进行序列化和反序列化。

public static XElement ToXElement<T>(this object obj)
{
    using (var memoryStream = new MemoryStream())
    {
        using (TextWriter streamWriter = new StreamWriter(memoryStream))
        {
            var xmlSerializer = new XmlSerializer(typeof(T));
            xmlSerializer.Serialize(streamWriter, obj);
            return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
        }
    }
}

public static T FromXElement<T>(this XElement xElement)
{
        var xmlSerializer = new XmlSerializer(typeof(T));
        return (T)xmlSerializer.Deserialize(xElement.CreateReader());
}

用法

XElement element = myClass.ToXElement<MyClass>();
var newMyClass = element.FromXElement<MyClass>();

【讨论】:

  • 创建扩展方法的好解决方案。
  • 你应该使用 public static XElement ToXElement(this T obj)
  • StreamWriter 不是默认为Encoding.UTF8不是 Encoding.ASCII?参考:msdn.microsoft.com/en-us/library/wtbhzte9.aspx.
  • 您应该使用Encoding.UTF8 而不是Encoding.ASCII。否则,如果你有一个像 "На берегу пустынных волн" 这样的字符串属性,这将中断。或者您可以使用 MemoryStream 或 Encoding 显式绕过(请参阅我的答案)。
  • 不知道为什么这个解决方案如此受欢迎。首先,编码是为解析指定的,但不是为写入指定的——这是一个错误,因为 Encoding.Default 可能不是 ASCII。其次,它比@Surjit Samra 的答案效率低 - 你转换对象> XmlDocument > string > byte [] > string > XDocument。他的解决方案改为执行 object > XmlDocument > XDocument 转换。第三,对 System.Object 进行扩展方法是一种非常糟糕的做法——它会在你的智能感知中到处添加这么多垃圾!
【解决方案2】:

您可以使用XMLSerialization

XML 序列化是转换对象的公共的过程 属性和字段为串行格式(在本例中为 XML) 储存或运输。 反序列化在其重新创建对象 XML 输出的原始状态。您可以将序列化视为 一种将对象状态保存到流或缓冲区中的方法。为了 例如,ASP.NET 使用 XmlSerializer 类来编码 XML Web 服务消息

XDocument Represents an XML document 来实现这一点

   using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;


namespace ConsoleApplication5
{
  public class Person
  {
    public int Age { get; set; }
    public string Name { get; set; }
  }

  class Program
  {
    static void Main(string[] args)
    {

      XmlSerializer xs = new XmlSerializer(typeof(Person));

      Person p = new Person();
      p.Age = 35;
      p.Name = "Arnold";

      Console.WriteLine("\n Before serializing...\n");
      Console.WriteLine(string.Format("Age = {0} Name = {1}", p.Age,p.Name));

      XDocument d = new XDocument();
      using (XmlWriter xw = d.CreateWriter())
        xs.Serialize(xw, p);

      // you can use LINQ on elm now

      XElement elm = d.Root;

      Console.WriteLine("\n From XElement...\n");

      elm.Elements().All(e => { Console.WriteLine(string.Format("element name {0} , element value {1}", e.Name, e.Value)); return true; });

      //deserialize back to object
      Person pDeserialized = xs.Deserialize((d.CreateReader())) as Person;

      Console.WriteLine("\n After deserializing...\n");
      Console.WriteLine(string.Format("Age = {0} Name = {1}", p.Age, p.Name));

      Console.ReadLine();

    }
  }


}

这里是输出

【讨论】:

  • 我认为您的意思是在反序列化后编写以下内容: Console.WriteLine(string.Format("Age = {0} Name = {1}", pDeserialized.Age, pDeserialized.Name)) ;
【解决方案3】:

(迟到的答案)

序列化:

var doc = new XDocument();
var xmlSerializer = new XmlSerializer(typeof(MyClass));
using (var writer = doc.CreateWriter())
{
    xmlSerializer.Serialize(writer, obj);
}
// now you can use `doc`(XDocument) or `doc.Root` (XElement)

反序列化:

MyClass obj; 
using(var reader = doc.CreateReader())
{
    obj = (MyClass)xmlSerializer.Deserialize(reader);
}

【讨论】:

    【解决方案4】:

    没有代码分析问题的 ToXelement,与 Abdul Muim 的答案相同,但修复了 CA 问题(CA1004 除外,在这种情况下无法解决,这是设计使然)

        public static XElement ToXElement<T>(this object value)
        {
            MemoryStream memoryStream = null;
            try
            {
                memoryStream = new MemoryStream();
                using (TextWriter streamWriter = new StreamWriter(memoryStream))
                {
                    memoryStream = null;
                    var xmlSerializer = new XmlSerializer(typeof(T));
                    xmlSerializer.Serialize(streamWriter, value);
                    return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
                }
            }
            finally
            {
                if (memoryStream != null)
                {
                    memoryStream.Dispose();
                }
            }
        }
    

    【讨论】:

    • 不使用using包裹内存流是什么原因?
    【解决方案5】:

    怎么样

    public static byte[] BinarySerialize(Object obj)
            {
                byte[] serializedObject;
                MemoryStream ms = new MemoryStream();
                BinaryFormatter b = new BinaryFormatter();
                try
                {
                    b.Serialize(ms, obj);
                    ms.Seek(0, 0);
                    serializedObject = ms.ToArray();
                    ms.Close();
                    return serializedObject;
                }
                catch
                {
                    throw new SerializationException("Failed to serialize. Reason: ");
                }
    
            }
    

    【讨论】:

    • catch(一个真正的异常)然后抛出一些新的垃圾异常,它无法传达原始异常的有用信息(“无法序列化。原因:”)是可怕的。只是不要捕捉,或者如果你想要一个假捕捉作为断点,那么只需执行“throw;”。隐藏真正的异常是糟糕的编码。
    • 捕获只是一个示例!!!,因为主要问题是关于序列化
    猜你喜欢
    • 2011-09-12
    • 1970-01-01
    • 1970-01-01
    • 2015-04-14
    • 1970-01-01
    • 2018-07-17
    • 2015-11-14
    • 2012-05-16
    • 1970-01-01
    相关资源
    最近更新 更多