Serializuj obiekt na ciąg

311

Mam następującą metodę zapisania obiektu do pliku:

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

Przyznaję, że go nie napisałem (przekonwertowałem go tylko na metodę rozszerzenia, która przyjęła parametr typu).

Teraz muszę go zwrócić z powrotem xml jako ciąg znaków (zamiast zapisywać go w pliku). Patrzę na to, ale jeszcze tego nie rozgryzłem.

Pomyślałem, że może to być naprawdę łatwe dla kogoś, kto zna te przedmioty. Jeśli nie, w końcu to rozwiążę.

Vaccano
źródło

Odpowiedzi:

530

Użyj StringWriterzamiast zamiast 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();
    }
}

Uwaga: ważne jest, aby używać toSerialize.GetType()zamiast typeof(T)w konstruktorze XmlSerializer: jeśli użyjesz pierwszego, kod obejmie wszystkie możliwe podklasy T(które są ważne dla metody), podczas gdy użycie drugiego nie powiedzie się, gdy przekażesz typ pochodny T. Oto link z przykładowym kodem, który motywuje tę instrukcję, z XmlSerializerrzucaniem Exceptionkiedy typeof(T)jest używany, ponieważ przekazujesz instancję typu pochodnego do metody, która wywołuje SerializeObject, która jest zdefiniowana w klasie bazowej typu pochodnego: http: // ideone .pl / 1Z5J1 .

Ponadto Ideone używa Mono do wykonywania kodu; rzeczywisty Exceptionsposób korzystania z środowiska wykonawczego Microsoft .NET jest inny Messageniż pokazany w Ideone, ale kończy się tak samo.

dtb
źródło
2
@JohnSaunders: ok, dobrym pomysłem jest przeniesienie tej dyskusji na Meta. Oto link do pytania, które właśnie opublikowałem w Meta Stack Overflow dotyczące tej edycji .
Fulvio
27
@casperOne Guys, przestańcie zadzierać z moją odpowiedzią. Chodzi o to, aby użyć StringWriter zamiast StreamWriter, wszystko inne nie ma związku z pytaniem. Jeśli chcesz omówić szczegóły takie jak typeof(T) kontra toSerialize.GetType(), zrób to, ale nie w mojej odpowiedzi. Dzięki.
dtb
9
@dtb Niestety, ale przepełnienie stosu jest edytowane wspólnie . Ponadto, ta konkretna odpowiedź została omówiona na meta , więc edycja jest ważna. Jeśli się nie zgadzasz, odpowiedz na ten post na meta, dlaczego uważasz, że twoja odpowiedź jest szczególnym przypadkiem i nie powinna być edytowana wspólnie.
casperOne
2
Codewise, to najkrótszy przykład, jaki widziałem. +1
froggythefrog
13
StringWriter implementuje IDisposable, dlatego powinien być zamknięty w bloku używającym.
TrueWill
70

Wiem, że tak naprawdę nie jest to odpowiedź na pytanie, ale na podstawie liczby głosów na pytanie i przyjętej odpowiedzi podejrzewam, że ludzie faktycznie używają kodu do serializacji obiektu na ciąg.

Zastosowanie serializacji XML dodaje niepotrzebne dodatkowe śmieci tekstowe do wyniku.

Dla następnej klasy

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

generuje

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

Lepszym rozwiązaniem jest zastosowanie serializacji JSON (jedną z najlepszych jest Json.NET ). Aby serializować obiekt:

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

Aby dokonać deserializacji obiektu:

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

Serializowany ciąg JSON wyglądałby następująco:

{"UserId":0}
Xhafan
źródło
4
W tym przypadku masz rację, ale widziałeś duże dokumenty XML i duże dokumenty JSON. Dokument JSON jest mało czytelny. „Śmieci”, o których mówisz, podobnie jak przestrzenie nazw, można stłumić. Wygenerowany XML może być tak czysty jak JSON, ale ZAWSZE jest bardziej czytelny jak JSON. Czytelność jest dużą zaletą nad JSON.
Herman Van Der Blom
2
Jeśli wyszukujesz w trybie online „json online parser”, znajdziesz kilka parserów json online, które mogą sformatować ciąg json w sposób bardziej czytelny dla człowieka.
xhafan,
9
@HermanVanDerBlom XML bardziej czytelny niż JSON? Co ty na świecie palisz? To jedna z największych zalet JSON w stosunku do XML: jest znacznie łatwiejszy do odczytania ze względu na wyższy stosunek sygnału do szumu. Mówiąc najprościej, w JSON zawartość nie tonie w zupie tag!
Mason Wheeler
63

Serializacja i deserializacja (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);
    }
PRZYZNAĆ
źródło
15
+1 za pokazanie, jak dokonać deserializacji, w przeciwieństwie do wszystkich innych odpowiedzi. Dzięki!
deadlydog
6
Jedną drobną zmianą byłoby jednak zwrócenie T zamiast obiektu i rzutowanie zwróconego obiektu na T w funkcji DeserializeObject. W ten sposób zwracany jest silnie typowany obiekt zamiast obiektu ogólnego.
deadlydog
Dzięki @deadlydog, naprawiłem.
ADM-IT
3
TextWriter ma funkcję Dispose (), którą należy wywołać. Zapomniałeś więc instrukcji Using.
Herman Van Der Blom
38

Kod Uwaga bezpieczeństwa

Jeśli chodzi o przyjętą odpowiedź , ważne jest, aby użyć toSerialize.GetType()zamiast typeof(T)w XmlSerializerkonstruktorze: jeśli użyjesz pierwszego, kod obejmie wszystkie możliwe scenariusze, podczas gdy użycie drugiego niekiedy się nie powiedzie.

Oto link z przykładowym kodem, który motywuje tę instrukcję, z XmlSerializerrzuceniem wyjątku, gdy typeof(T)jest używany, ponieważ przekazujesz instancję typu pochodnego do metody, która wywołuje SerializeObject<T>()definicję w klasie bazowej typu pochodnego: http: // ideone .pl / 1Z5J1 . Należy pamiętać, że Ideone używa Mono do wykonania kodu: faktyczny wyjątek, który można uzyskać przy użyciu środowiska wykonawczego Microsoft .NET, ma inną wiadomość niż ta wyświetlana w Ideone, ale kończy się tak samo.

Ze względu na kompletność zamieszczam tutaj pełną próbkę kodu do wykorzystania w przyszłości, na wypadek gdyby Ideone (gdzie zamieściłem kod) stał się niedostępny w przyszłości:

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();
    }
}
Fulvio
źródło
12
Powinieneś także zrobić using (StringWriter textWriter = new StringWriter() {}dla właściwego zamknięcia / usunięcia obiektu.
Polubowne
Całkowicie się z tobą zgadzam @Dopuszczalne! Po prostu starałem się zachować mój przykładowy kod tak blisko jak to możliwe OP, aby podkreślić moją kwestię dotyczącą typów obiektów. W każdym razie dobrze jest pamiętać, że usingoświadczenie jest najlepszym przyjacielem zarówno dla nas, jak i dla naszych drogich IDisposableobiektów wykonawczych;)
Fulvio,
„Metody rozszerzeń umożliwiają„ dodawanie ”metod do istniejących typów bez tworzenia nowego typu pochodnego, ponownej kompilacji lub innego modyfikowania oryginalnego typu.” msdn.microsoft.com/en-us/library/bb383977.aspx
Adrian
12

My 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());
                }
            }
        }
oPless
źródło
+1 za użycie XmlWriterSettings (). Chciałem, aby mój zserializowany kod XML nie marnował miejsca przy ładnym drukowaniu i ustawienie Indent = false i NewLineOnAttributes = false wykonało zadanie.
Lee Richardson,
Dzięki @LeeRichardson - musiałem zrobić dokładnie odwrotnie, także XmlWriter w .net domyślnie UTF16 to nie to, do czego pisałem.
oPless
użycie tej kombinacji memorystream i uzyskanie go przez kodowanie GetString uwzględni Preamble / BOM jako pierwszy znak w twoim ciągu. Zobacz także stackoverflow.com/questions/11701341/…
Jamee
@Jamee „Encoding = UTF8Encoding (false)” oznacza, że ​​nie zapisuj BOM zgodnie z docs.microsoft.com/en-us/dotnet/api / ... ... czy to zmieniło się od czasu .net4?
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();
            }
        }
teapeng
źródło
1

Nie mogłem użyć metody JSONConvert sugerowanej przez xhafan

W .Net 4.5 nawet po dodaniu odwołania do zestawu „System.Web.Extensions” nadal nie mogłem uzyskać dostępu do JSONConvert.

Jednak po dodaniu odwołania możesz wydrukować ten sam ciąg znaków, używając:

JavaScriptSerializer js = new JavaScriptSerializer();
string jsonstring = js.Serialize(yourClassObject);
Thomas Tiveron
źródło
2
Klasa JSONConvert znajduje się w przestrzeni nazw NewtonSoft.Json. Idź do menedżera pakietów w tobie VS, a następnie pobierz pakiet NewtonSoft.Json
Amir Shrestha
1

Czułem, że muszę udostępnić ten zmanipulowany kod do zaakceptowanej odpowiedzi - ponieważ nie mam reputacji, nie mogę komentować ..

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

Petteri Kautonen
źródło
Wiem, że to jest stare, ale skoro dałeś naprawdę dobrą odpowiedź, dodam mały komentarz, tak jakbym zrobił recenzję kodu na PR: powinieneś mieć ograniczenia na T, gdy używasz generycznych. Pomaga utrzymać porządek, a nie każdy obiekt w bazie kodu i odnośnych ramach nadaje się do serializacji
Frank R. Haugen
-1

W niektórych rzadkich przypadkach możesz zaimplementować własną serializację ciągów.

Ale to prawdopodobnie zły pomysł, chyba że wiesz, co robisz. (np. serializacja dla I / O za pomocą pliku wsadowego)

Coś takiego wystarczyłoby (i byłoby to łatwe do edycji ręcznie / wsadowo), ale uważaj, aby wykonać więcej kontroli, ponieważ ta nazwa nie zawiera nowego wiersza.

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;
}
satibel
źródło
-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

[DO#]

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;
}
Brian
źródło