Как работать с XmlWriter внутри другого?

Я пытаюсь решить проблему, аналогичную описанной на странице Как проверьте имя элемента с помощью WriteEndElement

Я написал этот код с помощью внутреннего и внешнего XmlWriter. Идея состоит в том, что закрытие внутреннего XmlWriter также закроет все теги, оставленные не совсем надежной сторонней библиотекой.

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

Я ожидаю, что это произведет

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

Но вместо этого я получаю ошибку времени выполнения в строке, которая должна писать <payload2>

InvalidOperationException Скриншот, модуль записи закрыт или находится в состоянии ошибки.

Почему я получаю эту ошибку? Я не ожидал, что закрытие внутреннего XmlWriter приведет к закрытию внешнего.


person Colonel Panic    schedule 05.04.2013    source источник
comment
Вы уверены, что он не закрывается внутри вашей ThirdPartyLibrary.Serialise (results, inner); вызов метода?   -  person Richard Hooper    schedule 05.04.2013
comment
@Penfold хороший замечание. Изменил мой пример выше, чтобы вы могли его запустить.   -  person Colonel Panic    schedule 05.04.2013


Ответы (2)


Использование имен переменных в образце кода, закрывающее внутренний XmlWriter, освободит (внешний) XmlWriter xml. Это ожидаемое поведение согласно этой записи MSDN, удаляющей XmlWriter " освобождает все ресурсы, используемые текущим экземпляром класса XmlWriter, т.е. освобождает любой «внешний» XMLWriter, с помощью которого он был создан.

Такой код сгенерирует ошибку анализа кода CA2202. Решение таких ситуаций описано в следующей ссылке.

person joe90p    schedule 05.04.2013

Посмотрите, что происходит, когда вы создаете еще XmlWriter из 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();
}

Видеть? Итак, когда вы выбрасываете inner, вы фактически избавляетесь от xml, потому что это один и тот же объект.

Если вы проверите MSDN: (http://msdn.microsoft.com/en-us/library/77t6e4w3.aspx)

Тип возвращаемого значения:

System.Xml.XmlWriter Объект XmlWriter, который

обернуты вокруг указанного объекта XmlWriter.

Поэтому я предполагаю, что он использует тот же объект.

ИЗМЕНИТЬ Вот решение, которое вы можете использовать:

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());            
    }
person Conrad Clark    schedule 05.04.2013