C# XslCompiledTransform忽略.NET Framework 4.6及更高版本上的DTD架构

C# XslCompiledTransform忽略.NET Framework 4.6及更高版本上的DTD架构,c#,.net,xslt,.net-4.5,C#,.net,Xslt,.net 4.5,目标是通过运行XSL转换,将XML标记与DTD中指定的键(短标记名与全标记名/ref标记名)交换: XSL确定XML的格式:短格式还是完整格式 XSL试图为DTD中的XML标记找到相反的键(full的缩写,full的缩写) XSL用找到的相反键替换XML标记 所以XML <!DOCTYPE Note SYSTEM "note.dtd"> <Note> <To>Tove</To> <From>Jani</From

目标是通过运行XSL转换,将XML标记与DTD中指定的键(短标记名与全标记名/ref标记名)交换:

  • XSL确定XML的格式:短格式还是完整格式
  • XSL试图为DTD中的XML标记找到相反的键(full的缩写,full的缩写)
  • XSL用找到的相反键替换XML标记
所以XML

<!DOCTYPE Note SYSTEM "note.dtd">
<Note>
    <To>Tove</To>
    <From>Jani</From>
    <Heading>Reminder</Heading>
    <Body>Don't forget me this weekend</Body>
</Note>
看起来这个特性在最新的.NET框架中不知何故丢失了,因为我一直在考虑编写自定义代码

  • XSL中的脚本或
  • 用C语言进行转换#
不过,我希望有人能知道一些关于这种不良行为的事情

PS在我轻描淡写地说DTD完全被忽略之前,我花了很多时间在谷歌上搜索这个问题,并做了很多没有发生的实验:

  • 不同的测试框架:XUnin、NUnit
  • 在不同的机器上运行
  • x64 x86
  • 在XslCompiledTransform中启用脚本
  • 为XslCompiledTransform设置不同的设置

我认为您的XmlReaderSettings需要使用例如

new XmlReaderSettings() { DtdProcessing = DtdProcessing.Parse, XmlResolver = new XmlUrlResolver() }
见:

默认值是一个没有凭据的新XmlUrlResolver。从 在.NET Framework 4.5.2中,此设置的默认值为null


@zx485我几乎可以肯定.NET不支持现代XSL版本中的某些功能,但版本号主要用于提供信息。我将把示例中的版本号更新为1.0。您的用例是否真的使用StringReader从字符串加载XML,但需要解析字符串中的相对URI?或者,为什么那个相当复杂的示例有一个带有XML的字符串,使用字符串操作来预处理用XMLLINQ创建的DTD?如果通过while循环拉取
读取器
并检查每个元素节点,会发生什么情况,它是否具有来自DTD的固定属性?属性真的只有在将读取器传递给XslCompiledTransform时才会丢失吗?是的,就是这样!非常感谢。
<!ELEMENT Note (To,From,Heading,Body)>
<!ELEMENT To (#PCDATA)>
<!ATTLIST To
  refname (To) #FIXED "To"
  shortname (to) #FIXED "x1">
<!ELEMENT From (#PCDATA)>
<!ELEMENT Heading (#PCDATA)>
<!ELEMENT Body (#PCDATA)>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
  <xsl:variable name="target">
    <xsl:choose>
      <xsl:when test="local-name(/*)='Note'">short</xsl:when>
      <xsl:otherwise>reference</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:template match="*">
    <xsl:variable name="target-name">
      <xsl:choose>
        <xsl:when test="($target='short') and not(@shortname)">
          <xsl:value-of select="name()"/>
        </xsl:when>
        <xsl:when test="$target='short'">
          <xsl:value-of select="@shortname"/>
        </xsl:when>
        <xsl:when test="not(@refname)">
          <xsl:value-of select="name()"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="@refname"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:element name="{$target-name}">
      <xsl:copy-of select="@*[not(name()='refname' or name()='shortname')]"/>
      <xsl:apply-templates select="*|text()"/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="text()">
    <xsl:copy/>
  </xsl:template>
</xsl:stylesheet>
using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Xsl;

using Xunit;

public class Tests
{
    private const string DtdDirectoryPath = @"c:\source\...\Resources";
    private const string DtdFileName = "note.dtd";

    private const string RootTag = "Note";
    private string xml = @"
<Note>
  <To>Tove</To>
  <From>Jani</From>
  <Heading>Reminder</Heading>
  <Body>Don't forget me this weekend</Body>
</Note>";

    [Fact]
    public void Test()
    {
        // copy the DTD to the expected location
        var dtdFilePath = Path.Combine(Environment.CurrentDirectory, DtdFileName);
        File.Copy(Path.Combine(DtdDirectoryPath, DtdFileName), dtdFilePath, overwrite: true);

        XslCompiledTransform xslt;
        using (var stringReader = new StringReader(Resources.SwitchTagNamesXSL))
        using (var xmlReader = new XmlTextReader(stringReader))
        {
            xslt = new XslCompiledTransform(enableDebug: true);
            xslt.Load(xmlReader);
        }

        // add the DTD definition at the XML top in order the XSL transformation pass
        this.xml = this.xml.Insert(
            0,
            new XDocumentType(RootTag, publicId: null, systemId: DtdFileName, internalSubset: null).ToString());

        Console.WriteLine("Initial XML with DTD at the top");
        Console.WriteLine(this.xml);

        var output = new XDocument();
        using (var writer = output.CreateWriter())
        using (var stringReader = new StringReader(this.xml))
        using (var reader = XmlReader.Create(
            stringReader,
            new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse })) // this flag is important to force DTD Processing
        {
            xslt.Transform(reader, writer);
        }

        Console.WriteLine("Output");
        Console.WriteLine(output);
    }
}
new XmlReaderSettings() { DtdProcessing = DtdProcessing.Parse, XmlResolver = new XmlUrlResolver() }