Comment enregistrer / restaurer un objet sérialisable vers / depuis un fichier?

94

J'ai une liste d'objets et je dois l'enregistrer quelque part dans mon ordinateur. J'ai lu quelques forums et je sais que l'objet doit être Serializable. Mais ce serait bien si je pouvais avoir un exemple. Par exemple, si j'ai ce qui suit:

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

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

Mais comment puis-je stocker object1quelque part dans mon ordinateur et récupérer plus tard?

Tono Nam
la source
3
Voici un tutoriel qui montre comment sérialiser dans un fichier switchonthecode.com/tutorials
Brook

Réponses:

142

Vous pouvez utiliser les éléments suivants:

    /// <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;
    }
Alex Mendez
la source
1
Agréable! Bien que string attributeXml = string.Empty;dans DeSerializeObjectne soit jamais utilisé;)
Jimbo
3
Pas besoin d'appeler la méthode close sur un lecteur dans votre bloc using. Dispose () est implicite et aura lieu même si une exception est déclenchée dans le bloc avant le Close () explicite. Bloc de code très utile.
S.Brentson
2
Comment enregistrer une liste d'objets en utilisant cette fonction Je l'ai utilisé mais il
n'enregistre
1
Cette méthode n'enregistrera pas les champs internes ou privés, vous pouvez utiliser ceci: github.com/mrbm2007/ObjectSaver
mrbm
152

Je viens d'écrire un article de blog sur l'enregistrement des données d'un objet en binaire, XML ou Json . Vous avez raison de dire que vous devez décorer vos classes avec l'attribut [Serializable], mais uniquement si vous utilisez la sérialisation binaire. Vous préférerez peut-être utiliser la sérialisation XML ou Json. Voici les fonctions pour le faire dans les différents formats. Voir mon article de blog pour plus de détails.

Binaire

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

Nécessite que l'assembly System.Xml soit inclus dans votre projet.

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

Vous devez inclure une référence à l'assembly Newtonsoft.Json, qui peut être obtenue à partir du package 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();
    }
}

Exemple

// 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");
chien mortel
la source
2
J'aime votre code de sérialisation binaire. Mais sur WriteToBinaryFile, pourquoi voudriez-vous jamais ajouter au fichier? Il semble que vous souhaitiez créer un nouveau fichier dans tous les cas. Sinon, il y aurait tout un tas d'informations supplémentaires sur la désérialisation.
public sans fil
1
@publicwireless Ouais, vous avez probablement raison. Je n'y ai pas beaucoup pensé à l'époque; Je voulais juste que les signatures des 3 fonctions correspondent: P
deadlydog
en utilisant la méthode append, en sérialisant de nombreux objets dans le même fichier, comment les désérialiser? comment chercher dans le flux?
John Demetriou
1
Veuillez ajouter le commentaire au sérialiseur binaire qui informera les gens que les données résultantes sont estampillées avec le nom fort de l'assembly et que les modifications apportées aux versions de celui-ci sans ajouter de liaisons de redirection ou exécuter dans des environnements qui ne respectent pas lesdites liaisons (par exemple, PowerShell) fail
zaitsman
1
@JohnDemetriou Si vous enregistrez plusieurs choses dans un fichier, je recommanderais d'encapsuler les objets dans une forme d'objet de contexte et de sérialiser cet objet (laissez le gestionnaire d'objets analyser les parties que vous voulez). Si vous essayez d'enregistrer plus de données que vous ne pouvez en conserver en mémoire, vous souhaiterez peut-être basculer vers un magasin d'objets (base de données d'objets) au lieu d'un fichier.
Tezra
30

Vous aurez besoin de sérialiser quelque chose: c'est-à-dire de choisir binaire ou xml (pour les sérialiseurs par défaut) ou d'écrire un code de sérialisation personnalisé pour sérialiser sous une autre forme de texte.

Une fois que vous avez choisi cela, votre sérialisation appellera (normalement) un Stream qui écrit dans une sorte de fichier.

Donc, avec votre code, si j'utilisais la sérialisation 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);
}

Ensuite, pour désérialiser:

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

    var myObject = _xSer.Deserialize(fs);
}

REMARQUE: ce code n'a pas été compilé, encore moins exécuté - il peut y avoir des erreurs. En outre, cela suppose une sérialisation / désérialisation complètement prête à l'emploi. Si vous avez besoin d'un comportement personnalisé, vous devrez effectuer un travail supplémentaire.

AllenG
la source
10

1. Restaurer l'objet à partir du fichier

À partir de là, vous pouvez désérialiser un objet d'un fichier de deux manières.

Solution-1: lire le fichier dans une chaîne et désérialiser JSON en un type

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

Solution-2: désérialiser JSON directement à partir d'un fichier

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

2. Enregistrer l'objet dans un fichier

à partir de là, vous pouvez sérialiser un objet dans un fichier de deux manières.

Solution-1: sérialiser JSON en une chaîne, puis écrire une chaîne dans un fichier

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

Solution-2: sérialiser JSON directement dans un fichier

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

3. Extra

Vous pouvez télécharger Newtonsoft.Json à partir de NuGet en suivant la commande

Install-Package Newtonsoft.Json
Emdadul Sawon
la source
1

**1. Convertissez la chaîne json en base64string et écrivez-la ou ajoutez-la au fichier binaire. 2. Lisez base64string à partir du fichier binaire et désérialisez à l'aide de 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);
        }
    }
}
client
la source
0

Vous pouvez utiliser JsonConvert de la bibliothèque Newtonsoft. Pour sérialiser un objet et écrire dans un fichier au format json:

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

Et pour le désérialiser en objet:

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