【问题标题】:cdata-section-elements not workingcdata-section-elements 不工作
【发布时间】:2017-06-20 10:44:33
【问题描述】:

我正在尝试通过设置全局参数在通过 XSLT(使用 Saxon-HE v9.7.0.14)生成的 xml 文件中设置密码。

密码可以包含任何字符,因此需要放在CDATA 部分中。

我试图通过设置我的 xslt 的 xsl:output 元素的 cdata-section-elements 属性来包含密码元素的名称来实现这一点:

<xsl:output method="xml" indent="yes" cdata-section-elements="password"/>

这不起作用。我在下面包含了示例代码、输入、xslt、当前输出和所需输出。

我需要更改哪些内容才能在 CDATA 部分中获取密码?

计划:

using System;
using System.IO;
using Saxon.Api;

namespace XsltTest {
    class Program {
        static void Main(string[] args) {
            var xslt = new FileInfo(@"transform.xslt");
            var input = new FileInfo(@"input.xml");
            var output = new FileInfo(@"output.xml");
            var processor = new Processor();
            var compiler = processor.NewXsltCompiler();
            var executable = compiler.Compile(new Uri(xslt.FullName));
            var transformer = executable.Load();
            var destination = new DomDestination();
            using (var inputStream = input.OpenRead()) {               
                transformer.SetInputStream(inputStream, new Uri(Path.GetTempPath()));
                transformer.SetParameter(
                    new QName("password"),
                    new XdmAtomicValue("secret"));
                transformer.Run(destination);
            }
            destination.XmlDocument.Save(output.FullName);
        }
    }
}

Transform.xslt:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes" cdata-section-elements="password"/>
  <xsl:param name="password" />
  <xsl:template match="@* | node()">
    <bar>
      <username>
        <xsl:value-of select="//username"/>
      </username>
      <password>
        <xsl:value-of select="$password"/>
      </password>
    </bar>
  </xsl:template>
</xsl:stylesheet>

Input.xml:

<?xml version="1.0" encoding="utf-8" ?>
<foo>
  <username>john</username>
</foo>

输出.xml:

<bar>
  <username>john</username>
  <password>secret</password>
</bar>

密码没有放在CDATA 部分内。

期望的结果:

<bar>
  <username>john</username>
  <password><![CDATA[secret]]></password>
</bar>

【问题讨论】:

    标签: c# xml xslt saxon


    【解决方案1】:

    我测试了您的代码,它似乎工作正常。

    输入线

    xsltproc --stringparam password "1>2Ä-34" Transform.xslt Input.xml
    

    返回所需的输出

    <?xml version="1.0"?>
    <bar>
      <username>john</username>
      <password><![CDATA[1>2Ä-34]]></password>
    </bar>
    

    您是否错过了将 password 参数输入到 XSLT 处理器以提供 &lt;xsl:param name="password" /&gt; 的输入?

    【讨论】:

    • 我没有使用 xsltproc。根据示例程序,我在 C# 应用程序中使用 Saxon。
    • 是的。我知道。但它是相似的:将字符串参数“password”及其值传递给 XSLT。它应该像我在回答中描述的那样工作。在xsl:stylesheet 级别的空&lt;xsl:param name="password" /&gt; 是为了这样的目的而确定的。
    【解决方案2】:

    xsl:output 上的选项会影响序列化器的操作,如果输出没有序列化,则它们没有效果。您正在写入 DomDestination 而不是 Serializer(然后使用 DOM 方法序列化 DOM,这些方法对 XSLT xsl:output 声明一无所知)。

    无论如何,您的前提是错误的:“密码可以包含任何字符,因此需要放在 CDATA 部分中。”如果没有 cdata-section-elements,特殊字符将使用实体引用(例如 &amp;lt;&amp;amp;)进行序列化,应该可以正常工作。

    【讨论】:

    • 谢谢。我现在使用 Serializer 而不是 DomDestination 并且它正在工作。也感谢关于没有 CDATA 的实体编码的评论。我实际上是在用 CDATA 中的密码替换已经发送 XML 消息的系统,而我之前的团队说他们在使用 CDATA 之前遇到了问题。我们无法控制读取消息的外部系统,所以我认为问题在于没有正确处理 XML,并且我知道在使用 CDATA 时它可以工作。
    • 为不称职的解析 XML 的人生成 XML 只比阅读由不称职的人创建的(所谓的)XML 简单一点——遗憾的是,在我们的职业中有很多不称职的人。
    【解决方案3】:

    Michael Kay's answer 解释了解决方案,即cdata-section-elements 属性仅适用于写入Serializer 而不是DomDestination

    这是执行此操作的 C# 代码:

    static void Main(string[] args) {
        var xslt = new FileInfo(@"transform.xslt");
        var input = new FileInfo(@"input.xml");
        var output = new FileInfo(@"output.xml");
        var processor = new Processor();
        var compiler = processor.NewXsltCompiler();
        var executable = compiler.Compile(new Uri(xslt.FullName));
        var transformer = executable.Load();
        var serializer = new Serializer();
        using (var writer = new StreamWriter(output.FullName))
        using (var inputStream = input.OpenRead()) {
            serializer.SetOutputWriter(writer);
            transformer.SetInputStream(inputStream, new Uri(Path.GetTempPath()));
            transformer.SetParameter(
                new QName("password"),
                new XdmAtomicValue("secret"));
            transformer.Run(serializer);
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-21
      • 1970-01-01
      • 2012-01-08
      • 2011-12-11
      • 1970-01-01
      • 2022-07-15
      • 1970-01-01
      • 2017-11-18
      相关资源
      最近更新 更多