Серіалізуйте об'єкт до рядка


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 час виконання відрізняється Messageвід того, який показано на Ideone, але він не вдається точно так само.


2
@JohnSaunders: добре, це гарна ідея перенести цю дискусію на Meta. Ось посилання на питання, яке я щойно опублікував у Meta Stack Overflow щодо цього редагування .
Фульвіо

27
@casperOne Хлопці, будь ласка, перестаньте возитися з моєю відповіддю. Справа в тому, щоб використовувати StringWriter замість StreamWriter, все інше не стосується питання. Якщо ви хочете обговорити такі деталі, як, наприклад, typeof(T) проти toSerialize.GetType(), будь ласка, зробіть це, але не в моїй відповіді. Дякую.
dtb

9
@dtb Вибачте, але стек Overflow редагується спільно . Також ця конкретна відповідь обговорювалася на мета , тому редагування стоїть. Якщо ви не згодні, то, будь ласка, дайте відповідь на цю публікацію на мета про те, чому ви вважаєте, що ваша відповідь є особливим випадком і не слід редагувати спільно.
casperOne

2
Codewise, це найкоротший приклад, який я бачив. +1
froggythefrog

13
StringWriter реалізує IDisposable, тому повинен бути укладений у використовуючий блок.
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 також показує, як дезаріалізувати, на відміну від усіх інших відповідей. Дякую!
смертельної собаки

6
Однією незначною зміною було б повернення T замість об'єкта і передача повернутого об'єкта на T у функції DeserializeObject. Таким чином повертається сильно набраний об'єкт замість загального об'єкта.
смертельної собаки

Дякую @deadlydog, я виправив.
ADM-IT

3
TextWriter має функцію Dispose (), яку слід викликати. Отже, ви забуваєте користуватися операторами Use.
Герман Ван дер Блом

38

Примітка щодо безпеки коду

Стосовно прийнятої відповіді важливо використовувати toSerialize.GetType()замість typeof(T)inXmlSerializer конструкторі: якщо ви використовуєте перший код охоплює всі можливі сценарії, при використанні останньої не вдається іноді.

Ось посилання з деяким прикладом коду, що мотивує це висловлювання, з 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! Я просто намагався зберегти зразок коду якомога ближче до ОП, щоб виділити мою точку, що стосується типів об'єктів. У будь-якому випадку добре пам’ятати будь-кого, що usingтвердження є найкращим другом як для нас, так і для наших дорогих IDisposableреалізуючих об’єктів;)
Фулвіо

"Методи розширення дозволяють" додавати "методи до існуючих типів без створення нового похідного типу, перекомпіляції чи іншої модифікації вихідного типу." msdn.microsoft.com/en-us/library/bb383977.aspx
Адріан

12

Мій 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 для використання XmlWriterSettings (). Я хотів, щоб мій серіалізований XML не витрачав місця на гарні матеріали друку та налаштування Indent = false, а NewLineOnAttributes = false зробив цю роботу.
Лі Річардсон

Дякую @LeeRichardson - мені потрібно було зробити навпаки, також XmlWriter під .net за замовчуванням UTF16, що не те, що я писав ні на що.
oPless

використання цієї комбінації потоку пам'яті та отримання її за допомогою кодування GetString включить Преамбулу / BOM в якості першого символу у вашій рядку. Дивіться також stackoverflow.com/questions/11701341 / ...
Jamee

@Jamee "Encoding = UTF8Encoding (false)" означає, що не пишіть BOM відповідно до docs.microsoft.com/en-us/dotnet/api/… ... чи змінилася ця поведінка з моменту .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();
            }
        }

1

Мені не вдалося використати метод JSONConvert, запропонований xhafan

У .Net 4.5 навіть після додавання посилання на збірку "System.Web.Extensions" я все ще не міг отримати доступ до JSONConvert.

Однак, як тільки ви додасте посилання, ви можете вивести той самий рядок, використовуючи:

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

2
Клас JSONConvert знаходиться в просторі імен NewtonSoft.Json. Перейдіть до менеджера пакунків у вас, а потім завантажте пакет 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

[В.Б.]

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.