做这样的事情:
using (XmlWriter myMamlHelpWriter = XmlWriter.Create(myFileStream, XmlHelpExToMamlXslTransform.OutputSettings))
{
XmlHelpExToMamlXslTransform.Transform(myMsHelpExTopicFilePath, null, myMamlHelpWriter);
}
,其中
private static XslCompiledTransform XmlHelpExToMamlXslTransform
{
get
{
if (fMsHelpExToMamlXslTransform == null)
{
// Create the XslCompiledTransform and load the stylesheet.
fMsHelpExToMamlXslTransform = new XslCompiledTransform();
using (Stream myStream = typeof(XmlHelpBuilder).Assembly.GetManifestResourceStream(
typeof(XmlHelpBuilder),
MamlXmlTopicConsts.cMsHelpExToMamlTransformationResourceName))
{
XmlTextReader myReader = new XmlTextReader(myStream);
fMsHelpExToMamlXslTransform.Load(myReader, null, null);
}
}
return fMsHelpExToMamlXslTransform;
}
}
每次字符串“& quot;”在结果文件中替换为实际引号 无法理解为什么会发生这种情况......
答案 0 :(得分:1)
原因是在XSLT的内部表示中,"
与"
完全相同。它们都代表ascii代码点0x34。似乎当XslCompiledTransform产生其输出时,它使用"
,这样做是合法的。我想它仍然会在属性值中输出"
。
在输出中"
生成"
会对您造成问题吗?
我刚使用任意输入文件在Visual Studio中运行以下XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/*">
<xml>
<xsl:variable name="chars">"'<>&</xsl:variable>
<node a='{$chars}' b="{$chars}">
<xsl:value-of select="$chars"/>
</node>
</xml>
</xsl:template>
</xsl:stylesheet>
输出结果为:
<xml>
<node a=""'<>&" b=""'<>&">"'<>&</node>
</xml>
正如您所看到的,即使所有五个字符最初都表示为实体,但每个地方都会产生'
的引用,并且在文本节点中将引号生成为"
。此外,具有a
分隔符的'
属性在输出中使用"
分隔符。正如我所说,就XSLT而言,引号只是一个引号,而一个属性只是一个属性。如何在输出中生成这些内容取决于XSLT处理器。
编辑:此行为的根本原因似乎是XmlWriter类的行为。对于那些想要更多自定义转义的人来说,看起来像是扩展XmlTextWriter
类的一般建议。 This page的实施看起来非常有希望:
public class KeepEntityXmlTextWriter : XmlTextWriter
{
private static readonly string[] ENTITY_SUBS = new string[] { "'", """ };
private static readonly char[] REPLACE_CHARS = new char[] { '\'', '"' };
public KeepEntityXmlTextWriter(string filename) : base(filename, null) { ; }
private void WriteStringWithReplace(string text)
{
string[] textSegments = text.Split(KeepEntityXmlTextWriter.REPLACE_CHARS);
if (textSegments.Length > 1)
{
for (int pos = -1, i = 0; i < textSegments.Length; ++i)
{
base.WriteString(textSegments[i]);
pos += textSegments[i].Length + 1;
// Assertion: Replace the following if-else when the number of
// replacement characters and substitute entities has grown
// greater than 2.
Debug.Assert(2 == KeepEntityXmlTextWriter.REPLACE_CHARS.Length);
if (pos != text.Length)
{
if (text[pos] == KeepEntityXmlTextWriter.REPLACE_CHARS[0])
base.WriteRaw(KeepEntityXmlTextWriter.ENTITY_SUBS[0]);
else
base.WriteRaw(KeepEntityXmlTextWriter.ENTITY_SUBS[1]);
}
}
}
else base.WriteString(text);
}
public override void WriteString( string text)
{
this.WriteStringWithReplace(text);
}
}
另一方面,MSDN documentation建议使用XmlWriter.Create()
而不是直接实例化XmlTextWriters。
在.NET Framework 2.0版本中,建议的做法是使用XmlWriter.Create方法和XmlWriterSettings类创建XmlWriter实例。这使您可以充分利用此版本中引入的所有新功能。有关更多信息,请参阅创建XML Writer。
一种方法是使用与上面相同的逻辑,但将其放在包含XmlWriter
的类中。 This page有一个现成的XmlWrappingWriter实现,您可以根据需要进行修改。
要将上述代码与XmlWrappingWriter
一起使用,您可以将包装编写器子类化,如下所示:
public class KeepEntityWrapper : XmlWrappingWriter
{
public KeepEntityWrapper(XmlWriter baseWriter)
: base(baseWriter)
{
}
private static readonly string[] ENTITY_SUBS = new string[] { "'", """ };
private static readonly char[] REPLACE_CHARS = new char[] { '\'', '"' };
private void WriteStringWithReplace(string text)
{
string[] textSegments = text.Split(REPLACE_CHARS);
if (textSegments.Length > 1)
{
for (int pos = -1, i = 0; i < textSegments.Length; ++i)
{
base.WriteString(textSegments[i]);
pos += textSegments[i].Length + 1;
// Assertion: Replace the following if-else when the number of
// replacement characters and substitute entities has grown
// greater than 2.
Debug.Assert(2 == REPLACE_CHARS.Length);
if (pos != text.Length)
{
if (text[pos] == REPLACE_CHARS[0])
base.WriteRaw(ENTITY_SUBS[0]);
else
base.WriteRaw(ENTITY_SUBS[1]);
}
}
}
else base.WriteString(text);
}
public override void WriteString(string text)
{
this.WriteStringWithReplace(text);
}
}
请注意这与KeepEntityXmlTextWriter
基本相同的代码,但使用XmlWrappingWriter作为基类并使用不同的构造函数。
我无法识别Guard
代码在两个地方使用的XmlWrappingWriter
,但鉴于您将自己使用代码,删除类似的行应该是非常安全的这个。它们只是确保不将null值传递给构造函数或(在上面的情况下无法访问)BaseWriter
属性:
Guard.ArgumentNotNull(baseWriter, "baseWriter");
要创建XmlWrappingWriter
的实例,您需要创建一个XmlWriter,然后使用:
KeepEntityWrapper wrap = new KeepEntityWrapper(writer);
然后你将这个wrap
变量用作传递给XSL转换的XmlWriter。
答案 1 :(得分:1)
XSLT处理器不知道字符是否由字符实体表示。这是因为XML解析器用任何代码值替换任何字符实体。
因此,XSLT处理器将看到完全相同的字符,无论它是表示为“或"
还是"
还是"
。
使用所谓的 "character maps" ,可以在XSLT 2.0中实现您的目标。
答案 2 :(得分:0)
这是你想要的技巧:
&
替换为&
&
替换为&