Serialisieren Sie ein Objekt in eine Zeichenfolge


311

Ich habe die folgende Methode, um ein Objekt in einer Datei zu speichern:

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

Ich gebe zu, dass ich es nicht geschrieben habe (ich habe es nur in eine Erweiterungsmethode konvertiert, die einen Typparameter verwendet hat).

Jetzt brauche ich es, um mir die XML als Zeichenfolge zurückzugeben (anstatt sie in einer Datei zu speichern). Ich prüfe es, aber ich habe es noch nicht herausgefunden.

Ich dachte, das könnte für jemanden, der mit diesen Objekten vertraut ist, wirklich einfach sein. Wenn nicht, werde ich es irgendwann herausfinden.

Antworten:


530

Verwenden Sie a StringWriteranstelle von 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();
    }
}

Beachten Sie, dass die Verwendung toSerialize.GetType()anstelle des typeof(T)XmlSerializer-Konstruktors wichtig ist: Wenn Sie die erste verwenden, deckt der Code alle möglichen Unterklassen von T(die für die Methode gültig sind) ab, während die Verwendung der letzteren fehlschlägt, wenn ein von abgeleiteter Typ übergeben wird T. Hier ist ein Link mit einem Beispielcode, der diese Anweisung motiviert, wobei das XmlSerializerAuslösen eines ExceptionWann typeof(T)verwendet wird, da Sie eine Instanz eines abgeleiteten Typs an eine Methode übergeben, die SerializeObject aufruft, das in der Basisklasse des abgeleiteten Typs definiert ist: http: // ideone .com / 1Z5J1 .

Außerdem verwendet Ideone Mono, um Code auszuführen. Die tatsächliche ExceptionLaufzeit von Microsoft .NET unterscheidet sich von Messageder auf Ideone angezeigten Laufzeit, schlägt jedoch trotzdem fehl.


2
@ JohnSaunders: Ok, es ist eine gute Idee, diese Diskussion auf Meta zu verschieben. Hier ist der Link zu der Frage, die ich gerade zu dieser Änderung auf Meta Stack Overflow gestellt habe .
Fulvio

27
@casperOne Leute, bitte hört auf, mit meiner Antwort herumzuspielen. Der Punkt ist, StringWriter anstelle von StreamWriter zu verwenden, alles andere ist für die Frage nicht relevant. Wenn Sie Details wie typeof(T) Versus besprechen möchten toSerialize.GetType(), tun Sie dies bitte, aber nicht in meiner Antwort. Vielen Dank.
dtb

9
@dtb Sorry, aber Stack Overflow wird gemeinsam bearbeitet . Auch diese spezifische Antwort wurde auf Meta diskutiert , so dass die Bearbeitung steht. Wenn Sie nicht einverstanden sind, antworten Sie bitte auf diesen Beitrag auf Meta, warum Ihre Antwort Ihrer Meinung nach ein Sonderfall ist und nicht gemeinsam bearbeitet werden sollte.
CasperOne

2
Codeweise ist dies das kürzeste Beispiel, das ich gesehen habe. +1
Froggythefrog

13
StringWriter implementiert IDisposable und sollte daher in einem using-Block eingeschlossen werden.
TrueWill

70

Ich weiß, dass dies nicht wirklich eine Antwort auf die Frage ist, aber basierend auf der Anzahl der Stimmen für die Frage und der akzeptierten Antwort vermute ich, dass die Leute den Code tatsächlich verwenden, um ein Objekt in eine Zeichenfolge zu serialisieren.

Durch die Verwendung der XML-Serialisierung wird der Ausgabe unnötiger zusätzlicher Textabfall hinzugefügt.

Für die folgende Klasse

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

es erzeugt

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

Eine bessere Lösung ist die Verwendung der JSON-Serialisierung (eine der besten ist Json.NET ). So serialisieren Sie ein Objekt:

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

So deserialisieren Sie ein Objekt:

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

Die serialisierte JSON-Zeichenfolge würde folgendermaßen aussehen:

{"UserId":0}

4
In diesem Fall haben Sie Recht, haben aber große XML-Dokumente und große JSON-Dokumente gesehen. Das JSON-Dokument ist kaum lesbar. Der "Müll", von dem Sie sprechen, wie die Namespaces, kann unterdrückt werden. Das generierte XML kann so sauber wie JSON sein, ist jedoch IMMER besser lesbar als JSON. Die Lesbarkeit ist ein großer Vorteil gegenüber JSON.
Herman Van Der Blom

2
Wenn Sie online nach "json online parser" suchen, finden Sie einige Online-json-Parser, die den json-String besser lesbar formatieren können.
Xhafan

9
@HermanVanDerBlom XML besser lesbar als JSON? Was in aller Welt rauchst du? Dies ist einer der größten Vorteile von JSON gegenüber XML: Aufgrund des höheren Signal / Rausch-Verhältnisses ist das Lesen viel einfacher. Einfach ausgedrückt, mit JSON ertrinkt der Inhalt nicht in Tag-Suppe!
Mason Wheeler

63

Serialisieren und Deserialisieren (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, um im Gegensatz zu allen anderen Antworten auch zu zeigen, wie man deserialisiert. Vielen Dank!
tödlicher Hund

6
Eine geringfügige Änderung wäre jedoch, T anstelle von Objekt zurückzugeben und das zurückgegebene Objekt in der Funktion DeserializeObject in T umzuwandeln. Auf diese Weise wird das stark typisierte Objekt anstelle eines generischen Objekts zurückgegeben.
tödlicher Hund

Danke @deadlydog, ich habe behoben.
ADM-IT

3
TextWriter verfügt über eine Dispose () - Funktion, die aufgerufen werden sollte. Sie vergessen also die Using-Anweisungen.
Herman Van Der Blom

38

Code Sicherheitshinweis

In Bezug auf die akzeptierte Antwort , ist es wichtig , zu verwenden , toSerialize.GetType()statt typeof(T)im XmlSerializerKonstruktor: Wenn Sie das erste der Code umfasst alle möglichen Szenarien verwenden, während die letztere mit manchmal nicht.

Hier ist ein Link mit einem Beispielcode, der diese Anweisung motiviert und bei Verwendung XmlSerializereine Ausnahme auslösttypeof(T) , da Sie eine Instanz eines abgeleiteten Typs an eine Methode übergeben, die aufruft SerializeObject<T>(), die in der Basisklasse des abgeleiteten Typs definiert ist: http: // ideone .com / 1Z5J1 . Beachten Sie, dass Ideone Mono zum Ausführen von Code verwendet: Die tatsächliche Ausnahme, die Sie mit der Microsoft .NET-Laufzeit erhalten würden, hat eine andere Meldung als die in Ideone gezeigte, schlägt jedoch trotzdem fehl.

Der Vollständigkeit halber veröffentliche ich hier das vollständige Codebeispiel als Referenz für den Fall, dass Ideone (wo ich den Code veröffentlicht habe) in Zukunft nicht mehr verfügbar ist:

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
Sie sollten dies auch tun, using (StringWriter textWriter = new StringWriter() {}um das Objekt ordnungsgemäß zu schließen / zu entsorgen.
Freundlich

Ich stimme dir voll und ganz zu @Amicable! Ich habe einfach versucht, mein Codebeispiel so nah wie möglich am OP zu halten, um meinen Punkt hervorzuheben, bei dem es um Objekttypen geht. Wie auch immer, es ist gut, sich an jemanden zu erinnern, dass die usingAussage sowohl für uns als auch für unsere lieben IDisposableImplementierungsobjekte der beste Freund ist ;)
Fulvio

"Mit Erweiterungsmethoden können Sie Methoden zu vorhandenen Typen" hinzufügen ", ohne einen neuen abgeleiteten Typ zu erstellen, den ursprünglichen Typ neu zu kompilieren oder auf andere Weise zu ändern." msdn.microsoft.com/en-us/library/bb383977.aspx
Adrian

12

Mein 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 für die Verwendung von XmlWriterSettings (). Ich wollte, dass mein serialisiertes XML keinen Speicherplatz mit dem hübschen Druckmaterial verschwendet, und die Einstellung Indent = false und NewLineOnAttributes = false erledigten den Job.
Lee Richardson

Danke @LeeRichardson - ich musste genau das Gegenteil tun, auch XmlWriter unter .net ist standardmäßig UTF16, was ich auch nicht geschrieben habe.
oPless

Wenn Sie diese Kombination aus Memorystream verwenden und über Encoding GetString abrufen, wird die Präambel / Stückliste als erstes Zeichen in Ihre Zeichenfolge aufgenommen. Siehe auch stackoverflow.com/questions/11701341/…
Jamee

@Jamee "Encoding = UTF8Encoding (false)" bedeutet, dass die Stückliste nicht gemäß docs.microsoft.com/en-us/dotnet/api/… geschrieben wird. Hat sich dieses Verhalten seit .net4 geändert?
oPless

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

Ich konnte die von xhafan vorgeschlagene JSONConvert-Methode nicht verwenden

In .Net 4.5 konnte ich auch nach dem Hinzufügen der Assemblyreferenz "System.Web.Extensions" nicht auf JSONConvert zugreifen.

Sobald Sie jedoch die Referenz hinzugefügt haben, können Sie denselben Zeichenfolgendruck mit folgenden Zeichen erhalten:

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

2
Die JSONConvert-Klasse befindet sich im NewtonSoft.Json-Namespace. Gehen Sie zu Paketmanager in Ihrem VS und laden Sie dann das NewtonSoft.Json-Paket
Amir Shrestha

1

Ich hatte das Gefühl, dass ich diesen manipulierten Code an die akzeptierte Antwort weitergeben musste - da ich keinen Ruf habe, kann ich keinen Kommentar abgeben.

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


Ich weiß, dass dies alt ist, aber da Sie eine wirklich gute Antwort gegeben haben, füge ich einen kleinen Kommentar hinzu, als hätte ich eine Codeüberprüfung für eine PR durchgeführt: Sie sollten Einschränkungen für T haben, wenn Sie Generika verwenden. Es hilft, die Dinge ordentlich zu halten, und nicht jedes Objekt in einer Codebasis und in Frameworks, auf die verwiesen wird, eignet sich für die Serialisierung
Frank R. Haugen,

-1

In einigen seltenen Fällen möchten Sie möglicherweise Ihre eigene String-Serialisierung implementieren.

Aber das ist wahrscheinlich eine schlechte Idee, wenn Sie nicht wissen, was Sie tun. (zB Serialisierung für E / A mit einer Batchdatei)

So etwas würde den Trick machen (und es wäre einfach, von Hand / Batch zu bearbeiten), aber seien Sie vorsichtig, dass weitere Überprüfungen durchgeführt werden sollten, da dieser Name keine neue Zeile enthält.

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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.