Come salvare / ripristinare un oggetto serializzabile su / da file?


94

Ho un elenco di oggetti e devo salvarlo da qualche parte nel mio computer. Ho letto alcuni forum e so che l'oggetto deve essere Serializable. Ma sarebbe bello se potessi avere un esempio. Ad esempio, se ho quanto segue:

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

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

Ma come posso archiviare object1da qualche parte nel mio computer e successivamente recuperarlo?


3
Ecco un tutorial che mostra come serializzare in un file switchonthecode.com/tutorials/…
Brook

Risposte:


141

Puoi usare quanto segue:

    /// <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
Bello! Anche se string attributeXml = string.Empty;in DeSerializeObjectnon viene mai utilizzata;)
Jimbo

3
Non è necessario chiamare il metodo di chiusura su un lettore all'interno del blocco using. Dispose () è implicito e avrà luogo anche se viene sollevata un'eccezione all'interno del blocco prima dell'esplicito Close (). Blocco di codice molto utile.
S. Brentson

2
Come salvare un elenco di oggetti utilizzando questa funzione L'ho usato ma salva solo l'ultimo oggetto nella mia lista
Decoder94

1
Questo metodo non salverà i campi interni o privati, puoi usare questo: github.com/mrbm2007/ObjectSaver
mrbm

151

Ho appena scritto un post sul blog sul salvataggio dei dati di un oggetto in Binary, XML o Json . Hai ragione sul fatto che devi decorare le tue classi con l'attributo [Serializable], ma solo se stai usando la serializzazione binaria. Potresti preferire utilizzare la serializzazione XML o Json. Ecco le funzioni per farlo nei vari formati. Vedi il mio post sul blog per maggiori dettagli.

Binario

/// <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

Richiede che l'assembly System.Xml sia incluso nel progetto.

/// <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

È necessario includere un riferimento all'assembly Newtonsoft.Json, che può essere ottenuto dal pacchetto NuGet Json.NET .

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

Esempio

// 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
Mi piace il tuo codice di serializzazione binario. Ma su WriteToBinaryFile perché dovresti mai voler aggiungere al file? Sembra che tu voglia creare un nuovo file in tutti i casi. Altrimenti ci sarebbero un sacco di informazioni extra sulla deserializzazione.
wireless pubblico

1
@publicwireless Sì, probabilmente hai ragione. Non ci ho pensato molto in quel momento; Volevo solo che le firme delle 3 funzioni corrispondessero: P
deadlydog

utilizzando il metodo append, serializzando molti oggetti nello stesso file, come deserializzarli? come cerco nel flusso?
John Demetriou

1
Si prega di aggiungere il commento al serializzatore binario che avviserà le persone che i dati risultanti sono contrassegnati con il nome sicuro dell'assembly e le modifiche alle versioni di questo senza aggiungere binding di reindirizzamento o eseguire in ambienti che non rispettano detti binding (ad esempio powershell) fallire
zaitsman

1
@JohnDemetriou Se si salvano più cose in un file, consiglierei di avvolgere gli oggetti in una qualche forma di oggetto di contesto e serializzare quell'oggetto (lascia che il gestore degli oggetti analizzi le parti che desideri). Se stai cercando di salvare più dati di quanti ne puoi tenere in memoria, potresti voler passare a un archivio oggetti (database degli oggetti) invece che a un file.
Tezra

30

Avrai bisogno di serializzare qualcosa: cioè, scegliere binario o xml (per serializzatori predefiniti) o scrivere codice di serializzazione personalizzato per serializzare in qualche altro formato di testo.

Dopo averlo selezionato, la serializzazione chiamerà (normalmente) un flusso che sta scrivendo su un qualche tipo di file.

Quindi, con il tuo codice, se stessi usando la serializzazione 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);
}

Quindi, per deserializzare:

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

    var myObject = _xSer.Deserialize(fs);
}

NOTA: questo codice non è stato compilato, figuriamoci eseguito, potrebbero esserci degli errori. Inoltre, ciò presuppone una serializzazione / deserializzazione completamente out-of-the-box. Se hai bisogno di un comportamento personalizzato, dovrai fare del lavoro aggiuntivo.


10

1. Ripristina oggetto da file

Da qui puoi deserializzare un oggetto dal file in due modi.

Soluzione-1: leggere il file in una stringa e deserializzare JSON in un tipo

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

Soluzione-2: deserializza JSON direttamente da un file

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

2. Salva oggetto su file

da qui puoi serializzare un oggetto su file in due modi.

Soluzione-1: serializzare JSON in una stringa e quindi scrivere la stringa in un file

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

Soluzione-2: serializzare JSON direttamente in un file

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

3. Extra

Puoi scaricare Newtonsoft.Json da NuGet seguendo il comando

Install-Package Newtonsoft.Json

1

** 1. Converti la stringa json in base64string e scrivi o accodala a un file binario. 2. Leggere base64string dal file binario e deserializzare utilizzando 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

Puoi utilizzare JsonConvert dalla libreria Newtonsoft. Per serializzare un oggetto e scrivere su un file in formato json:

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

E per deserializzarlo nuovamente in oggetto:

var obj = JsonConvert.DeserializeObject<ObjType>(File.ReadAllText(filePath));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.