Сериализация объекта в строку
У меня есть следующий способ сохранения объекта в файл:
// 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 мне как строку (а не сохранить его в файл). Я изучаю это, но пока не понял.
Я думал, что это может быть очень легко для кого-то, знакомого с этими объектами. Если нет, я в конце концов выясню это.
Ответы
Ответ 1
Используйте 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
throw , когда используется typeof(T)
, потому что вы передаете экземпляр производного типа методу, который вызывает SerializeObject, который определен в производном тип базового класса: http://ideone.com/1Z5J1.
Кроме того, Ideone использует Mono для выполнения кода; фактический Exception
, который вы получили бы с использованием среды выполнения Microsoft.NET, имеет другой Message
, чем тот, который показан на Ideone, но он терпит неудачу точно так же.
Ответ 2
Я знаю, что на самом деле это не ответ на вопрос, но исходя из количества голосов за вопрос и принятого ответа, я подозреваю, что люди фактически используют код для сериализации объекта для строки.
Использование сериализации 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}
Ответ 3
Сериализация и десериализация:
public static T Deserialize<T>(this string toDeserialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringReader textReader = new StringReader(toDeserialize);
return (T)xmlSerializer.Deserialize(textReader);
}
public static string Serialize<T>(this T toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringWriter textWriter = new StringWriter();
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
Ответ 4
Замечание по безопасности кода
Что касается принятого ответа, важно использовать 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();
}
}
Ответ 5
Мой 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());
}
}
}
Ответ 6
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();
}
}
Ответ 7
Мне не удалось использовать метод JSONConvert, предложенный xhafan
В .Net 4.5 даже после добавления ссылки на сборку "System.Web.Extensions" мне все еще не удалось получить доступ к JSONConvert.
Однако, как только вы добавите ссылку, вы можете получить такую же распечатку строки, используя:
JavaScriptSerializer js = new JavaScriptSerializer();
string jsonstring = js.Serialize(yourClassObject);
Ответ 8
В некоторых редких случаях вам может понадобиться реализовать собственную сериализацию строк.
Но это, вероятно, идея bad, если вы не знаете, что делаете. (например, сериализация для ввода-вывода с пакетным файлом)
Что-то подобное сделало бы трюк (и его было бы легко отредактировать вручную/партиями), но будьте осторожны, чтобы сделать еще несколько проверок, например, это имя не содержит новую строку.
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;
}