将对象序列化为 XElement 并在内存中反序列化

Posted

技术标签:

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

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

我该怎么做?

【问题讨论】:

请考虑更改接受的答案,因为我在 Abdul 的回复中添加了评论。不要让其他人使用可接受的解决方案,他们最好使用完美的解决方案(Surjit Samra 或 Eren Ersönmez)。 接受的答案不是一个很好的答案 - 它浪费内存分配并假设为 Ascii 编码。 Eren 的回答简单、有效、高性能:***.com/a/28872669/344638 【参考方案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;”。隐藏真正的异常是糟糕的编码。 捕获只是一个示例!!!,因为主要问题是关于序列化

以上是关于将对象序列化为 XElement 并在内存中反序列化的主要内容,如果未能解决你的问题,请参考以下文章

将 XElement 反序列化为类

如何在 .NET 中反序列化为本地集合?

在 C# 中反序列化复杂对象

如何在 C# 中反序列化多个 JSON 对象?

使用 LitJson 在 C# 中反序列化 JSON 对象数组 [重复]

将 xml 反序列化为对象时出错:System.FormatException 输入字符串格式不正确