尝试选择第一个匹配元素时,XSLT出现意外行为

时间:2013-08-28 22:01:30

标签: xml xslt

我想要以特定格式序列化为XML的数据。我有一个TestClass数组,其中包含两个字符串属性:AB

我知道这听起来不太好,但在这种情况下,我希望A的所有TestClass实例的A值都相同。

我想创建一个包含公共B值和所有using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml.Serialization; using System.Xml.Xsl; using System.IO; namespace test { public class Program { public class TestClass { public string A { get; set; } public string B { get; set; } } static void Main(string[] args) { TestClass[] array = new TestClass[] { new TestClass() { A = "A1", B="B1"}, new TestClass() { A = "A2", B="B2"} }; XmlSerializer serializer = new XmlSerializer(typeof(TestClass[])); XslCompiledTransform xslt = new XslCompiledTransform(); xslt.Load("test.xslt"); string xml1Name = @"c:\temp\xmltest\original.xml"; string xml2Name = @"c:\temp\xmltest\transformed.xml"; using (FileStream xmlStream = new FileStream(xml1Name, FileMode.Create)) { serializer.Serialize(xmlStream, array); } xslt.Transform(xml1Name, xml2Name); } } } 值的XML文件。我写了以下代码:

<?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:template match="/ArrayOfTestClass/TestClass[1]">
    <Test>
      <A>
        <xsl:value-of select="A"/>
      </A>
      <B_Values>
        <xsl:for-each select="/ArrayOfTestClass/TestClass">
          <value>
            <xsl:value-of select="B" />
          </value>
        </xsl:for-each>
      </B_Values>
    </Test>
  </xsl:template>
</xsl:stylesheet>

test.xslt包含以下内容:

<?xml version="1.0"?>
<ArrayOfTestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TestClass>
    <A>A1</A>
    <B>B1</B>
  </TestClass>
  <TestClass>
    <A>A2</A>
    <B>B2</B>
  </TestClass>
</ArrayOfTestClass>

我希望我的代码输出两个文件。 XMLSerializer类生成的“默认”序列化似乎是正确的。

A

XSLT转换的XML几乎是正确的,但它包含一些额外的尾随文本,它们在序列化数组的第二个元素中显示为B<?xml version="1.0" encoding="utf-8"?> <Test><A>A1</A><B_Values><value>B1</value><value>B2</value></B_Values></Test> A2 B2 的值。这不是一个错字:

{{1}}

为什么我的XSLT没有产生我期望的结果?我怎样才能做对吗?

1 个答案:

答案 0 :(得分:3)

这是因为XSLT的built-in templates,它用于XSLT处理器无法在样式表中找到它试图匹配的节点的特定模板的情况。元素和根节点的内置模板实质上是跳过元素并查找与其匹配的模板。如果找到文本节点,它将输出文本。

在您的XSLT中,您没有匹配/ArrayOfTestClass元素的模板,因此内置模板会启动。如果ArrayOfTestClass的情况,它将查找与其所有子节点匹配的模板,但您只有与第一个子节点匹配的特定模板。因此,其他TestClass元素将与内置模板匹配,最终输出其中的文本。

您需要做的就是添加一个模板,告诉XSLT停止处理其他TestClass元素。你可以添加这个模板

<xsl:template match="/ArrayOfTestClass/TestClass[position() > 1]" />

但是,如果您只将TestClass元素作为ArrayOfTestClass元素的子节点,则可以将其简化为

<xsl:template match="TestClass" />

这利用了当多个模板可能匹配相同元素时,XSLT将优先考虑更具体的模板。