如何将可序列化对象保存/恢复到/自文件?

111

我有一个对象列表,需要将其保存在电脑上。我已经阅读了一些论坛,知道对象必须是可序列化的。但如果能提供一个示例就更好了。例如,如果我有以下内容:

[Serializable]
public class SomeClass
{
     public string someProperty { get; set; }
}

SomeClass object1 = new SomeClass { someProperty = "someString" };

但是我该怎样在电脑中存储object1并在以后检索?


3
这是一个教程,它展示了如何将对象序列化到文件中。链接:http://www.switchonthecode.com/tutorials/csharp-tutorial-serialize-objects-to-a-file - Brook
7个回答

183
我刚刚写了一篇关于将对象数据保存为二进制、XML或Json的博客文章。你说得对,如果你使用二进制序列化,你必须给你的类添加[Serializable]属性,但是如果你更喜欢使用XML或Json序列化,那就不需要添加该属性。以下是在各种格式下进行序列化的函数。更多细节请参阅我的博客文章。
更新:由于安全原因,BinaryFormatter已被弃用,不再建议使用。
二进制:
/// <summary>
/// Writes the given object instance to a binary file.
/// <para>Object type (and all child types) must be decorated with the [Serializable] attribute.</para>
/// <para>To prevent a variable from being serialized, decorate it with the [NonSerialized] attribute; cannot be applied to properties.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the binary file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the binary file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToBinaryFile<T>(string filePath, T objectToWrite, bool append = false)
{
    using (Stream stream = File.Open(filePath, append ? FileMode.Append : FileMode.Create))
    {
        var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        binaryFormatter.Serialize(stream, objectToWrite);
    }
}

/// <summary>
/// Reads an object instance from a binary file.
/// </summary>
/// <typeparam name="T">The type of object to read from the binary file.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the binary file.</returns>
public static T ReadFromBinaryFile<T>(string filePath)
{
    using (Stream stream = File.Open(filePath, FileMode.Open))
    {
        var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        return (T)binaryFormatter.Deserialize(stream);
    }
}

XML

需要在您的项目中包含System.Xml程序集。

/// <summary>
/// Writes the given object instance to an XML file.
/// <para>Only Public properties and variables will be written to the file. These can be any type though, even other classes.</para>
/// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [XmlIgnore] attribute.</para>
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToXmlFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()
{
    TextWriter writer = null;
    try
    {
        var serializer = new XmlSerializer(typeof(T));
        writer = new StreamWriter(filePath, append);
        serializer.Serialize(writer, objectToWrite);
    }
    finally
    {
        if (writer != null)
            writer.Close();
    }
}

/// <summary>
/// Reads an object instance from an XML file.
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object to read from the file.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the XML file.</returns>
public static T ReadFromXmlFile<T>(string filePath) where T : new()
{
    TextReader reader = null;
    try
    {
        var serializer = new XmlSerializer(typeof(T));
        reader = new StreamReader(filePath);
        return (T)serializer.Deserialize(reader);
    }
    finally
    {
        if (reader != null)
            reader.Close();
    }
}

Json

你必须包含对Newtonsoft.Json程序集的引用,该程序集可以从Json.NET NuGet Package获取。

/// <summary>
/// Writes the given object instance to a Json file.
/// <para>Object type must have a parameterless constructor.</para>
/// <para>Only Public properties and variables will be written to the file. These can be any type though, even other classes.</para>
/// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [JsonIgnore] attribute.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToJsonFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()
{
    TextWriter writer = null;
    try
    {
        var contentsToWriteToFile = JsonConvert.SerializeObject(objectToWrite);
        writer = new StreamWriter(filePath, append);
        writer.Write(contentsToWriteToFile);
    }
    finally
    {
        if (writer != null)
            writer.Close();
    }
}

/// <summary>
/// Reads an object instance from an Json file.
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object to read from the file.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the Json file.</returns>
public static T ReadFromJsonFile<T>(string filePath) where T : new()
{
    TextReader reader = null;
    try
    {
        reader = new StreamReader(filePath);
        var fileContents = reader.ReadToEnd();
        return JsonConvert.DeserializeObject<T>(fileContents);
    }
    finally
    {
        if (reader != null)
            reader.Close();
    }
}

示例

// Write the contents of the variable someClass to a file.
WriteToBinaryFile<SomeClass>("C:\someClass.txt", object1);

// Read the file contents back into a variable.
SomeClass object1= ReadFromBinaryFile<SomeClass>("C:\someClass.txt");

2
我喜欢你的二进制序列化代码。但是在WriteToBinaryFile中,为什么你会想要追加到文件中呢?在所有情况下,似乎你都想创建一个新文件。否则,在反序列化时会有一堆额外的信息。 - public wireless
2
@publicwireless 是的,你可能是对的。当时我没有多想,只是想让这3个函数的签名匹配:P - deadlydog
使用append方法,在同一文件中序列化多个对象,如何对它们进行反序列化?如何在流中寻找位置? - John Demetriou
1
请在二进制序列化器中添加注释,提醒用户生成的数据会盖有程序集的强名称,并且如果不添加重定向绑定或在不遵守该绑定的环境中运行(例如PowerShell),对版本的更改将导致失败。 - zaitsman
1
@JohnDemetriou 如果要将多个内容保存到文件中,我建议将这些对象包装在某种上下文对象中并对该对象进行序列化(让对象管理器解析出您想要的部分)。如果您尝试保存的数据量超过了内存容量,您可能需要切换到对象存储(对象数据库)而不是文件。 - Tezra
显示剩余8条评论

154
您可以使用以下内容:
    /// <summary>
    /// Serializes an object.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="serializableObject"></param>
    /// <param name="fileName"></param>
    public void SerializeObject<T>(T serializableObject, string fileName)
    {
        if (serializableObject == null) { return; }

        try
        {
            XmlDocument xmlDocument = new XmlDocument();
            XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
            using (MemoryStream stream = new MemoryStream())
            {
                serializer.Serialize(stream, serializableObject);
                stream.Position = 0;
                xmlDocument.Load(stream);
                xmlDocument.Save(fileName);
            }
        }
        catch (Exception ex)
        {
            //Log exception here
        }
    }


    /// <summary>
    /// Deserializes an xml file into an object list
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="fileName"></param>
    /// <returns></returns>
    public T DeSerializeObject<T>(string fileName)
    {
        if (string.IsNullOrEmpty(fileName)) { return default(T); }

        T objectOut = default(T);

        try
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(fileName);
            string xmlString = xmlDocument.OuterXml;

            using (StringReader read = new StringReader(xmlString))
            {
                Type outType = typeof(T);

                XmlSerializer serializer = new XmlSerializer(outType);
                using (XmlReader reader = new XmlTextReader(read))
                {
                    objectOut = (T)serializer.Deserialize(reader);
                }
            }
        }
        catch (Exception ex)
        {
            //Log exception here
        }

        return objectOut;
    }

1
不错!虽然DeSerializeObject中的string attributeXml = string.Empty;从未被使用 ;) - Jimbo
3
在 using 块中不需要调用 reader 的 Close 方法。即使在显式 Close() 之前块内发生异常,Dispose() 也是隐含的并将发生。这是非常有用的代码块。 - S. Brentson
2
如何使用此函数保存对象列表?我已经使用它,但它只保存了列表中的最后一个对象。 - Dev
1
该方法不会保存内部或私有字段,你可以使用这个:https://github.com/mrbm2007/ObjectSaver - mrbm

30
你需要将对象序列化成某种格式,比如二进制或XML(对于默认序列化程序),或者编写自定义序列化代码以将其序列化为其他文本形式。
一旦你选择了序列化方式,通常会调用一个流(Stream)来写入到某种文件中。
因此,如果我正在使用XML序列化并使用你的代码:
var path = @"C:\Test\myserializationtest.xml";
using(FileStream fs = new FileStream(path, FileMode.Create))
{
    XmlSerializer xSer = new XmlSerializer(typeof(SomeClass));

    xSer.Serialize(fs, serializableObject);
}

接下来,进行反序列化:

using(FileStream fs = new FileStream(path, FileMode.Open)) //double check that...
{
    XmlSerializer _xSer = new XmlSerializer(typeof(SomeClass));

    var myObject = _xSer.Deserialize(fs);
}

注意:这段代码还没有编译,更不用说运行了 - 可能会有一些错误。此外,这假定完全使用开箱即用的序列化/反序列化。如果您需要自定义行为,您需要进行额外的工作。


13

1. 从文件中恢复对象

在这里,你可以通过两种方式从文件反序列化对象。

解决方案一:将文件读入字符串并将JSON反序列化为类型

string json = File.ReadAllText(@"c:\myObj.json");
MyObject myObj = JsonConvert.DeserializeObject<MyObject>(json);

解决方案-2:直接从文件反序列化JSON

using (StreamReader file = File.OpenText(@"c:\myObj.json"))
{
    JsonSerializer serializer = new JsonSerializer();
    MyObject myObj2 = (MyObject)serializer.Deserialize(file, typeof(MyObject));
}

2. 将对象保存到文件

这里可以了解如何以两种方式将对象序列化到文件中。

方法一:将JSON序列化为字符串,然后将字符串写入文件

string json = JsonConvert.SerializeObject(myObj);
File.WriteAllText(@"c:\myObj.json", json);

解决方案2:直接将JSON序列化到文件中

using (StreamWriter file = File.CreateText(@"c:\myObj.json"))
{
    JsonSerializer serializer = new JsonSerializer();
    serializer.Serialize(file, myObj);
}

3. 额外内容

您可以通过以下命令从NuGet下载Newtonsoft.Json

Install-Package Newtonsoft.Json

5
您可以使用Newtonsoft库中的JsonConvert。将对象序列化并以json格式写入文件:
File.WriteAllText(filePath, JsonConvert.SerializeObject(obj));

将其反序列化为对象:

var obj = JsonConvert.DeserializeObject<ObjType>(File.ReadAllText(filePath));

1

1. 将json字符串转换为base64string并写入或追加到二进制文件中。 2. 从二进制文件中读取base64string并使用BsonReader进行反序列化。

 public static class BinaryJson
{
    public static string SerializeToBase64String(this object obj)
    {
        JsonSerializer jsonSerializer = new JsonSerializer();
        MemoryStream objBsonMemoryStream = new MemoryStream();
        using (BsonWriter bsonWriterObject = new BsonWriter(objBsonMemoryStream))
        {
            jsonSerializer.Serialize(bsonWriterObject, obj);
            return Convert.ToBase64String(objBsonMemoryStream.ToArray());
        }           
        //return Encoding.ASCII.GetString(objBsonMemoryStream.ToArray());
    }
    public static T DeserializeToObject<T>(this string base64String)
    {
        byte[] data = Convert.FromBase64String(base64String);
        MemoryStream ms = new MemoryStream(data);
        using (BsonReader reader = new BsonReader(ms))
        {
            JsonSerializer serializer = new JsonSerializer();
            return serializer.Deserialize<T>(reader);
        }
    }
}

0

不需要包含外部包例如 Newtonsoft.Json,你可以使用新的标准 System.Text.Json。 示例代码:

using System.Text.Json;

namespace SerializeBasic
{
    public class WeatherForecast
    {
        public DateTimeOffset Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }
    }

    public class Program
    {
        public static void Main()
        {
            var weatherForecast = new WeatherForecast
            {
                Date = DateTime.Parse("2019-08-01"),
                TemperatureCelsius = 25,
                Summary = "Hot"
            };

            string jsonString = JsonSerializer.Serialize(weatherForecast);

            Console.WriteLine(jsonString);
        }
    }
}
// output:
//{"Date":"2019-08-01T00:00:00-07:00","TemperatureCelsius":25,"Summary":"Hot"}

更多信息:.NET中如何序列化和反序列化JSON


使用weatherforecast = JsonSerializer.Deserialize(of Weatherforecast)(jsonString)将其反序列化。 - undefined

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