I have the following method to save an Object to a file:
// 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();
}
I confess I did not write it (I only converted it to a extension method that took a type parameter).
Now I need it to give the xml back to me as a string (rather than save it to a file). I am looking into it, but I have not figured it out yet.
I thought this might be really easy for someone familiar with these objects. If not I will figure it out eventually.
Use a StringWriter instead of a 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();
}
}
Note, it is important to use toSerialize.GetType() instead of typeof(T) in XmlSerializer constructor: if you use the first one the code covers all possible subclasses of T (which are valid for the method), while using the latter one will fail when passing a type derived from T.
Here is a link with some example code that motivate this statement, with XmlSerializer throwing an Exception when typeof(T) is used, because you pass an instance of a derived type to a method that calls SerializeObject that is defined in the derived type's base class: http://ideone.com/1Z5J1.
Also, Ideone uses Mono to execute code; the actual Exception you would get using the Microsoft .NET runtime has a different Message than the one shown on Ideone, but it fails just the same.
Serialize and Deserialize XML/JSON (SerializationHelper.cs):
using Newtonsoft.Json;
using System.IO;
using System.Xml.Serialization;
namespace MyProject.Helpers
{
public static class SerializationHelper
{
public static T DeserializeXml<T>(this string toDeserialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader textReader = new StringReader(toDeserialize))
{
return (T)xmlSerializer.Deserialize(textReader);
}
}
public static string SerializeXml<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 DeserializeJson<T>(this string toDeserialize)
{
return JsonConvert.DeserializeObject<T>(toDeserialize);
}
public static string SerializeJson<T>(this T toSerialize)
{
return JsonConvert.SerializeObject(toSerialize);
}
}
}
I know this is not really an answer to the question, but based on the number of votes for the question and the accepted answer, I suspect the people are actually using the code to serialize an object to a string.
Using XML serialization adds unnecessary extra text rubbish to the output.
For the following class
public class UserData
{
public int UserId { get; set; }
}
it generates
<?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>
Better solution is to use JSON serialization (one of the best is Json.NET).
To serialize an object:
var userData = new UserData {UserId = 0};
var userDataString = JsonConvert.SerializeObject(userData);
To deserialize an object:
var userData = JsonConvert.DeserializeObject<UserData>(userDataString);
The serialized JSON string would look like:
{"UserId":0}
Code Safety Note
Regarding the accepted answer, it is important to use toSerialize.GetType() instead of typeof(T) in XmlSerializer constructor: if you use the first one the code covers all possible scenarios, while using the latter one fails sometimes.
Here is a link with some example code that motivate this statement, with XmlSerializer throwing an Exception when typeof(T) is used, because you pass an instance of a derived type to a method that calls SerializeObject<T>() that is defined in the derived type's base class: http://ideone.com/1Z5J1. Note that Ideone uses Mono to execute code: the actual Exception you would get using the Microsoft .NET runtime has a different Message than the one shown on Ideone, but it fails just the same.
For the sake of completeness I post the full code sample here for future reference, just in case Ideone (where I posted the code) becomes unavailable in the future:
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();
}
}
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());
}
}
}
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();
}
}
I felt a like I needed to share this manipulated code to the accepted answer - as I have no reputation, I'm unable to comment..
using System;
using System.Xml.Serialization;
using System.IO;
namespace ObjectSerialization
{
public static class ObjectSerialization
{
// THIS: (C): https://stackoverflow.com/questions/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);
}
}
}
}
I was unable to use the JSONConvert method suggested by xhafan
In .Net 4.5 even after adding the "System.Web.Extensions" assembly reference I was still unable to access the JSONConvert.
However, once you add the reference you can get the same string print out using:
JavaScriptSerializer js = new JavaScriptSerializer();
string jsonstring = js.Serialize(yourClassObject);
In some rare cases you might want to implement your own String serialization.
But that probably is a bad idea unless you know what you are doing. (e.g. serializing for I/O with a batch file)
Something like that would do the trick (and it would be easy to edit by hand/batch), but be careful that some more checks should be done, like that name doesn't contain a newline.
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;
}
[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;
}
Related
Currently I try to write a generic XML parser and having trouble to write a generic Parser Class.
My current Parser:
public class XmlFileLoader
{
public T GetDeserializedData<T>(Type targetType, string rootElementName, string filename)
where T: class
{
XmlDocument doc = new XmlDocument();
XmlTextReader reader = new XmlTextReader(GetPath(filename));
reader.Read();
doc.Load(reader);
T result = DatabaseXmlSerializer.DeserializeXmlString<T>(doc.InnerXml, rootElementName, targetType);
return result;
}
}
My Deserializer:
public static class DatabaseXmlSerializer
{
public static T DeserializeXmlString<T>(string XmlString, string RootElementName , Type TargetType)
{
T tempObject = default;
using (MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(XmlString)))
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = RootElementName;
xRoot.IsNullable = true;
XmlSerializer xs = new XmlSerializer(TargetType, xRoot);
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
tempObject = (T)xs.Deserialize(memoryStream);
}
return tempObject;
}
}
My call:
var loader= new XmlFileLoader();
var books = loader.GetDeserializedData<List<MySolution.Book>>(typeof(List<MySolution.Book>), "Bookstore", "Books.xml");
What is my concern?
I have to pass the type twice, but somehow i can't figure out how to just write it with one type.
I want my call to be like this:
var loader= new XmlFileLoader();
var books = loader.GetDeserializedData<List<MySolution.Book>>("Bookstore", "Books.xml");
There are so many errors in your code that I don't know what to do.
It's easier to rewrite the code completely.
public class XmlFileLoader
{
public T GetData<T>(string rootElementName, string filename)
{
var xmlSerializer = new XmlSerializer(typeof(T),
new XmlRootAttribute(rootElementName));
using (var xmlReader = XmlReader.Create(filename))
return (T)xmlSerializer.Deserialize(xmlReader);
}
}
This is the whole code!
Use it:
var xmlFileLoader = new XmlFileLoader();
var someModel = xmlFileLoader.GetData<SomeModel>("root", "filename");
The deserialization code is so simple that you can just throw out this class and just use XmlSerializer directly where you need it.
However, there is a serious problem when using XmlRootAttribute: multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. See documentation: Dynamically Generated Assemblies.
Therefore, it makes sense to cache serializer instances inside our class. Then its presence becomes justified.
public class XmlFileLoader
{
private static readonly Dictionary<(Type, string), XmlSerializer> serializers
= new Dictionary<(Type, string), XmlSerializer>();
public T GetData<T>(string rootElementName, string filename)
{
var key = (typeof(T), rootElementName);
if (!serializers.TryGetValue(key, out XmlSerializer xmlSerializer))
{
xmlSerializer = new XmlSerializer(typeof(T),
new XmlRootAttribute(rootElementName));
serializers.Add(key, xmlSerializer);
}
using (var xmlReader = XmlReader.Create(filename))
return (T)xmlSerializer.Deserialize(xmlReader);
}
}
I need to remove all of the xmlns attributes from every element. I am using the XmlSerializer class in c#.
Here is a sample of the XML I am getting from serializing an object returned by a webservice.
Code for serialization
public static string ToXML<T>(object obj,string nameSpace)
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
var xml = "";
using (var stringwriter = new StringWriter())
{
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringwriter, obj,ns);
xml = xml + stringwriter;
}
return xml;
}
Calling Code
var unitInfo = RC.GetUnitInfo(txtUnitNum.Text);
var x = XML.DocumentExtensions.ToXML<Vehicles>(unitInfo, "");
Result
<Vehicles>
<Unit xmlns="http://Entities/2006/09">430160</Unit>
<VinNumber xmlns="http://Entities/2006/09">1FUJGP9337</VinNumber>
<CustName xmlns="http://Entities/2006/09">Ryder : N/A</CustName>
<CustCode xmlns="http://Entities/2006/09">4199</CustCode>
<NationalAccountFVM xmlns="http://Entities/2006/09">0</NationalAccountFVM>
<VehMake xmlns="http://Entities/2006/09">FREIGHTLINER/MERCEDES</VehMake>
<VehModel xmlns="http://Entities/2006/09">PX12564ST CASCADIA</VehModel>
<VehYear xmlns="http://Entities/2006/09">2012</VehYear>
<VehDescrip xmlns="http://Entities/2006/09">TRACTOR</VehDescrip>
<InService xmlns="http://Entities/2006/09">10/28/2011 12:00:00 AM</InService>
<EngMake xmlns="http://Entities/2006/09">CUMMINS ENGINE CO.</EngMake>
<EngModel xmlns="http://Entities/2006/09">ISX'10 14.9 450/1800</EngModel>
<EngSize xmlns="http://Entities/2006/09">450</EngSize>
<EngSerial xmlns="http://Entities/2006/09">79502</EngSerial>
<TransMake xmlns="http://Entities/2006/09">FULLER TRANS., DIV. EATON</TransMake>
</Vehicles
I need to serialize the object without getting the xmlns attributes.
Vehicle Object
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34234")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://Entities/2006/09")]
public partial class Vehicles {
private string unitField;
private string vinNumberField;
/// <remarks/>
public string Unit {
get {
return this.unitField;
}
set {
this.unitField = value;
}
}
/// <remarks/>
public string VinNumber {
get {
return this.vinNumberField;
}
set {
this.vinNumberField = value;
}
}
}
I modified your ToXML() method below to always correctly exclude a namespace.
The main difference is how the XmlSerializerNamespaces is instantiated.
Generating an empty namespace disables the inserting of namespaces by the XmlWriter into the output XML.
I also change how you build the output XML since string concatenation is more resource intensive than utilizing a StringBuilder in conjuction with a StringWriter.
public static string ToXML<T>(object obj)
{
StringBuilder outputXml = new StringBuilder();
using (var stringwriter = new StringWriter(outputXml))
{
// Define a blank/empty Namespace that will allow the generated
// XML to contain no Namespace declarations.
XmlSerializerNamespaces emptyNS = new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") });
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringwriter, obj, emptyNS);
}
return outputXml.ToString();
}
Passing in a sample object, I get the output XML of
<?xml version="1.0" encoding="utf-16"?>
<Vehicles>
<Unit>430160</Unit>
<VinNumber>1FUJGP9337</VinNumber>
<CustName>Ryder : N/A</CustName>
<CustCode>4199</CustCode>
<NationalAccountFVM>0</NationalAccountFVM>
<VehMake>FREIGHTLINER/MERCEDES</VehMake>
<VehModel>PX12564ST CASCADIA</VehModel>
<VehYear>2012</VehYear>
<VehDescrip>TRACTOR</VehDescrip>
<InService>2011-10-28T00:00:00</InService>
<EngMake>CUMMINS ENGINE CO.</EngMake>
<EngModel>ISX'10 14.9 450/1800</EngModel>
<EngSize>450</EngSize>
<EngSerial>79502</EngSerial>
<TransMake>FULLER TRANS., DIV. EATON</TransMake>
</Vehicles>
I have the following method to save an Object to a file:
// 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();
}
I confess I did not write it (I only converted it to a extension method that took a type parameter).
Now I need it to give the xml back to me as a string (rather than save it to a file). I am looking into it, but I have not figured it out yet.
I thought this might be really easy for someone familiar with these objects. If not I will figure it out eventually.
Use a StringWriter instead of a 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();
}
}
Note, it is important to use toSerialize.GetType() instead of typeof(T) in XmlSerializer constructor: if you use the first one the code covers all possible subclasses of T (which are valid for the method), while using the latter one will fail when passing a type derived from T.
Here is a link with some example code that motivate this statement, with XmlSerializer throwing an Exception when typeof(T) is used, because you pass an instance of a derived type to a method that calls SerializeObject that is defined in the derived type's base class: http://ideone.com/1Z5J1.
Also, Ideone uses Mono to execute code; the actual Exception you would get using the Microsoft .NET runtime has a different Message than the one shown on Ideone, but it fails just the same.
Serialize and Deserialize XML/JSON (SerializationHelper.cs):
using Newtonsoft.Json;
using System.IO;
using System.Xml.Serialization;
namespace MyProject.Helpers
{
public static class SerializationHelper
{
public static T DeserializeXml<T>(this string toDeserialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader textReader = new StringReader(toDeserialize))
{
return (T)xmlSerializer.Deserialize(textReader);
}
}
public static string SerializeXml<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 DeserializeJson<T>(this string toDeserialize)
{
return JsonConvert.DeserializeObject<T>(toDeserialize);
}
public static string SerializeJson<T>(this T toSerialize)
{
return JsonConvert.SerializeObject(toSerialize);
}
}
}
I know this is not really an answer to the question, but based on the number of votes for the question and the accepted answer, I suspect the people are actually using the code to serialize an object to a string.
Using XML serialization adds unnecessary extra text rubbish to the output.
For the following class
public class UserData
{
public int UserId { get; set; }
}
it generates
<?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>
Better solution is to use JSON serialization (one of the best is Json.NET).
To serialize an object:
var userData = new UserData {UserId = 0};
var userDataString = JsonConvert.SerializeObject(userData);
To deserialize an object:
var userData = JsonConvert.DeserializeObject<UserData>(userDataString);
The serialized JSON string would look like:
{"UserId":0}
Code Safety Note
Regarding the accepted answer, it is important to use toSerialize.GetType() instead of typeof(T) in XmlSerializer constructor: if you use the first one the code covers all possible scenarios, while using the latter one fails sometimes.
Here is a link with some example code that motivate this statement, with XmlSerializer throwing an Exception when typeof(T) is used, because you pass an instance of a derived type to a method that calls SerializeObject<T>() that is defined in the derived type's base class: http://ideone.com/1Z5J1. Note that Ideone uses Mono to execute code: the actual Exception you would get using the Microsoft .NET runtime has a different Message than the one shown on Ideone, but it fails just the same.
For the sake of completeness I post the full code sample here for future reference, just in case Ideone (where I posted the code) becomes unavailable in the future:
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();
}
}
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());
}
}
}
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();
}
}
I felt a like I needed to share this manipulated code to the accepted answer - as I have no reputation, I'm unable to comment..
using System;
using System.Xml.Serialization;
using System.IO;
namespace ObjectSerialization
{
public static class ObjectSerialization
{
// THIS: (C): https://stackoverflow.com/questions/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);
}
}
}
}
I was unable to use the JSONConvert method suggested by xhafan
In .Net 4.5 even after adding the "System.Web.Extensions" assembly reference I was still unable to access the JSONConvert.
However, once you add the reference you can get the same string print out using:
JavaScriptSerializer js = new JavaScriptSerializer();
string jsonstring = js.Serialize(yourClassObject);
In some rare cases you might want to implement your own String serialization.
But that probably is a bad idea unless you know what you are doing. (e.g. serializing for I/O with a batch file)
Something like that would do the trick (and it would be easy to edit by hand/batch), but be careful that some more checks should be done, like that name doesn't contain a newline.
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;
}
[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;
}
How do I serialize an XML-serializable object to an XML fragment (no XML declaration nor namespace references in the root element)?
Here is a hack-ish way to do it without having to load the entire output string into an XmlDocument:
using System;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
public class Example
{
public String Name { get; set; }
static void Main()
{
Example example = new Example { Name = "Foo" };
XmlSerializer serializer = new XmlSerializer(typeof(Example));
XmlSerializerNamespaces emptyNamespace = new XmlSerializerNamespaces();
emptyNamespace.Add(String.Empty, String.Empty);
StringBuilder output = new StringBuilder();
XmlWriter writer = XmlWriter.Create(output,
new XmlWriterSettings { OmitXmlDeclaration = true });
serializer.Serialize(writer, example, emptyNamespace);
Console.WriteLine(output.ToString());
}
}
You should be able to just serialize like you usually do, and then use the Root property from the resulting document.
You may need to clear the attributes of the element first.
By the way this is awesome.
I implemented this code to make it easy to work with xml fragments as classes quickly and then you can just replace the node when finished. This makes the transition between code and xml ultra-easy.
First create some extension methods.
public static class SerializableFragmentExtensions
{
public static XElement ToElement(this ISerializableFragment iSerializableFragment)
{
var serializer = new XmlSerializer(iSerializableFragment.GetType());
var emptyNamespace = new XmlSerializerNamespaces();
emptyNamespace.Add(String.Empty, String.Empty);
var output = new StringBuilder();
var writer = XmlWriter.Create(output,
new XmlWriterSettings { OmitXmlDeclaration = true });
serializer.Serialize(writer, iSerializableFragment, emptyNamespace);
return XElement.Parse(output.ToString(), LoadOptions.None);
}
public static T ToObject<T>(this XElement xElement)
{
var serializer = new XmlSerializer(typeof (T));
var reader = xElement.CreateReader();
var obj = (T) serializer.Deserialize(reader);
return obj;
}
}
Next Implement the required interface (marker interface--I know you are not supposed to but I think this is the perfect reason to it.)
public interface ISerializableFragment
{
}
Now all you have to do is decorate any Serializable class, you want to convert to an XElement Fragment, with the interface.
[Serializable]
public class SomeSerializableClass : ISerializableFragment
{
[XmlAttribute]
public string SomeData { get; set; }
}
Finally test the code.
static void Main(string[] args)
{
var someSerializableClassObj = new SomeSerializableClass() {SomeData = "Testing"};
var element = someSerializableClass.ToElement();
var backToSomeSerializableClassObj = element.ToObject<SomeSerializableClass>();
}
Thanks again for this amazingly useful code.
If you run this code:
public class Program
{
public class MyClass
{
public string Text { get; set; }
}
static MyClass myClass = new MyClass();
static string Desktop = "C:\\Users\\Juan Luis\\Desktop\\";
static void Main(string[] args)
{
myClass.Text = "\r\nhello";
Console.WriteLine((int)myClass.Text[0]);
Save(Desktop + "file.xml", myClass);
myClass = Load(Desktop + "file.xml");
Console.WriteLine((int)myClass.Text[0]);
Console.Read();
}
private static MyClass Load(string fileName)
{
MyClass result = null;
using (Stream stream = File.Open(fileName, FileMode.Open))
{
XmlSerializer xmlFormatter = new XmlSerializer(typeof(MyClass));
result = (MyClass)xmlFormatter.Deserialize(stream);
}
return result;
}
private static void Save(string fileName, MyClass obj)
{
using (Stream tempFileStream = File.Create(fileName))
{
XmlSerializer xmlFormatter = new XmlSerializer(typeof(MyClass));
xmlFormatter.Serialize(tempFileStream, obj);
}
}
}
The output will be 13, 10. The XmlSerializer is removing the carriage return. This is a problem in my case because I need to compare strings for equality in a class that gets serialized and deserialized, and this is causing two strings that are equal before serializing to be unequal after serialized. What would be the best work around?
Edit: After reading answers, this was my solution, in case it will help anyone:
public class SafeXmlSerializer : XmlSerializer
{
public SafeXmlSerializer(Type type) : base(type) { }
public new void Serialize(Stream stream, object o)
{
XmlWriterSettings ws = new XmlWriterSettings();
ws.NewLineHandling = NewLineHandling.Entitize;
using (XmlWriter xmlWriter = XmlWriter.Create(stream, ws))
{
base.Serialize(xmlWriter, o);
}
}
}
I wouldn't call it unreliable exactly: the XmlSerializer strips white space around text inside elements. If it didn't do this then the meaning of XML documents would change according to how you formatted them in the IDE.
You could consider putting the text in a CDATA section, which will preserve the contents exactly. For example, How do you serialize a string as CDATA using XmlSerializer?
Edit: This looks to have a better explanation of where the problem lies, along with a simpler solution - How to keep XmlSerializer from killing NewLines in Strings?