จะบันทึก / กู้คืนอ็อบเจกต์ต่อเนื่องเป็น / จากไฟล์ได้อย่างไร?


95

ฉันมีรายการวัตถุและฉันต้องการบันทึกไว้ที่ใดที่หนึ่งในคอมพิวเตอร์ของฉัน Serializableฉันได้อ่านฟอรั่มบางอย่างและฉันรู้ว่าวัตถุที่จะต้องมี แต่คงจะดีถ้าได้ตัวอย่าง ตัวอย่างเช่นหากฉันมีสิ่งต่อไปนี้:

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

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

แต่ฉันจะเก็บไว้object1ที่ไหนสักแห่งในคอมพิวเตอร์ของฉันและเรียกดูในภายหลังได้อย่างไร


3
นี่คือบทช่วยสอนที่แสดงวิธีการทำให้เป็นอนุกรมกับไฟล์switchonthecode.com/tutorials/…
Brook

คำตอบ:


144

คุณสามารถใช้สิ่งต่อไปนี้:

    /// <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
ดี! แม้ว่าstring attributeXml = string.Empty;ในDeSerializeObjectจะไม่ใช้;)
โบ้

3
ไม่จำเป็นต้องเรียกใช้วิธีปิดในเครื่องอ่านภายในบล็อกโดยใช้ของคุณ Dispose () เป็นผลโดยนัยและจะเกิดขึ้นแม้ว่าจะมีการยกข้อยกเว้นภายในบล็อกก่อนปิดอย่างชัดเจน () บล็อกโค้ดที่มีประโยชน์มาก
S. Brentson

2
วิธีบันทึกรายการวัตถุโดยใช้ฟังก์ชั่นนี้ฉันเคยใช้ แต่กำลังบันทึกเฉพาะวัตถุสุดท้ายในรายการของฉัน
Decoder94

1
วิธีนี้จะไม่บันทึกฟิลด์ภายในหรือฟิลด์ส่วนตัวคุณสามารถใช้สิ่งนี้: github.com/mrbm2007/ObjectSaver
mrbm

152

ฉันเพิ่งเขียนบล็อกโพสต์เกี่ยวกับการบันทึกข้อมูลของวัตถุเป็น Binary, XML หรือ JsonJSON คุณถูกต้องที่คุณต้องตกแต่งชั้นเรียนของคุณด้วยแอตทริบิวต์ [Serializable] แต่ถ้าคุณใช้การทำให้เป็นอนุกรมไบนารีเท่านั้น คุณอาจต้องการใช้ XML หรือ Json serialization นี่คือฟังก์ชั่นที่ต้องทำในรูปแบบต่างๆ ดูโพสต์บล็อกของฉันสำหรับรายละเอียดเพิ่มเติม

ไบนารี่

/// <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 ชุมนุมซึ่งสามารถได้รับจากNuGet แพคเกจ

/// <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 ทำไมคุณถึงต้องการต่อท้ายไฟล์? ดูเหมือนว่าคุณต้องการสร้างไฟล์ใหม่ในทุกกรณี มิฉะนั้นจะมีข้อมูลเพิ่มเติมมากมายเกี่ยวกับ deserialization
ระบบไร้สายสาธารณะ

1
@publicwireless ใช่คุณคงพูดถูก ตอนนั้นฉันไม่ได้คิดถึงเรื่องนี้มากนัก ฉันแค่อยากให้ลายเซ็นของฟังก์ชั่นทั้ง 3 ตรงกัน: P
Deadlydog

โดยใช้วิธีการต่อท้ายการทำให้เป็นอนุกรมของวัตถุหลายชิ้นในไฟล์เดียวกันฉันจะยกเลิกการกำหนดค่าเริ่มต้นได้อย่างไร ฉันจะค้นหาในสตรีมได้อย่างไร
John Demetriou

1
โปรดเพิ่มความคิดเห็นในไบนารีซีเรียลไลเซอร์ที่จะแจ้งให้ผู้คนทราบว่าข้อมูลที่เป็นผลลัพธ์ถูกประทับตราด้วยชื่อที่รัดกุมของแอสเซมบลีและเปลี่ยนเป็นเวอร์ชันนี้โดยไม่ต้องเพิ่มการเชื่อมโยงการเปลี่ยนเส้นทางหรือทำงานในสภาพแวดล้อมที่ไม่เคารพการผูกดังกล่าว (เช่น powershell) จะ fail
zaitsman

1
@JohnDemetriou หากบันทึกหลาย ๆ สิ่งลงในไฟล์ฉันขอแนะนำให้ตัดอ็อบเจกต์ในรูปแบบบริบทของอ็อบเจ็กต์บางรูปแบบและทำให้อ็อบเจ็กต์นั้นเป็นอนุกรม (ให้ตัวจัดการอ็อบเจ็กต์แยกวิเคราะห์ส่วนที่คุณต้องการ) หากคุณพยายามบันทึกข้อมูลมากกว่าที่จะเก็บไว้ในหน่วยความจำได้คุณอาจต้องการเปลี่ยนไปใช้ที่เก็บอ็อบเจ็กต์ (ฐานข้อมูลอ็อบเจ็กต์) แทนไฟล์
Tezra

30

คุณจะต้องทำให้เป็นอนุกรมกับบางสิ่งนั่นคือเลือกไบนารีหรือ xml (สำหรับตัวอนุกรมเริ่มต้น) หรือเขียนรหัสซีเรียลไลเซชันที่กำหนดเองเพื่อทำให้เป็นอนุกรมกับรูปแบบข้อความอื่น

เมื่อคุณเลือกแล้วการทำให้เป็นอนุกรมของคุณ (โดยปกติ) จะเรียกสตรีมที่กำลังเขียนไปยังไฟล์บางประเภท

ดังนั้นด้วยรหัสของคุณถ้าฉันใช้ XML Serialization:

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);
}

หมายเหตุ: รหัสนี้ยังไม่ได้รวบรวมนับประสารัน - อาจมีข้อผิดพลาดบางอย่าง นอกจากนี้ยังถือว่าการทำให้เป็นอนุกรม / deserialization แบบทันทีทันใด หากคุณต้องการพฤติกรรมที่กำหนดเองคุณจะต้องทำงานเพิ่มเติม


10

1. กู้คืนวัตถุจากไฟล์

จากที่นี่ คุณสามารถยกเลิกการกำหนดค่าเริ่มต้นของวัตถุจากไฟล์ได้สองวิธี

โซลูชันที่ 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. บันทึกวัตถุลงในไฟล์

จากที่นี่คุณสามารถทำให้วัตถุเป็นไฟล์อนุกรมได้สองวิธี

โซลูชันที่ 1: ซีเรียลไลซ์ 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. พิเศษ

คุณสามารถดาวน์โหลด Newtonsoft.Json จากNuGet ได้โดยทำตามคำสั่ง

Install-Package Newtonsoft.Json

1

** 1. แปลงสตริง json เป็น base64string และเขียนหรือต่อท้ายเป็นไฟล์ไบนารี 2. อ่าน base64string จากไฟล์ไบนารีและ deserialize โดยใช้ 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);
        }
    }
}

1

คุณสามารถใช้ JsonConvert จากไลบรารี Newtonsoft ในการทำให้วัตถุเป็นอนุกรมและเขียนลงไฟล์ในรูปแบบ json:

File.WriteAllText(filePath, JsonConvert.SerializeObject(obj));

และหากต้องการยกเลิกการกำหนดค่ากลับเป็นวัตถุ:

var obj = JsonConvert.DeserializeObject<ObjType>(File.ReadAllText(filePath));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.