Сериализация объекта в строку

311

У меня есть следующий метод для сохранения объекта в файл:

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

Признаюсь, я не написал его (я только преобразовал его в метод расширения, который принял параметр типа).

Теперь мне нужно, чтобы он вернул мне xml как строку (а не сохранил ее в файл). Я смотрю в это, но я еще не понял это.

Я подумал, что это может быть действительно легко для тех, кто знаком с этими объектами. Если нет, то я это выясню в конце концов.

Vaccano
источник

Ответы:

530

Используйте StringWriterвместо 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();
    }
}

Обратите внимание, что важно использовать toSerialize.GetType()вместо конструктора typeof(T)XmlSerializer: если вы используете первый, код покрывает все возможные подклассы T(которые действительны для метода), в то время как использование последнего не удастся при передаче типа, производного от T. Вот ссылка с некоторым примером кода, который мотивирует этот оператор, с использованием XmlSerializerброска Exceptionкогда typeof(T)используется, потому что вы передаете экземпляр производного типа методу, который вызывает SerializeObject, который определен в базовом классе производного типа: http: // ideone .com / 1Z5J1 .

Кроме того, Ideone использует Mono для выполнения кода; Фактически, Exceptionвы получите использование Microsoft .NET Runtime, отличающееся Messageот того, что показано на Ideone, но все равно не получится.

DTB
источник
2
@JohnSaunders: хорошо, это хорошая идея перенести эту дискуссию на Meta. Вот ссылка на вопрос, который я только что опубликовал в Meta Stack Overflow относительно этого редактирования .
Фульвио
27
@casperOne Ребята, пожалуйста, перестаньте возиться с моим ответом. Суть в том, чтобы использовать StringWriter вместо StreamWriter, все остальное не имеет отношения к вопросу. Если вы хотите обсудить детали, такие как typeof(T) против toSerialize.GetType(), пожалуйста, сделайте это, но не в моем ответе. Спасибо.
ДТБ
9
@dtb Извините, но переполнение стека редактируется совместно . Кроме того, этот конкретный ответ был обсужден на мета , поэтому редактирование стоит. Если вы не согласны, то, пожалуйста, ответьте на этот пост на мета-том, почему вы считаете, что ваш ответ является особым случаем и не должен редактироваться совместно.
casperOne
2
Codewise, это самый короткий пример, который я видел. +1
froggythefrog
13
StringWriter реализует IDisposable, поэтому должен быть заключен в блок using.
TrueWill
70

Я знаю, что это не совсем ответ на вопрос, но, основываясь на количестве голосов за вопрос и принятом ответе, я подозреваю, что люди фактически используют код для сериализации объекта в строку.

Использование сериализации XML добавляет ненужный дополнительный текстовый мусор к выводу.

Для следующего класса

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

это генерирует

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

Лучшее решение - использовать сериализацию JSON (одним из лучших является Json.NET ). Для сериализации объекта:

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

Для десериализации объекта:

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

Сериализованная строка JSON будет выглядеть так:

{"UserId":0}
xhafan
источник
4
В этом случае вы правы, но видели ли вы большие XML-документы и большие JSON-документы. Документ JSON трудно читаемый. «Мусор», о котором вы говорите, как пространства имен, может быть подавлен. Сгенерированный XML может быть таким же чистым, как JSON, но ВСЕГДА более читаем, как JSON. Читаемость является большим преимуществом перед JSON.
Херман ван дер Блом
2
Если вы ищете в Интернете «json online parser», вы найдете несколько онлайн-анализаторов json, которые могут форматировать строку json более понятным для человека способом.
xhafan
9
@HermanVanDerBlom XML более читабельный, чем JSON? Что в мире вы курите? Это один из самых сильных преимуществ JSON над XML: это гораздо легче читать из - за более высокого отношения сигнал / шум. Проще говоря, с JSON контент не тонет в супе тегов!
Мейсон Уилер
63

Сериализация и десериализация (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 для того, чтобы показать, как десериализовать, в отличие от всех других ответов. Спасибо!
deadlydog
6
Одно небольшое изменение, однако, заключалось бы в возврате T вместо объекта и приведении возвращенного объекта к T в функции DeserializeObject. Таким образом, вместо универсального объекта возвращается строго типизированный объект.
deadlydog
Спасибо @deadlydog, я исправил.
ADM-IT
3
TextWriter имеет функцию Dispose (), которая должна быть вызвана. Таким образом, вы забыли операторы Using.
Херман ван дер Блом
38

Кодекс безопасности Примечание

Что касается принятого ответа , важно использовать toSerialize.GetType()вместо typeof(T)в XmlSerializerконструкторе: если вы используете первый код охватывает все возможные сценарии, при использовании последней не удается иногда.

Вот ссылка с некоторым примером кода, который мотивирует этот оператор, с XmlSerializerвыдачей исключения, когда typeof(T)он используется, потому что вы передаете экземпляр производного типа методу, который вызывает метод SerializeObject<T>(), определенный в базовом классе производного типа: http: // ideone .com / 1Z5J1 . Обратите внимание, что Ideone использует Mono для выполнения кода: фактическое исключение, которое вы получите, используя среду выполнения Microsoft .NET, имеет сообщение, отличное от того, которое показано на Ideone, но оно терпит неудачу.

Ради полноты я опубликую здесь полный пример кода для дальнейшего использования, на случай, если Ideone (где я разместил код) станет недоступен в будущем:

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
Вы также должны сделать using (StringWriter textWriter = new StringWriter() {}для правильного закрытия / утилизации объекта.
Дружный
Я полностью согласен с тобой @Amicable! Я просто попытался сделать мой пример кода как можно ближе к OP, чтобы подчеркнуть мою точку зрения, касающуюся типов объектов. В любом случае, хорошо помнить кого-либо, что это usingутверждение - лучший друг и для нас, и для наших дорогих IDisposableреализующих объектов;)
Фульвио,
«Методы расширения позволяют« добавлять »методы к существующим типам без создания нового производного типа, перекомпиляции или иного изменения исходного типа». msdn.microsoft.com/en-us/library/bb383977.aspx
Адриан,
12

Мой 2р ...

        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
источник
+1 за использование XmlWriterSettings (). Я хотел, чтобы мой сериализованный XML не занимал пустого места из-за красивой печати и установки Indent = false и NewLineOnAttributes = false.
Ли Ричардсон
Спасибо @LeeRichardson - мне нужно было сделать прямо противоположное, также XmlWriter под .net по умолчанию использует UTF16, который я тоже не писал.
oPless
используя эту комбинацию потока памяти и получая ее через Encoding, GetString будет включать в себя преамбулу / спецификацию как первый символ в вашей строке. Смотрите также stackoverflow.com/questions/11701341/...
Jamee
@Jamee "Encoding = UTF8Encoding (false)" означает, что не пишите спецификацию в соответствии с docs.microsoft.com/en-us/dotnet/api/… ... изменилось ли это поведение после .net4?
Без
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
источник
1

Мне не удалось использовать метод JSONConvert, предложенный xhafan

В .Net 4.5 даже после добавления ссылки на сборку «System.Web.Extensions» я все еще не мог получить доступ к JSONConvert.

Однако, как только вы добавите ссылку, вы можете распечатать ту же строку, используя:

JavaScriptSerializer js = new JavaScriptSerializer();
string jsonstring = js.Serialize(yourClassObject);
Томас Тиверон
источник
2
Класс JSONConvert находится в пространстве имен NewtonSoft.Json. Перейдите к менеджеру пакетов в вашей VS и затем загрузите пакет NewtonSoft.Json
Амир Шреста
1

Я чувствовал, что мне нужно было поделиться этим манипулированным кодом с принятым ответом - поскольку у меня нет репутации, я не могу комментировать ..

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

Петтери Каутонен
источник
Я знаю, что это старо, но так как вы дали действительно хороший ответ, я добавлю небольшой комментарий, как будто я делал обзор кода для PR: у вас должны быть ограничения на T при использовании обобщений. Это помогает держать вещи в порядке, и не каждый объект в кодовой базе и ссылочных инфраструктурах поддается сериализации
Фрэнк Р. Хауген,
-1

В некоторых редких случаях вы можете захотеть реализовать свою собственную сериализацию String.

Но это, вероятно, плохая идея, если вы не знаете, что делаете. (например, сериализация для ввода / вывода с пакетным файлом)

Нечто подобное могло бы помочь (и это было бы легко отредактировать вручную / пакетно), но будьте осторожны, чтобы сделать еще несколько проверок, так как это имя не содержит перевода строки.

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
источник
-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;
}
Брайан
источник