简体   繁体   中英

C# fastest way to serialise object to xml string

Background: I have been tasked with serialising C# objects into xml string. The xml string is then passed to webservice and are written to disk in xml file. The task of serialising needs to occur within 5 mins timeframe that the process gets. The consumer webservice only accepts string as xml. I have been researching into various ways of creating xml string from xml serialiser, xmlwriter, xdocument, stringbuilder to write xml string, object to json to xml, linq to xml but I needed to know if anyone had experience of doing something similar. Main aim is to have a high performant xml string that is not so verbose and error prone like creating xml in string.

My object is Called Employee and has 18 string/date properties. The objects are created in memory and we get around 4000k objects in total once the process boots up. The process runs for 1 hour a day, loads data from data file and creates person objects. A number of functions are performed on the objects. Once objects are ready, they need to be serialised and data in xml is sent to webservice and is writren to xml file. So in short, these objects need to be serialised and saved to disk and sent to webservice.

Does anyone recommend any high performant yet easy to. Maintain approach? Apologies for not positing any code because I can create a class and add xml serialiser etc code but i dont think it will add any value at the moment as currently I am looking for past experiences plus i want to ensure i dont go on a wild goose chase and want to implement with right solution.

I have tried following serialiser code but it takes 10+ mins to serialise all 4000k objects.

public static bool Serialize<T>(T value, ref string serializeXml)
{
    if (value == null)
    {
        return false;
    }
    try
    {
        XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
        StringWriter stringWriter = new StringWriter();
        XmlWriter writer = XmlWriter.Create(stringWriter);

        xmlserializer.Serialize(writer, value);

        serializeXml = stringWriter.ToString();

        writer.Close();
        return true;
    }
    catch (Exception ex)
    {
        return false;
    }
}

I have also tried caching serialiser but doesn't give any performance improvements

According to your requirement, speed is the most demanding part. We need to write a benchmark here. As mentioned in the comment, besides XmlSerializer , we can use DataContractSerializer for our purpose. There are several Q&A related to the difference between these two, eg:

  1. DataContractSerializer vs XmlSerializer: Pros and Cons of each serializer
  2. Linq to Xml VS XmlSerializer VS DataContractSerializer
  3. Difference between DataContractSerializer vs XmlSerializer

Another options are manually write your XML either using StringBuilder or XmlWriter . Although in the requirement you mentioned:

Main aim is to have a high performant xml string that is not so verbose and error prone like creating xml in string

these three serializer is added for comparison. Of course, in the case of StringBuilder , the text must be escaped. Here, I used System.Security.SecurityElement.Escape . The object to be serialized looks like:

//Simple POCO with 11 String properties, 7 DateTime properties
[DataContractAttribute()]
public class Employee
{
    [DataMember()]
    public string FirstName { set; get; }
    [DataMember()]
    public string LastName { set; get; }
    //...omitted for clarity
    [DataMember()]
    public DateTime Date03 { set; get; }
    [DataMember()]
    public DateTime Date04 { set; get; }
}

and all the properties have value (not null), assigned prior to calling the serializer. The serializer codes looks like:

    //Serialize using XmlSerializer
public static bool Serialize<T>(T value, ref StringBuilder sb)
{
    if (value == null)
        return false;
    try
    {
        XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
        using (XmlWriter writer = XmlWriter.Create(sb))
        {
            xmlserializer.Serialize(writer, value);
            writer.Close();
        }
        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
        return false;
    }
}

//Serialize using DataContractSerializer
public static bool SerializeDataContract<T>(T value, ref StringBuilder sb)
{
    if (value == null)
        return false;
    try
    {
        DataContractSerializer xmlserializer = new DataContractSerializer(typeof(T));
        using (XmlWriter writer = XmlWriter.Create(sb))
        {
            xmlserializer.WriteObject(writer, value);
            writer.Close();
        }
        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
        return false;
    }
}

//Serialize using StringBuilder
public static bool SerializeStringBuilder(Employee obj, ref StringBuilder sb)
    {
        if (obj == null)
            return false;

        sb.Append(@"<?xml version=""1.0"" encoding=""utf-16""?>");
        sb.Append("<Employee>");

        sb.Append("<FirstName>");
        sb.Append(SecurityElement.Escape(obj.FirstName));
        sb.Append("</FirstName>");

        //... Omitted for clarity

        sb.Append("</Employee>");

        return true;
    }

//Serialize using XmlSerializer (manually add elements)
public static bool SerializeManual(Employee obj, ref StringBuilder sb)
{
    if (obj == null)
        return false;

    try
    {
        using (var xtw = XmlWriter.Create(sb))
        {
            xtw.WriteStartDocument();
            xtw.WriteStartElement("Employee");

            xtw.WriteStartElement("FirstName");
            xtw.WriteString(obj.FirstName);
            xtw.WriteEndElement();

            //...Omitted for clarity

            xtw.WriteEndElement();
            xtw.WriteEndDocument();

            xtw.Close();
        }
        return true;
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex);
        return false;
    }
}

In the benchmark, 4M Employee objects are given as the argument and XML is written to preallocated StringBuilder (parameter ref StringBuilder sb ). For DataContractSerializer and Manual XmlWriter , benchmark with Parallel.Invoke (3 parallel tasks) also performed. Required processing time for each serializer:

//Simple POCO with 11 String properties, 7 DateTime properties
XmlSerializer         =00:02:37.8151125 = 157 sec: 100% (reference)
DataContractSerializer=00:01:10.3384361 = 70  sec: 45% (3-Parallel: 47sec = 30%)
StringBuilder         =00:01:22.5742122 = 82  sec: 52%
Manual XmlWriter      =00:00:57.8436860 = 58  sec: 37% (3-Parallel: 40sec = 25%)

Environment: .Net Framework 4.5.2, Intel(R) Core(TM) i5-3337U @ 1.80GHz 1.80GHz, Windows 10, 6.0GB Memory . I expect StringBuilder will be the fastest, but it wasn't. Perhaps, the bottleneck is in System.Security.SecurityElement.Escape() .

The conclusion: DataContractSerializer is within the requirement, processing time is 30-45% compared to XmlSerializer . The results may differs depend on the environment, and you should make your own benchmark.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM