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


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 как строку (а не сохранил ее в файл). Я смотрю в это, но я еще не понял это.

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

Ответы:


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, но все равно не получится.


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}

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

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

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

-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;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.