358

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.

dtb
  • 213,145
  • 36
  • 401
  • 431
Vaccano
  • 78,325
  • 149
  • 468
  • 850

11 Answers11

613

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.

Matt Klein
  • 7,856
  • 6
  • 45
  • 46
dtb
  • 213,145
  • 36
  • 401
  • 431
  • 2
    @JohnSaunders: ok, it is a good idea to move this discussion on Meta. Here is the [link to the question I just posted on Meta Stack Overflow regarding this edit](http://meta.stackexchange.com/q/135865/188113). – Fulvio Jun 13 '12 at 16:22
  • 37
    @casperOne Guys, please stop messing with my answer. The point is to use StringWriter instead of StreamWriter, everything else is not relevant to the question. If you want to discuss details such as `typeof(T)` versus `toSerialize.GetType()`, please do so, but not in my answer. Thanks. – dtb Jun 14 '12 at 13:47
  • 9
    @dtb Sorry, but [Stack Overflow is collaboratively edited](http://stackoverflow.com/faq#editing). Also, [this specific answer has been discussed on meta](http://meta.stackexchange.com/a/135872/140951), so the edit stands. If you disagree, then please reply to that post on meta on why you think your answer is a special case and *shouldn't* be collaboratively edited. – casperOne Jun 14 '12 at 14:06
  • I found that there is no default constructor for StreamWriter, StringWriter worked though. – Harry Jun 11 '13 at 13:49
  • 2
    Codewise, this is the shortest example I've seen. +1 – froggythefrog Aug 31 '13 at 22:00
  • Oh.... but the reason I don't use this is because the StringBuilder always uses UTF-16 encoding and I am wanting UTF-8. Back to MemoryStream. – froggythefrog Aug 31 '13 at 23:34
  • 15
    StringWriter implements IDisposable, so should be enclosed in a using block. – TrueWill Mar 04 '14 at 16:15
  • Since indeed you should be using `.GetType()` instead of `typeof(T)`, then the generic parameter is completely unnecessary and should be replaced by `object`. – Bruno Brant Dec 27 '16 at 17:34
  • 1
    If struggling with `XmlSerializers failed to load in the 'LoadFrom' binding context of the AppDomain with ID 1` then see solution here: https://stackoverflow.com/a/22187247/5269981 – golfalot Nov 22 '20 at 21:25
  • @TrueWill actually, if you bothered to open the documentation for StringWriter you would see the massive note that says the following: "This type implements the IDisposable interface, but does not actually have any resources to dispose. This means that disposing it by directly calling Dispose() or by using a language construct such as using (in C#) or Using (in Visual Basic) is not necessary." – fabspro Mar 07 '22 at 01:17
  • @dtb The way you have been treated for this answer epitomises what I dislike about SO. People were 'helpfully' making all kinds of edits which were simply, wrong. – fabspro Mar 07 '22 at 01:18
90

Serialize and Deserialize XML/JSON (SerializationHelper.cs) (You can use Newtonsoft.Json Nuget Package for JSON serialization):

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

Alternatively you can use System.Text.Json Nuget Package instead for Json serialization:

using System.Text.Json;

...

public static T DeserializeJson<T>(this string toDeserialize, JsonSerializerOptions options = null)
{
    return JsonSerializer.Deserialize<T>(toDeserialize, options);
}

public static string SerializeJson<T>(this T toSerialize, JsonSerializerOptions options = null)
{
    return JsonSerializer.Serialize<T>(toSerialize, options);
}
ADM-IT
  • 3,719
  • 1
  • 25
  • 26
  • 19
    +1 for also showing how to deserialize, unlike all of the other answers. Thanks! – deadlydog Nov 18 '14 at 15:26
  • 6
    One minor change though would be to return T instead of object, and cast the returned object to T in the DeserializeObject function. This way the strongly typed object is returned instead of a generic object. – deadlydog Nov 18 '14 at 15:29
  • Thanks @deadlydog, I've fixed. – ADM-IT Apr 27 '16 at 18:01
  • 3
    TextWriter has a Dispose() function that should be called. So you are forgetting the Using statements. – Herman Van Der Blom Mar 08 '17 at 09:02
  • I really like this implementation, just wanted to add a thing and things which might cleanup the code: First: The nuget dependency for using `Newtonsoft.Json` is `Newtonsoft.Json` more error resistant: `public static T? DeserializeXml(this string toDeserialize) { var xmlSerializer = new XmlSerializer(typeof(T)); using var sr = new StringReader(toDeserialize); try { return (T?)xmlSerializer.Deserialize(sr); } catch { return default(T); } }` – Netzmensch Aug 30 '23 at 07:29
  • @Netzmensch If you get errors it means something goes wrong so you have to fix it, instead of hiding errors. Never hide errors. . (25 years of coding). – ADM-IT Aug 30 '23 at 15:32
76

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}
xhafan
  • 2,140
  • 1
  • 26
  • 26
  • 6
    In this case you are right but have you seen large XML documents and large JSON documents. The JSON document are hardly readible. The "rubbish" you are talking about like the namespaces can be suppressed. The generated XML can be as clean as JSON but is ALWAYS more readible as JSON. The readability is a big advantage above JSON. – Herman Van Der Blom Mar 08 '17 at 09:00
  • 2
    If you search online for "json online parser" you will find some online json parsers which can format the json string in more human readable way. – xhafan Mar 08 '17 at 14:08
  • OP requires XML not JSON. This is irrelevant. – Jason P Sallinger Aug 06 '21 at 12:56
38

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();
    }
}
Community
  • 1
  • 1
Fulvio
  • 1,615
  • 1
  • 16
  • 18
  • 12
    You should also do `using (StringWriter textWriter = new StringWriter() {}` for proper closing/disposing of object. – Amicable May 16 '13 at 16:08
  • I completely agree with you @Amicable! I've simply tried to keep my code sample as near as possible to the OP one, in order to highlight my point which is all about object types. Anyway its good to remember anyone that the `using` statement is the best friend both to us and to our dear `IDisposable` implementing objects ;) – Fulvio Apr 05 '14 at 18:56
  • "Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type." https://msdn.microsoft.com/en-us/library/bb383977.aspx – Adrian Apr 05 '17 at 19:28
13

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());
                }
            }
        }
oPless
  • 618
  • 1
  • 8
  • 18
  • 2
    +1 for using XmlWriterSettings(). I wanted my serialised XML to not waste space with the pretty print stuff and setting Indent = false and NewLineOnAttributes = false did the job. – Lee Richardson Nov 17 '14 at 15:37
  • Thanks @LeeRichardson - I needed to do the exact opposite, also XmlWriter under .net defaults to UTF16 which isn't what I was writing out to either. – oPless Nov 17 '14 at 18:01
  • using this combination of memorystream and getting it via Encoding GetString will include the Preamble/BOM as the first char in your string. See also https://stackoverflow.com/questions/11701341/encoding-utf8-getstring-doesnt-take-into-account-the-preamble-bom – Jamee May 25 '19 at 12:50
  • @Jamee "Encoding = UTF8Encoding (false)" means don't write the BOM as per https://learn.microsoft.com/en-us/dotnet/api/system.text.utf8encoding.-ctor?view=netframework-4.8 ... has this changed behavior since .net4 ? – oPless Jul 04 '19 at 18:44
  • @oPless you're right I mis interpreted the answer of the link I provided – Jamee Aug 19 '20 at 14:13
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();
            }
        }
TPG
  • 2,811
  • 1
  • 31
  • 52
3

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

David Rogers
  • 2,601
  • 4
  • 39
  • 84
  • I know this is old, but since you gave a really good answer, I'll add a small comment, as if I did a code review on a PR:You should have constraints on T when using generics. It helps keeping things neat, and not every object in a code-base and frameworks referenced lends themselves to serialization – Frank R. Haugen Apr 26 '20 at 14:29
1

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);
  • 2
    JSONConvert Class is in NewtonSoft.Json namespace. Go to package manager in you VS and then download NewtonSoft.Json package – Amir Shrestha Aug 22 '15 at 02:41
-1

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;
}
satibel
  • 290
  • 3
  • 9
-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;
}
Brian
  • 21
  • 1
  • 3
-1

Universal xml, json and bin serializer/deserializer from and to string

public static class SerializationHelper
{

    public static string Serialize<T>(this T toSerialize, OutTypeEnum oType)
    {
        if(oType == OutTypeEnum.JSON)
            return SerializeJson<T>(toSerialize);
        else if(oType == OutTypeEnum.XML)
            return SerializeXml<T>(toSerialize);
        else
            return SerializeBin<T>(toSerialize);
    }

    public static T Deserialize<T>(this string toDeserialize, OutTypeEnum oType)
    {
        if (oType == OutTypeEnum.JSON)
            return DeserializeJson<T>(toDeserialize);
        else if (oType == OutTypeEnum.XML)
            return DeserializeXml<T>(toDeserialize);
        else
            return DeserializeBin<T>(toDeserialize);
    }

    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 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 SerializeJson<T>(this T toSerialize) => JsonSerializer.Serialize(toSerialize);

    public static T DeserializeJson<T>(this string toDeserialize) => (T)JsonSerializer.Deserialize(toDeserialize, typeof(T));

    public static string SerializeBin<T>(this T toSerialize)
    {
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        MemoryStream memStr = new MemoryStream();

        try
        {
            bf.Serialize(memStr, toSerialize);
            memStr.Position = 0;

            return Convert.ToBase64String(memStr.ToArray());
        }
        finally
        {
            memStr.Close();
        }
    }

    public static T DeserializeBin<T>(this 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();
        }
    }
}

public enum OutTypeEnum
{
    JSON,
    XML,
    BIN
}

usage:

SomeClass obj = new SomeClass();
string serializedObject = obj.Serialize(OutTypeEnum.JSON);

SomeClass newObj = serializedObject.Deserialize<SomeClass>(OutTypeEnum.JSON);
  • Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? – Jeremy Caney Apr 18 '23 at 01:12