Como salvar / restaurar um objeto serializável de / para um arquivo?

94

Tenho uma lista de objetos e preciso salvá-la em algum lugar do meu computador. Eu li alguns fóruns e sei que o objeto tem que ser Serializable. Mas seria bom se eu pudesse obter um exemplo. Por exemplo, se eu tiver o seguinte:

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

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

Mas como posso armazenar object1em algum lugar no meu computador e depois recuperar?

Tono Nam
fonte
3
Aqui está um tutorial que mostra como serializar para um arquivo switchonthecode.com/tutorials/…
Brook

Respostas:

141

Você pode usar o seguinte:

    /// <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
fonte
1
Agradável! Embora string attributeXml = string.Empty;em DeSerializeObjectnunca seja usado;)
Jimbo
3
Não há necessidade de chamar o método close em um leitor dentro do seu bloco de uso. Dispose () está implícito e ocorrerá mesmo se uma exceção for levantada dentro do bloco antes de Close () explícito. Bloco de código muito útil.
S. Brentson
2
Como salvar uma lista de objetos usando esta função Eu usei, mas ela está salvando apenas o último objeto da minha lista
Decoder94
1
Este método não salvará campos internos ou privados, você pode usar isto: github.com/mrbm2007/ObjectSaver
mrbm
151

Acabei de escrever uma postagem no blog sobre como salvar os dados de um objeto em Binário, XML ou Json . Você está correto ao dizer que deve decorar suas classes com o atributo [Serializable], mas apenas se estiver usando a serialização binária. Você pode preferir usar a serialização XML ou Json. Aqui estão as funções para fazer isso nos vários formatos. Veja minha postagem do blog para mais detalhes.

Binário

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

Requer que o assembly System.Xml seja incluído em seu projeto.

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

Você deve incluir uma referência ao assembly Newtonsoft.Json, que pode ser obtido no pacote Json.NET 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();
    }
}

Exemplo

// 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");
cachorro mortal
fonte
2
Eu gosto do seu código de serialização binária. Mas em WriteToBinaryFile, por que você desejaria anexar ao arquivo? Parece que você deseja criar um novo arquivo em todos os casos. Caso contrário, haveria um monte de informações extras sobre a desserialização.
rede sem fio pública de
1
@publicwireless Sim, provavelmente você está certo. Não pensei muito nisso na época; Eu só queria que as assinaturas das 3 funções correspondessem: P
deadlydog
usando o método append, serializando muitos objetos no mesmo arquivo, como faço para desserializá-los? como procuro no stream?
John Demetriou
1
Por favor, adicione o comentário ao serializador binário que avisará as pessoas que os dados resultantes são marcados com o nome forte do assembly e as alterações nas versões deste sem adicionar ligações de redirecionamento ou rodando em ambientes que não respeitam essas ligações (por exemplo, PowerShell). falha
zaitsman
1
@JohnDemetriou Se estiver salvando várias coisas em um arquivo, eu recomendaria agrupar os objetos em alguma forma de objeto de contexto e serializar esse objeto (deixe o gerenciador de objetos analisar as partes que você deseja). Se você estiver tentando salvar mais dados do que pode armazenar na memória, convém alternar para um armazenamento de objeto (banco de dados de objeto) em vez de um arquivo.
Tezra de
30

Você precisará serializar para algo: isto é, escolher binário ou xml (para serializadores padrão) ou escrever um código de serialização personalizado para serializar em algum outro formato de texto.

Depois de escolher isso, sua serialização (normalmente) chamará um Stream que está gravando em algum tipo de arquivo.

Então, com seu código, se eu estivesse usando serialização 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);
}

Então, para desserializar:

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

    var myObject = _xSer.Deserialize(fs);
}

NOTA: Este código não foi compilado, muito menos executado - pode haver alguns erros. Além disso, isso pressupõe uma serialização / desserialização totalmente pronta para uso. Se precisar de um comportamento personalizado, você precisará fazer um trabalho adicional.

AllenG
fonte
10

1. Restaurar objeto do arquivo

A partir daqui, você pode desserializar um objeto do arquivo de duas maneiras.

Solução 1: leia o arquivo em uma string e desserialize o JSON para um tipo

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

Solução 2: desserializar JSON diretamente de um arquivo

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

2. Salvar objeto para arquivo

a partir daqui, você pode serializar um objeto para um arquivo de duas maneiras.

Solução 1: serializar JSON em uma string e, em seguida, gravar a string em um arquivo

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

Solução 2: serializar JSON diretamente em um arquivo

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

3. Extra

Você pode baixar Newtonsoft.Json de NuGet seguindo o comando

Install-Package Newtonsoft.Json
Emdadul Sawon
fonte
1

** 1. Converta a string json em base64string e grave ou anexe-a ao arquivo binário. 2. Leia base64string do arquivo binário e desserialize usando 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);
        }
    }
}
convidado
fonte
0

Você pode usar o JsonConvert da biblioteca Newtonsoft. Para serializar um objeto e gravar em um arquivo no formato json:

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

E para desserializar de volta no objeto:

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