1

My C# application has method that takes a list of objects (inputList) as parameter, it creates an XML string using the TextWriter and XMLWriter with the code below and submits to a webservice.

using (TextWriter writer = new Utf8StringWriter())
{
    using (XmlWriter xw = XmlWriter.Create(writer, settings))
    {
        xw.WriteStartElement("submission");
        xw.WriteElementString("version", XMLversion);
        xw.WriteElementString("user", USER_NAME);

        foreach (var obj in inputList)
        {
            xw.WriteStartElement("node");
            xw.WriteElementString("data1", obj.data1.ToString());
            xw.WriteElementString("data2", obj.data2.ToString());
            xw.WriteElementString("data3", obj.data3.ToString());
            xw.WriteElementString("data4", obj.data4.ToString()); 
            xw.WriteEndElement();
        }

    }
    xmlFile = writer.ToString();
}

One of the requirements to to log the submission for each item in the list individual. So I'd like to know if there's a more efficient way to create a string of the XML node within the foreach loop?

I've considered using the XMLReader with the string afterwards but that's a whole new process and while I know I can create it manually, and am happy to do so, I am open to other suggestions. In essence, I'm looking for an efficient technique to generate a string as illustrated below:

<node>
    <data1>obj.data1.ToString()</data1>
    <data2>obj.data2.ToString()</data2>
    <data3>obj.data3.ToString()</data3>
    <data4>obj.data4.ToString()</data4>
</node>
Daniel
  • 2,167
  • 5
  • 23
  • 44
  • 1
    Have you tried simply serializing the object? – DiskJunky Feb 28 '18 at 14:18
  • I recommend use Linq-to-XML to generate XML document. The MSDN link is: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml – diberium Feb 28 '18 at 14:23
  • Don't be tempted to just generate a string in barnyard fashion with a StringBuilder, XmlWriter takes care of many important things like byte encoding and translation of input character like '>' to '>' so that the resulting stream is well-formed. – Jono Feb 28 '18 at 14:26
  • I like the idea of serializing it @DiskJunky, will try that. After all, the data is stored as a blob in the log so who cares? – Daniel Mar 01 '18 at 12:49
  • Thanks @diberium I don't see how that would address my query though. – Daniel Mar 01 '18 at 12:52
  • @Jono I'm not sure what you're suggesting here, I'm already using XmlWriter and am only looking to log one node generated in a loop. The answer suggested below has that taken care of, I just want to know if there are performance / efficiency considerations when using that approach – Daniel Mar 01 '18 at 12:54
  • Your C# code sample calls writer.ToString() but the XML fragment shows only the elements from node and its children. My intention was to alert you to the pitfalls of treating XML as strings if you choose high performance over correctness. Fragments of XML cannot simply be extracted textually without losing context like namespaces or encodings. – Jono Mar 01 '18 at 19:17
  • Thanks for the feedback @Jono, I’ll take it all under consideration. Much appreciated – Daniel Mar 02 '18 at 04:28

1 Answers1

0

Your XML document does not seem large enough to warrant using XmlWriter and XmlReader. Assuming it can fit comfortably in memory then I'd use XmlDocument, which allows you to get a node's OuterXml (or InnerXml if you prefer) as a string.

var document = new XmlDocument();

var submission = document.CreateElement("submission");
document.AppendChild(submission);

var version = document.CreateElement("version");
submission.AppendChild(version);
var versionText = document.CreateTextNode(XMLversion);
version.AppendChild(versionText);

var user = document.CreateElement("user");
submission.AppendChild(user);
var userText = document.CreateTextNode(USER_NAME);
user.AppendChild(userText);

foreach (var obj in inputList)
{
    var node = document.CreateElement("node");
    submission.AppendChild(node);

    var data1 = document.CreateElement("data1");
    node.AppendChild(data1);
    var data1Text = document.CreateTextNode(obj.data1.ToString());
    data1.AppendChild(data1Text);

    // TODO: repeat last 4 lines for data2, data3 and data4

    Console.WriteLine(node.OuterXml);
}

Console.WriteLine(document.OuterXml);
Jono
  • 1,964
  • 4
  • 18
  • 35
  • This looks promising Jono but there may be instances where the `inputlist` could contain 1,000+ items, will the XMLDocument class manage with that? – Daniel Mar 01 '18 at 12:41
  • One size hardly ever fits all. You will need to find the trade-off point between CPU and memory usage that best suits your own needs. The technique above uses XmlDocument as an in-memory cache; it will handle thousands of elements but at the expense of keeping the whole document in memory for the time that you're using it. XmlWriter on the other hand can write directly to a stream (and it doesn't have to be a FileStream or a MemoryStream). With well-designed code reuse, you could write your object list to two different streams. Which approach is best depends on your environmental constraints. – Jono Mar 01 '18 at 19:07
  • I’ve done some research on the various methods and while yours and a suggested method above using LINQ may be a simpler route, it appears that XmlWriter is the most efficient for my use case. – Daniel Mar 02 '18 at 04:30