【问题标题】:Unexpected behavior with XSLT when trying to select first matching element尝试选择第一个匹配元素时 XSLT 的意外行为
【发布时间】:2013-08-28 22:01:30
【问题描述】:

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

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

我想创建一个 XML 文件,其中包含常见的 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);
        }
    }
}

test.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: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>

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

<?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>

XSLT 转换后的 XML 几乎是正确的,但它包含一些额外的尾随文本,这似乎是序列化数组的第二个元素中的 AB 的值。这不是错字:

<?xml version="1.0" encoding="utf-8"?>
  <Test><A>A1</A><B_Values><value>B1</value><value>B2</value></B_Values></Test>

    A2
    B2

为什么我的 XSLT 没有生成我期望的结果?我怎样才能使它正确?

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    这是因为 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 将优先考虑更具体的模板。

    【讨论】:

    • 谢谢。这正是我所需要的。
    猜你喜欢
    • 1970-01-01
    • 2014-11-26
    • 2012-03-19
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多