Serializza un oggetto su stringa


311

Ho il seguente metodo per salvare un oggetto in un file:

// Save an object out to the disk
public static void SerializeObject<T>(this T toSerialize, String filename)
{
    XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
    TextWriter textWriter = new StreamWriter(filename);

    xmlSerializer.Serialize(textWriter, toSerialize);
    textWriter.Close();
}

Confesso di non averlo scritto (l'ho solo convertito in un metodo di estensione che ha preso un parametro di tipo).

Ora ne ho bisogno per restituirmi l'xml come stringa (piuttosto che salvarlo in un file). Ci sto esaminando, ma non l'ho ancora capito.

Ho pensato che potrebbe essere davvero facile per qualcuno che abbia familiarità con questi oggetti. Altrimenti alla fine lo capirò.

Risposte:


530

Usa a StringWriterinvece di a StreamWriter:

public static string SerializeObject<T>(this T toSerialize)
{
    XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());

    using(StringWriter textWriter = new StringWriter())
    {
        xmlSerializer.Serialize(textWriter, toSerialize);
        return textWriter.ToString();
    }
}

Nota, è importante usare toSerialize.GetType()invece che typeof(T)nel costruttore XmlSerializer: se usi la prima il codice copre tutte le possibili sottoclassi di T(che sono valide per il metodo), mentre usando quest'ultima fallirà quando passa un tipo derivato T. Ecco un collegamento con un codice di esempio che motiva questa affermazione, con il XmlSerializerlancio di un Exceptionquando typeof(T)viene utilizzato, perché si passa un'istanza di un tipo derivato a un metodo che chiama SerializeObject definito nella classe base del tipo derivato: http: // ideone .com / 1Z5J1 .

Inoltre, Ideone utilizza Mono per eseguire il codice; l'attuale Exceptionche si otterrebbe utilizzando il runtime di Microsoft .NET ha uno diverso Messageda quello mostrato su Ideone, ma fallisce lo stesso.


2
@JohnSaunders: ok, è una buona idea spostare questa discussione su Meta. Ecco il link alla domanda che ho appena pubblicato su Meta Stack Overflow per quanto riguarda questa modifica .
Fulvio,

27
@casperOne Ragazzi, per favore smettete di scherzare con la mia risposta. Il punto è usare StringWriter invece di StreamWriter, tutto il resto non è rilevante per la domanda. Se vuoi discutere dettagli come typeof(T) versus toSerialize.GetType(), ti preghiamo di farlo, ma non nella mia risposta. Grazie.
dtb

9
@dtb Siamo spiacenti, ma Stack Overflow viene modificato in modo collaborativo . Inoltre, questa risposta specifica è stata discussa su meta , quindi la modifica è valida. Se non sei d'accordo, rispondi a quel post su meta sul perché pensi che la tua risposta sia un caso speciale e non debba essere modificata in modo collaborativo.
casperOne

2
Codewise, questo è l'esempio più breve che abbia mai visto. +1
froggythefrog

13
StringWriter implementa IDisposable, quindi dovrebbe essere racchiuso in un blocco using.
TrueWill

70

So che questa non è davvero una risposta alla domanda, ma in base al numero di voti per la domanda e alla risposta accettata, sospetto che le persone stiano effettivamente utilizzando il codice per serializzare un oggetto su una stringa.

L'uso della serializzazione XML aggiunge immondizia di testo extra non necessaria all'output.

Per la seguente classe

public class UserData
{
    public int UserId { get; set; }
}

genera

<?xml version="1.0" encoding="utf-16"?>
<UserData xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <UserId>0</UserId>
</UserData>

La soluzione migliore è utilizzare la serializzazione JSON (una delle migliori è Json.NET ). Per serializzare un oggetto:

var userData = new UserData {UserId = 0};
var userDataString = JsonConvert.SerializeObject(userData);

Per deserializzare un oggetto:

var userData = JsonConvert.DeserializeObject<UserData>(userDataString);

La stringa JSON serializzata sarebbe simile a:

{"UserId":0}

4
In questo caso hai ragione, ma hai visto documenti XML di grandi dimensioni e documenti JSON di grandi dimensioni. Il documento JSON è difficilmente leggibile. La "spazzatura" di cui parli come gli spazi dei nomi può essere soppressa. L'XML generato può essere pulito come JSON ma è SEMPRE più leggibile come JSON. La leggibilità è un grande vantaggio rispetto a JSON.
Herman Van Der Blom

2
Se cerchi online "json online parser" troverai alcuni parser json online che possono formattare la stringa json in un modo più leggibile dall'uomo.
Xhafan,

9
@HermanVanDerBlom XML più leggibile di JSON? Cosa stai fumando nel mondo? Questo è uno dei maggiori vantaggi di JSON rispetto a XML: è molto più facile da leggere a causa del rapporto segnale / rumore più elevato. In poche parole, con JSON il contenuto non sta affogando nella zuppa di tag!
Mason Wheeler,

63

Serializza e deserializza (XML / JSON):

    public static T XmlDeserialize<T>(this string toDeserialize)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
        using(StringReader textReader = new StringReader(toDeserialize))
        {      
            return (T)xmlSerializer.Deserialize(textReader);
        }
    }

    public static string XmlSerialize<T>(this T toSerialize)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
        using(StringWriter textWriter = new StringWriter())
        {
            xmlSerializer.Serialize(textWriter, toSerialize);
            return textWriter.ToString();
        }
    }

    public static T JsonDeserialize<T>(this string toDeserialize)
    {
        return JsonConvert.DeserializeObject<T>(toDeserialize);
    }

    public static string JsonSerialize<T>(this T toSerialize)
    {
        return JsonConvert.SerializeObject(toSerialize);
    }

15
+1 per mostrare anche come deserializzare, a differenza di tutte le altre risposte. Grazie!
cane mortale,

6
Una piccola modifica sarebbe però quella di restituire T invece di oggetto, e lanciare l'oggetto restituito su T nella funzione DeserializeObject. In questo modo viene restituito l'oggetto fortemente tipizzato anziché un oggetto generico.
cane mortale,

Grazie @deadlydog, ho risolto.
ADM-IT

3
TextWriter ha una funzione Dispose () che dovrebbe essere chiamata. Quindi stai dimenticando le dichiarazioni Using.
Herman Van Der Blom

38

Codice Nota sulla sicurezza

Per quanto riguarda la risposta accettata , è importante utilizzare toSerialize.GetType()invece che typeof(T)nel XmlSerializercostruttore: se si utilizza la prima, il codice copre tutti i possibili scenari, mentre l'utilizzo di quest'ultima a volte fallisce.

Ecco un collegamento con un codice di esempio che motiva questa affermazione, con il XmlSerializerlancio di un'eccezione quando typeof(T)viene utilizzata, poiché si passa un'istanza di un tipo derivato a un metodo che chiama SerializeObject<T>()definito nella classe base del tipo derivato: http: // ideone .com / 1Z5J1 . Si noti che Ideone utilizza Mono per eseguire il codice: l'eccezione effettiva che si otterrebbe utilizzando il runtime di Microsoft .NET ha un messaggio diverso da quello mostrato su Ideone, ma fallisce lo stesso.

Per completezza, inserisco qui l'esempio di codice completo per riferimento futuro, nel caso in cui Ideone (dove ho pubblicato il codice) non sia disponibile in futuro:

using System;
using System.Xml.Serialization;
using System.IO;

public class Test
{
    public static void Main()
    {
        Sub subInstance = new Sub();
        Console.WriteLine(subInstance.TestMethod());
    }

    public class Super
    {
        public string TestMethod() {
            return this.SerializeObject();
        }
    }

    public class Sub : Super
    {
    }
}

public static class TestExt {
    public static string SerializeObject<T>(this T toSerialize)
    {
        Console.WriteLine(typeof(T).Name);             // PRINTS: "Super", the base/superclass -- Expected output is "Sub" instead
        Console.WriteLine(toSerialize.GetType().Name); // PRINTS: "Sub", the derived/subclass

        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
        StringWriter textWriter = new StringWriter();

        // And now...this will throw and Exception!
        // Changing new XmlSerializer(typeof(T)) to new XmlSerializer(subInstance.GetType()); 
        // solves the problem
        xmlSerializer.Serialize(textWriter, toSerialize);
        return textWriter.ToString();
    }
}

12
Si dovrebbe anche fare using (StringWriter textWriter = new StringWriter() {}per la corretta chiusura / smaltimento dell'oggetto.
Amichevole

Sono completamente d'accordo con te @Amicable! Ho semplicemente provato a mantenere il mio esempio di codice il più vicino possibile a quello OP, al fine di evidenziare il mio punto che riguarda i tipi di oggetto. Comunque è bene ricordare a chiunque che l' usingaffermazione è la migliore amica sia per noi che per i nostri cari IDisposableoggetti di attuazione;)
Fulvio

"I metodi di estensione ti consentono di" aggiungere "metodi a tipi esistenti senza creare un nuovo tipo derivato, ricompilare o modificare in altro modo il tipo originale." msdn.microsoft.com/en-us/library/bb383977.aspx
Adrian

12

Il mio 2p ...

        string Serialise<T>(T serialisableObject)
        {
            var xmlSerializer = new XmlSerializer(serialisableObject.GetType());

            using (var ms = new MemoryStream())
            {
                using (var xw = XmlWriter.Create(ms, 
                    new XmlWriterSettings()
                        {
                            Encoding = new UTF8Encoding(false),
                            Indent = true,
                            NewLineOnAttributes = true,
                        }))
                {
                    xmlSerializer.Serialize(xw,serialisableObject);
                    return Encoding.UTF8.GetString(ms.ToArray());
                }
            }
        }

+1 per l'utilizzo di XmlWriterSettings (). Volevo che il mio XML serializzato non sprecasse spazio con le belle stampe e l'impostazione Indent = false e NewLineOnAttributes = false ha fatto il lavoro.
Lee Richardson,

Grazie @LeeRichardson - Avevo bisogno di fare esattamente l'opposto, anche XmlWriter in .net ha come impostazione predefinita UTF16 che non è nemmeno quello che stavo scrivendo.
oPless il

usare questa combinazione di memorystream e ottenerlo tramite la codifica GetString includerà il preambolo / DBA come primo carattere nella stringa. Vedere anche stackoverflow.com/questions/11701341/...
Jamee

@Jamee "Encoding = UTF8Encoding (false)" significa che non scrivere la DBA secondo docs.microsoft.com/en-us/dotnet/api/… ... questo comportamento è cambiato da .net4?
oPlessa

4
public static string SerializeObject<T>(T objectToSerialize)
        {
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            MemoryStream memStr = new MemoryStream();

            try
            {
                bf.Serialize(memStr, objectToSerialize);
                memStr.Position = 0;

                return Convert.ToBase64String(memStr.ToArray());
            }
            finally
            {
                memStr.Close();
            }
        }

        public static T DerializeObject<T>(string objectToDerialize)
        {
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            byte[] byteArray = Convert.FromBase64String(objectToDerialize);
            MemoryStream memStr = new MemoryStream(byteArray);

            try
            {
                return (T)bf.Deserialize(memStr);
            }
            finally
            {
                memStr.Close();
            }
        }

1

Non sono stato in grado di utilizzare il metodo JSONConvert suggerito da xhafan

In .Net 4.5 anche dopo aver aggiunto il riferimento di assembly "System.Web.Extensions" non ero ancora in grado di accedere a JSONConvert.

Tuttavia, una volta aggiunto il riferimento è possibile ottenere la stessa stringa stampata utilizzando:

JavaScriptSerializer js = new JavaScriptSerializer();
string jsonstring = js.Serialize(yourClassObject);

2
La classe JSONConvert si trova nello spazio dei nomi NewtonSoft.Json. Vai al gestore pacchetti in VS e poi scarica il pacchetto NewtonSoft.Json
Amir Shrestha,

1

Mi sentivo come se avessi bisogno di condividere questo codice manipolato con la risposta accettata - poiché non ho reputazione, non posso commentare ..

using System;
using System.Xml.Serialization;
using System.IO;

namespace ObjectSerialization
{
    public static class ObjectSerialization
    {
        // THIS: (C): /programming/2434534/serialize-an-object-to-string
        /// <summary>
        /// A helper to serialize an object to a string containing XML data of the object.
        /// </summary>
        /// <typeparam name="T">An object to serialize to a XML data string.</typeparam>
        /// <param name="toSerialize">A helper method for any type of object to be serialized to a XML data string.</param>
        /// <returns>A string containing XML data of the object.</returns>
        public static string SerializeObject<T>(this T toSerialize)
        {
            // create an instance of a XmlSerializer class with the typeof(T)..
            XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());

            // using is necessary with classes which implement the IDisposable interface..
            using (StringWriter stringWriter = new StringWriter())
            {
                // serialize a class to a StringWriter class instance..
                xmlSerializer.Serialize(stringWriter, toSerialize); // a base class of the StringWriter instance is TextWriter..
                return stringWriter.ToString(); // return the value..
            }
        }

        // THIS: (C): VPKSoft, 2018, https://www.vpksoft.net
        /// <summary>
        /// Deserializes an object which is saved to an XML data string. If the object has no instance a new object will be constructed if possible.
        /// <note type="note">An exception will occur if a null reference is called an no valid constructor of the class is available.</note>
        /// </summary>
        /// <typeparam name="T">An object to deserialize from a XML data string.</typeparam>
        /// <param name="toDeserialize">An object of which XML data to deserialize. If the object is null a a default constructor is called.</param>
        /// <param name="xmlData">A string containing a serialized XML data do deserialize.</param>
        /// <returns>An object which is deserialized from the XML data string.</returns>
        public static T DeserializeObject<T>(this T toDeserialize, string xmlData)
        {
            // if a null instance of an object called this try to create a "default" instance for it with typeof(T),
            // this will throw an exception no useful constructor is found..
            object voidInstance = toDeserialize == null ? Activator.CreateInstance(typeof(T)) : toDeserialize;

            // create an instance of a XmlSerializer class with the typeof(T)..
            XmlSerializer xmlSerializer = new XmlSerializer(voidInstance.GetType());

            // construct a StringReader class instance of the given xmlData parameter to be deserialized by the XmlSerializer class instance..
            using (StringReader stringReader = new StringReader(xmlData))
            {
                // return the "new" object deserialized via the XmlSerializer class instance..
                return (T)xmlSerializer.Deserialize(stringReader);
            }
        }

        // THIS: (C): VPKSoft, 2018, https://www.vpksoft.net
        /// <summary>
        /// Deserializes an object which is saved to an XML data string.
        /// </summary>
        /// <param name="toDeserialize">A type of an object of which XML data to deserialize.</param>
        /// <param name="xmlData">A string containing a serialized XML data do deserialize.</param>
        /// <returns>An object which is deserialized from the XML data string.</returns>
        public static object DeserializeObject(Type toDeserialize, string xmlData)
        {
            // create an instance of a XmlSerializer class with the given type toDeserialize..
            XmlSerializer xmlSerializer = new XmlSerializer(toDeserialize);

            // construct a StringReader class instance of the given xmlData parameter to be deserialized by the XmlSerializer class instance..
            using (StringReader stringReader = new StringReader(xmlData))
            {
                // return the "new" object deserialized via the XmlSerializer class instance..
                return xmlSerializer.Deserialize(stringReader);
            }
        }
    }
}


So che è vecchio, ma poiché hai dato una risposta davvero buona, aggiungerò un piccolo commento, come se avessi fatto una revisione del codice su un PR: dovresti avere dei vincoli su T quando usi i generici. Aiuta a mantenere le cose pulite, e non tutti gli oggetti in una base di codice e nei framework a cui si fa riferimento si prestano alla serializzazione
Frank R. Haugen,

-1

In alcuni rari casi potresti voler implementare la tua serializzazione String.

Ma probabilmente è una cattiva idea se non sai cosa stai facendo. (ad es. serializzazione per I / O con un file batch)

Qualcosa del genere farebbe il trucco (e sarebbe facile modificarlo a mano / batch), ma fai attenzione che dovrebbero essere fatti altri controlli, come se quel nome non contenga una nuova riga.

public string name {get;set;}
public int age {get;set;}

Person(string serializedPerson) 
{
    string[] tmpArray = serializedPerson.Split('\n');
    if(tmpArray.Length>2 && tmpArray[0].Equals("#")){
        this.name=tmpArray[1];
        this.age=int.TryParse(tmpArray[2]);
    }else{
        throw new ArgumentException("Not a valid serialization of a person");
    }
}

public string SerializeToString()
{
    return "#\n" +
           name + "\n" + 
           age;
}

-1

[VB]

Public Function XmlSerializeObject(ByVal obj As Object) As String

    Dim xmlStr As String = String.Empty

    Dim settings As New XmlWriterSettings()
    settings.Indent = False
    settings.OmitXmlDeclaration = True
    settings.NewLineChars = String.Empty
    settings.NewLineHandling = NewLineHandling.None

    Using stringWriter As New StringWriter()
        Using xmlWriter__1 As XmlWriter = XmlWriter.Create(stringWriter, settings)

            Dim serializer As New XmlSerializer(obj.[GetType]())
            serializer.Serialize(xmlWriter__1, obj)

            xmlStr = stringWriter.ToString()
            xmlWriter__1.Close()
        End Using

        stringWriter.Close()
    End Using

    Return xmlStr.ToString
End Function

Public Function XmlDeserializeObject(ByVal data As [String], ByVal objType As Type) As Object

    Dim xmlSer As New System.Xml.Serialization.XmlSerializer(objType)
    Dim reader As TextReader = New StringReader(data)

    Dim obj As New Object
    obj = DirectCast(xmlSer.Deserialize(reader), Object)
    Return obj
End Function

[C #]

public string XmlSerializeObject(object obj)
{
    string xmlStr = String.Empty;
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = false;
    settings.OmitXmlDeclaration = true;
    settings.NewLineChars = String.Empty;
    settings.NewLineHandling = NewLineHandling.None;

    using (StringWriter stringWriter = new StringWriter())
    {
        using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings))
        {
            XmlSerializer serializer = new XmlSerializer( obj.GetType());
            serializer.Serialize(xmlWriter, obj);
            xmlStr = stringWriter.ToString();
            xmlWriter.Close();
        }
    }
    return xmlStr.ToString(); 
}

public object XmlDeserializeObject(string data, Type objType)
{
    XmlSerializer xmlSer = new XmlSerializer(objType);
    StringReader reader = new StringReader(data);

    object obj = new object();
    obj = (object)(xmlSer.Deserialize(reader));
    return obj;
}
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.