3

I have been using this handy function to "beautify" an xml document, so that it's formatted with indentations and newlines. But with bigger documents (~1MB), I get an OutOfMemoryException at doc.Save(writer) for some reason. How can I fix this issue?

public static string BeautifyXmlDocument(XmlDocument doc)
{
    MemoryStream sb = new MemoryStream();
    XmlWriterSettings s = new XmlWriterSettings();
    s.Indent = true;
    s.IndentChars = "  ";
    s.NewLineChars = "\r\n";
    s.NewLineHandling = NewLineHandling.Replace;
    s.Encoding = new UTF8Encoding(false);
    XmlWriter writer = XmlWriter.Create(sb, s);
    doc.Save(writer);
    writer.Close();
    return Encoding.UTF8.GetString(sb.ToArray());
}

Stack trace:

at System.IO.MemoryStream.set_Capacity(Int32 value)
at System.IO.MemoryStream.EnsureCapacity(Int32 value)
at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at System.Xml.XmlUtf8RawTextWriter.FlushBuffer()
at System.Xml.XmlUtf8RawTextWriter.RawText(Char* pSrcBegin, Char* pSrcEnd)
at System.Xml.XmlUtf8RawTextWriter.RawText(String s)
at System.Xml.XmlUtf8RawTextWriter.WriteFullEndElement(String prefix, String localName, String ns)
at System.Xml.XmlUtf8RawTextWriterIndent.WriteFullEndElement(String prefix, String localName, String ns)
at System.Xml.XmlWellFormedWriter.WriteFullEndElement()
at System.Xml.XmlElement.WriteTo(XmlWriter w)
at System.Xml.XmlElement.WriteContentTo(XmlWriter w)
at System.Xml.XmlElement.WriteTo(XmlWriter w)
at System.Xml.XmlElement.WriteContentTo(XmlWriter w)
at System.Xml.XmlElement.WriteTo(XmlWriter w)
at System.Xml.XmlElement.WriteContentTo(XmlWriter w)
at System.Xml.XmlElement.WriteTo(XmlWriter w)
at System.Xml.XmlElement.WriteContentTo(XmlWriter w)
at System.Xml.XmlElement.WriteTo(XmlWriter w)
at System.Xml.XmlElement.WriteContentTo(XmlWriter w)
at System.Xml.XmlElement.WriteTo(XmlWriter w)
at System.Xml.XmlDocument.Save(XmlWriter w)

2 Answers 2

1

Maybe try returning your string before closing the stream, The "using" statement can help here. This seems to work fine for a 5MB xml file.

  public static string BeautifyXmlDocument(XmlDocument doc)
        {
            using (MemoryStream sb = new MemoryStream())
            {
                XmlWriterSettings s = new XmlWriterSettings();
                s.Indent = true;
                s.IndentChars = "  ";
                s.NewLineChars = "\r\n";
                s.NewLineHandling = NewLineHandling.Replace;
                s.Encoding = new UTF8Encoding(false);
                using (XmlWriter writer = XmlWriter.Create(sb, s))
                {
                    doc.Save(writer);
                    return Encoding.UTF8.GetString(sb.ToArray());
                }
            }
        }
Sign up to request clarification or add additional context in comments.

Comments

1

As long as your implementation requires getting a full string of the document, someone can always create an XmlDocument that will cause an OutOfMemoryException. To really get around this, you'll need to move to using Stream objects (any TextWriter for example) so that only a small portion of the document is ever really in memory at any given time. If you give more details about how you use your beautified string, I or someone else could probably come up with something that could help.

If, on the other hand, you can't change your implementation to avoid having the entire string in memory, you should change your posted implementation to use a StringWriter and pass that to XmlWriter.Create instead. This at least gets rid of some memory pressure since you won't have the MemoryStream and the final string in memory at the same time:

public static string BeautifyXmlDocument(XmlDocument doc)
{
    using (StringWriter sw = new StringWriter())
    {
        XmlWriterSettings s = new XmlWriterSettings();
        s.Indent = true;
        s.IndentChars = "  ";
        s.NewLineChars = "\r\n";
        s.NewLineHandling = NewLineHandling.Replace;
        s.Encoding = new UTF8Encoding(false);
        using (XmlWriter writer = XmlWriter.Create(sw, s))
        {
            doc.Save(writer);
        }
        return sw.ToString();
    }
}

3 Comments

I tried your function and the one above, but both still give OutOfMemoryExceptions. In your function, sw.ToString() gives the memory error now. One thing I didn't mention is that my program loads about 1.5GB of data into memory. So, I suspect that maybe having an impact on things. The XML is just a configuration file that I save the filename locations of the data with other parameters.
I could just serialize my configuration straight into an xml file, but people want to look at it and modify it with a text editor, so I need to beautify it first.
Unless the Xml document is big (I've personally found files around 125 meg loaded into XmlDocument is about the limit with a 2 gig address space), I'd look at reducing memory usage in other areas. This is likely the straw breaking the camel's back. As for showing it on the GUI, ugh, I don't have an answer for you there. I'd like to know of a memory-efficient way to do that myself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.