XmlWriter 异步操作失败 XmlWriterSettings.OutputMethod = Html
XmlWriter Async operations fail with XmlWriterSettings.OutputMethod = Html
使用 XmlWriterSettings.OutputMethod = OutputMethod.Html
创建 XmlWriter
时,异步操作失败。使用 OutputMethod.AutoDetect
(默认)创建相同内容时,异步操作成功。
失败代码(fiddle):
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)
工作代码(工作 fiddle):
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;
}
所以最后,你得到 onion/ogre 层
XmlAsyncCheckWriter(
XmlWellFormedWriter(
QueryOutputWriter(
HtmlUtf8RawTextWriter)))
当你进行 Write...Async()
调用时,你会期望它从外部 Writer a 一直向下级联到 HtmlUtf8RawTextWriter
中的最深层次 - 其中确实有 the async calls你要
不幸的是,QueryOutputWriter 包装器不会将异步调用委托给内部编写器,实际上是抛出 NotImplementedException
. 的包装器是吗错误?还是刻意的选择?我不知道。
如果您不需要 DOCTYPE,并且不在输出中使用 CDATA(均由我们有问题的 QueryOutputWriter
处理),只需从 XSL 中删除 doctype-system
即可解决您的问题问题。这将导致以下 IsQuerySpecific
成为 false
,从而防止不希望的换行。
private bool IsQuerySpecific =>
CDataSectionElements.Count != 0
|| _docTypePublic != null
|| _docTypeSystem != null
|| _standalone == XmlStandalone.Yes;
...
if (IsQuerySpecific)
xmlWriter = new QueryOutputWriter((XmlRawWriter)xmlWriter, this);
如果您确实需要 DOCTYPE/CDATA,那么重新实现一些层并覆盖函数将是一个有趣的练习。
使用 XmlWriterSettings.OutputMethod = OutputMethod.Html
创建 XmlWriter
时,异步操作失败。使用 OutputMethod.AutoDetect
(默认)创建相同内容时,异步操作成功。
失败代码(fiddle):
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)
工作代码(工作 fiddle):
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;
}
所以最后,你得到 onion/ogre 层
XmlAsyncCheckWriter(
XmlWellFormedWriter(
QueryOutputWriter(
HtmlUtf8RawTextWriter)))
当你进行 Write...Async()
调用时,你会期望它从外部 Writer a 一直向下级联到 HtmlUtf8RawTextWriter
中的最深层次 - 其中确实有 the async calls你要
不幸的是,QueryOutputWriter 包装器不会将异步调用委托给内部编写器,实际上是抛出 NotImplementedException
. 的包装器是吗错误?还是刻意的选择?我不知道。
如果您不需要 DOCTYPE,并且不在输出中使用 CDATA(均由我们有问题的 QueryOutputWriter
处理),只需从 XSL 中删除 doctype-system
即可解决您的问题问题。这将导致以下 IsQuerySpecific
成为 false
,从而防止不希望的换行。
private bool IsQuerySpecific =>
CDataSectionElements.Count != 0
|| _docTypePublic != null
|| _docTypeSystem != null
|| _standalone == XmlStandalone.Yes;
...
if (IsQuerySpecific)
xmlWriter = new QueryOutputWriter((XmlRawWriter)xmlWriter, this);
如果您确实需要 DOCTYPE/CDATA,那么重新实现一些层并覆盖函数将是一个有趣的练习。