簡體   English   中英

XmlWriter 異步操作因 XmlWriterSettings.OutputMethod = Html 而失敗

[英]XmlWriter Async operations fail with XmlWriterSettings.OutputMethod = Html

使用XmlWriterSettings.OutputMethod = OutputMethod.Html創建XmlWriter時,異步操作失敗。 當使用OutputMethod.AutoDetect (默認)創建相同的內容時,異步操作成功。

失敗的代碼(帶小提琴):

var transform = new XslCompiledTransform();
using var reader = XmlReader.Create(new StringReader(@"
  <xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"">
    <xsl:output method=""html"" indent=""yes"" doctype-system=""html""/>
    <xsl:template match=""/"">
      <bar/>
    </xsl:template>
  </xsl:stylesheet>"));
transform.Load(reader);

var settings = transform.OutputSettings.Clone();
settings.CloseOutput = false;
settings.Async = true;

using var stream = new MemoryStream();
using (var writer = XmlWriter.Create(stream, settings))
{
    await writer.WriteStartDocumentAsync();
    await writer.WriteStartElementAsync(null, "foo", null);
    await writer.WriteEndElementAsync();
    await writer.WriteEndDocumentAsync();
}
stream.Position = 0;
var content = new StreamReader(stream).ReadToEnd();
Assert.Contains("foo", content);

使用堆棧跟蹤:

Message: 
System.NotImplementedException : The method or operation is not implemented.

  Stack Trace: 
XmlWriter.WriteStartElementAsync(String prefix, String localName, String ns)
XmlWellFormedWriter.WriteStartElementAsync_NoAdvanceState(String prefix, String localName, String ns)
XmlWellFormedWriter.WriteStartElementAsync(String prefix, String localName, String ns)
XmlAsyncCheckWriter.WriteStartElementAsync(String prefix, String localName, String ns)

工作代碼(帶工作小提琴):

var settings = new XmlWriterSettings();
settings.CloseOutput = false;
settings.Async = true;

using var stream = new MemoryStream();
using (var writer = XmlWriter.Create(stream, settings))
{
    await writer.WriteStartDocumentAsync();
    await writer.WriteStartElementAsync(null, "foo", null);
    await writer.WriteEndElementAsync();
    await writer.WriteEndDocumentAsync();
}
stream.Position = 0;
var content = new StreamReader(stream).ReadToEnd();
Assert.Contains("foo", content);

在調試模式下檢查各種東西,兩條代碼路徑似乎都在后台使用System.Xml.XmlAsyncCheckWriter

有趣的是,造成這種情況的不是OutputMethod ,而是doctype-system 刪除該屬性,您的異步調用將神奇地工作。

我可以告訴你發生了什么,但不能告訴你為什么他們選擇這樣做。


首先,編寫器由XmlWriterSettings.CreateWriter(Stream)創建。 剪掉所有的絨毛,它是這樣的:

internal XmlWriter CreateWriter(Stream output)
{
    XmlWriter writer;
    if (Encoding.WebName == "utf-8") {
        switch (OutputMethod) {
            case XmlOutputMethod.Html:
                writer= new HtmlUtf8RawTextWriter(output, this);
                break;
        }
    }

    // Wrap with Xslt/XQuery specific writer if needed;
    // XmlOutputMethod.AutoDetect writer does this lazily when it creates the underlying Xml or Html writer.
    if (OutputMethod != XmlOutputMethod.AutoDetect) {
        if (IsQuerySpecific) {
            // Create QueryOutputWriter if CData sections or DocType need to be tracked
            writer = new QueryOutputWriter((XmlRawWriter)writer, this);
        }
    }

    // wrap with well-formed writer
    writer = new XmlWellFormedWriter(writer, this);

    if (_useAsync)
        writer = new XmlAsyncCheckWriter(writer);

    return writer;
}

所以最后,你會得到一層洋蔥/食人魔

XmlAsyncCheckWriter(
    XmlWellFormedWriter(
        QueryOutputWriter(
            HtmlUtf8RawTextWriter)))

當您進行Write...Async()調用時,您會期望它從外部 Writer a 一直級聯到HtmlUtf8RawTextWriter中的最深級別 - 它確實具有您想要的異步調用

不幸的是, QueryOutputWriter包裝器不會將 Async 調用委托給內部編寫器,實際上是NotImplementedException的那個。 它是一個錯誤嗎? 還是深思熟慮的選擇? 我不知道。

如果您不需要 DOCTYPE,並且不在 output 中使用 CDATA(兩者都由我們有問題的QueryOutputWriter處理),只需從 XSL 中刪除doctype-system即可解決您的問題。 這將導致以下IsQuerySpecificfalse ,從而防止不需要的包裝。

private bool IsQuerySpecific => 
    CDataSectionElements.Count != 0
    || _docTypePublic != null
    || _docTypeSystem != null
    || _standalone == XmlStandalone.Yes;

...

if (IsQuerySpecific)
    xmlWriter = new QueryOutputWriter((XmlRawWriter)xmlWriter, this);

如果您確實需要 DOCTYPE/CDATA,那么重新實現一些層並覆蓋函數將是一個有趣的練習。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM