Question

I'm trying to solve a similar problem to that I described at How to check name of element with WriteEndElement

I wrote this code with an inner and outer XmlWriter. The idea is that closing the inner XmlWriter will also close any tags left hanging by the not-perfectly-reliable third party library.

var sb = new StringWriter();

using (var xml = XmlWriter.Create(sb, new XmlWriterSettings(){Indent = true}))
{
    xml.WriteStartElement("root");

    using (var inner = XmlWriter.Create(xml))
    {
        inner.WriteStartElement("payload1");

        // simulate ThirdPartyLibrary.Serialise(results, inner) leaving a tag open
        inner.WriteStartElement("third-party-stuff");
    }

    xml.WriteStartElement("payload2");
}

sb.ToString().Dump();

I expect this to produce

<root>
  <payload1>
    <third-party-stuff />
  </payload1>
  <payload2 />
</root>

But instead I get a run-time error at the line that should write <payload2>

InvalidOperationException
The Writer is closed or in error state.

Why do I get this error? I didn't expect closing the inner XmlWriter to close the outer one.

Was it helpful?

Solution

Using the variable names in your code sample closing the XmlWriter inner will release the (outer) XmlWriter xml. This is the expected behaviour as according to this MSDN entry disposing the XmlWriter "releases all resources used by the current instance of the XmlWriter class" ie it will release any "outer" XMLWriter it was created with.

Such code will generate code analysis error CA2202. A resolution to such situations is described in the following link.

OTHER TIPS

Check out what happens when you create another XmlWriter from xml:

static void Main(string[] args)
{
    var sb = new StringWriter();
    using (var xml = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true }))
    {
        xml.WriteStartElement("root");
        using (var inner = XmlWriter.Create(xml))
        {
            Debug.WriteLine(Object.ReferenceEquals(xml, inner));
            //UH OH! Returns true
            inner.WriteStartElement("payload1");
            // simulate ThirdPartyLibrary.Serialise(results, inner) leaving a tag open
            inner.WriteStartElement("third-party-stuff");
        }
        xml.WriteStartElement("payload2");
    }
    sb.ToString().Dump();
}

See? So when you dispose of inner, you're actually disposing of xml, because they are the same object.

If you check MSDN: (http://msdn.microsoft.com/en-us/library/77t6e4w3.aspx)

Return Value Type:

System.Xml.XmlWriter An XmlWriter object that is

wrapped around the specified XmlWriter object.

So I assume it's using the same object.

EDIT Here, a solution you can use:

static void Main(string[] args)
    {
        var sb = new StringWriter();
        var sb2 = new StringWriter();

        using (var xml = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true }))
        {
            xml.WriteStartElement("root");

            using (var inner = XmlWriter.Create(sb2, new XmlWriterSettings() {Indent = true , CloseOutput=true, OmitXmlDeclaration=true}))
            {
                
                inner.WriteStartElement("payload1");
                inner.WriteStartElement("third-party-stuff");
            }

            xml.WriteRaw(sb2.ToString());

            xml.WriteStartElement("payload2");
        }

        Debug.WriteLine(sb.ToString());            
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top