【问题标题】:XSL Transformation: Adding optional attributes to an elementXSL 转换:向元素添加可选属性
【发布时间】:2015-12-14 00:23:34
【问题描述】:

只有当值不为空时,我才尝试将属性添加到某个元素。我知道我可以使用模板和选择器来提供元素的静态版本,因此不需要 xsl:if,但我有 10 多个元素并且不想创建所有可能的排列。

可能仍然可以使用模板,如果可能的话,那将是理想的。如果没有,我可以使用 xsl:if。

源 XML:

<Test>
    <Attribute1>A</Attribute1>
    <Attribute3>B</Attribute3>
</Test>

使用以下 XSL:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:for-each select="Test">

            <xsl:element name="MyElement">

                    <xsl:attribute name="FirstAttribute">
                        <xsl:value-of select="Attribute1"/>
                    </xsl:attribute>

                    <xsl:attribute name="SecondAttribute">
                        <xsl:value-of select="Attribute2"/>
                    </xsl:attribute>

                    <xsl:attribute name="ThirdAttribute">
                        <xsl:value-of select="Attribute3"/>
                    </xsl:attribute>

            </xsl:element>

        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

我得到一个如下所示的输出:

<MyElement FirstAttribute="A" SecondAttribute="" ThirdAttribute="B" />

但我想要这个:

<MyElement FirstAttribute="A" ThirdAttribute="B" />

我原本想使用这种模板:

<xsl:element name="MyElement">

  <xsl:if test="Attribute1 != ''"> 
    <xsl:attribute name="FirstAttribute">
      <xsl:value-of select="Attribute1"/>
    </xsl:attribute>
  </xsl:if>

  <xsl:if test="Attribute2 != ''"> 
    <xsl:attribute name="SecondAttribute">
      <xsl:value-of select="Attribute2"/>
    </xsl:attribute>
  </xsl:if>

  <xsl:if test="Attribute3 != ''"> 
    <xsl:attribute name="ThirdAttribute">
      <xsl:value-of select="Attribute3"/>
    </xsl:attribute>
  </xsl:if>

</xsl:element>

很遗憾,这会导致以下错误:

XslTransformException was unhandled by user code
Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added.

当我使用这个 C# 代码来处理它时:

namespace XslTest
{
    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.XPath;
    using System.Xml.Xsl;

    class Program
    {
        private const string xmlSrcPath = "testXml.xml";
        private const string xslPath = "testXsl.xsl";

        static void Main(string[] args)
        {
            var result = Serialize(xmlSrcPath, xslPath);

            Console.WriteLine(result);
            Console.ReadLine();
        }

        private static string Serialize(string xmlFile, string xslTemplate)
        {
            var xmlMemoryStream = new MemoryStream();
            var xmlFileStream = new FileStream(xmlFile, FileMode.Open);

            xmlFileStream.CopyTo(xmlMemoryStream);
            xmlMemoryStream.Flush();
            xmlMemoryStream.Seek(0, SeekOrigin.Begin);

            var xPathDoc = new XPathDocument(xmlMemoryStream);
            var xslCompiledTransform = new XslCompiledTransform();
            var transformedMemoryStream = new MemoryStream();

            xslCompiledTransform.Load(xslTemplate);

            var transformedWriter = new XmlTextWriter(transformedMemoryStream, null);
            xslCompiledTransform.Transform(xPathDoc, transformedWriter);

            transformedMemoryStream.Seek(0, SeekOrigin.Begin);
            var transformedReader = new StreamReader(transformedMemoryStream);
            return transformedReader.ReadToEnd();
        }
    }
}

有没有办法做这种操作?

【问题讨论】:

  • 您必须编写一个恒等变换(将所有输入复制到输出)并在复制节点时进行修改。
  • 我认为您没有显示导致错误的代码。问题是在为给定元素编写非属性节点后无法添加属性。因此,请确保先写入所有属性,然后再写入内容。
  • 请提供一个可重现的示例 - 请参阅:stackoverflow.com/help/mcve。 --- “我有 10 多个元素,并且不想创建所有可能的排列。”我怀疑您可以在这里使用单个模板,而不是 10 多个 xsl:if 系列语句。但是我们也需要对这个问题有一个更清晰的定义。
  • 您仍然没有提供可重现的示例。仅使用您的代码,没有错误 - 请参阅:xsltransform.net/94rmq6q
  • 看来我可能错误地识别了问题的根本原因并在简化代码时将其删除。我会努力解决这个问题,回家后找到根本原因。

标签: c# xml xslt


【解决方案1】:

您的问题的一个解决方案是在元素名称和所需的目标属性名称之间使用xsl:key 映射。在这个插图中,我在数据文件中包含了attr 映射,但它可以外包给一个新的 XML 文档,然后作为数据源包含在内。我对 XSLT 进行了适当的修改以满足该插图的需要。这是带有映射的 XML:

<Root>
    <attr elemName="Attribute1" attribName="FirstAttribute" />
    <attr elemName="Attribute2" attribName="SecondAttribute" />
    <attr elemName="Attribute3" attribName="ThirdAttribute" />
    <Test>
        <Attribute1>A</Attribute1>
        <Attribute3>B</Attribute3>
    </Test>
</Root>

xsl:element 中的xsl:for-each 遍历所有元素,并将它们的名称与attr 节点elemName 属性中定义的名称与键映射进行比较。表达式周围的花括号用于评估表达式,而不是将其解释为文字字符串。样式表中的 xsl:key 使用 XML 中的 attr 节点。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="AllAttributes" match="attr" use="@elemName"/> 
    <xsl:template match="/Root">
        <xsl:for-each select="Test">
            <xsl:element name="MyElement">
                <xsl:for-each select="*">
                    <xsl:attribute name="{key('AllAttributes',local-name())/@attribName}">
                        <xsl:value-of select="."/>
                    </xsl:attribute>
                </xsl:for-each>
            </xsl:element>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

【讨论】:

    猜你喜欢
    • 2020-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-23
    相关资源
    最近更新 更多