XSLT XPATH 出错(表达式必须计算为节点集)

Posted

技术标签:

【中文标题】XSLT XPATH 出错(表达式必须计算为节点集)【英文标题】:Error with XSLT XPATH (Expression must evaluate to a node-set) 【发布时间】:2020-07-13 16:36:20 【问题描述】:

我有如下的 XML

<Employees>

  <Employee>
    <ID>100</ID>
    <FirstName>Bala</FirstName>
    <LastName>Murugan</LastName>
    <Dept>Production Support</Dept>
  </Employee>

  <Employee0>
    <ID>101</ID>
    <FirstName>Peter</FirstName>
    <LastName>Laurence</LastName>
    <Dept>Development</Dept>
  </Employee0>

  <Employee1>
    <ID>102</ID>
    <FirstName>Rick</FirstName>
    <LastName>Anderson</LastName>
    <Dept>Sales</Dept>
  </Employee1>

</Employees>

我想向其中一位员工展示使用以下 XSLT

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:param name="SLCT"/>
  <xsl:template match="/">
    <xsl:for-each select="$SLCT">
      <div style="border:1px black solid;width:300px;margin:1px">
        <div>
          <b>Employee ID:</b>
          <xsl:value-of select="ID"/>
        </div>
        <div>
          <b>Name:</b>
          <xsl:value-of select="FirstName"/>
          <xsl:text> </xsl:text>
          <xsl:value-of select="LastName"/>
        </div>
        <div>
          <b>Department:</b>
          <xsl:value-of select="Dept"/>
        </div>
      </div>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

(for-each select="$SLCT") 的问题,它不接受来自 C# 的参数。

我的 C# 代码

protected void Page_Load(object sender, EventArgs e)

    string strXSLTFile = Server.MapPath("EmployeeXSLT.xslt");
    string strXMLFile = Server.MapPath("Employess.xml");

    XmlReader reader = XmlReader.Create(strXMLFile);
    XslCompiledTransform objXSLTransform = new XslCompiledTransform();
    objXSLTransform.Load(strXSLTFile);

    // Create the XsltArgumentList.
    XsltArgumentList argList = new XsltArgumentList();
    // Set new value to the parameter
    argList.AddParam("SLCT", "", "(//Employee)[1]");
    XmlWriter writer = XmlWriter.Create(Server.MapPath("OutPut.xml"));
    objXSLTransform.Transform(new XPathDocument(strXMLFile), argList, writer); 
    //Expression must evaluate to a node-set
    writer.Close(); 
    reader.Close();
    Xml2.DocumentSource = Server.MapPath("~/test/Employess.xml");
    Xml2.TransformSource = Server.MapPath("~/test/OutPut.xml");
    Xml2.DataBind();

我得到了代码中注释的这个异常(//表达式必须评估为节点集)。但是,当我直接在 XSLT 文件中使用“(//Employee)[1]”或“//Employee”而不是使用参数 (SLCT) 时,我得到了所需的结果。那么,问题出在哪里呢?

【问题讨论】:

到底谁认为为每个员工使用不同的元素名称是一个聪明的主意?很难想到一种设计选择会使处理难度增加五倍,并且绝对没有任何补偿性好处, 【参考方案1】:

xsl:for-each 指令的select 属性中使用的 XPath 表达式无法在运行时动态计算。

由于您的 XML 对每个员工记录使用不同的元素名称(EmployeeEmployee0Employee1 等),您可以将所需元素的名称作为参数传递,然后使用谓词。

例如,下面的样式表:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:param name="SLCT"/>

<xsl:template match="/Employees">
    <xsl:variable name="emp" select="*[name()=$SLCT]" />
    <div>
      <b>Employee ID:</b>
      <xsl:value-of select="$emp/ID"/>
    </div>
</xsl:template>

</xsl:stylesheet>

当使用"Employee0" 的值调用SLCT 参数时,将返回:

结果

<div><b>Employee ID:</b>101</div>

注意: 因为只有一个元素会满足谓词,所以我使用了一个变量而不是xsl:for-each。但它也可以使用:

<xsl:template match="/Employees">
    <xsl:for-each select="*[name()=$SLCT]">
        <div>
          <b>Employee ID:</b>
          <xsl:value-of select="ID"/>
        </div>
    </xsl:for-each>
</xsl:template>

【讨论】:

【参考方案2】:

您的 XML 需要一些改进。无需为每个员工使用不同的 XML 元素名称。您可以根据唯一 ID 元素值检索特定员工:100、101 等。我调整了 XSLT 以接受该类型的参数。请在下面查看。

XML

<?xml version="1.0"?>
<Employees>
    <Employee>
        <ID>100</ID>
        <FirstName>Bala</FirstName>
        <LastName>Murugan</LastName>
        <Dept>Production Support</Dept>
    </Employee>
    <Employee>
        <ID>101</ID>
        <FirstName>Peter</FirstName>
        <LastName>Laurence</LastName>
        <Dept>Development</Dept>
    </Employee>
    <Employee>
        <ID>102</ID>
        <FirstName>Rick</FirstName>
        <LastName>Anderson</LastName>
        <Dept>Sales</Dept>
    </Employee>
</Employees>

XSLT

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:param name="SLCT">100</xsl:param>

    <xsl:template match="/">
        <xsl:apply-templates select="Employees/Employee[ID=$SLCT]"/>
    </xsl:template>

    <xsl:template match="Employee">
        <div style="border:1px black solid;width:300px;margin:1px">
            <div>
                <b>Employee ID:</b>
                <xsl:value-of select="ID"/>
            </div>
            <div>
                <b>Name:</b>
                <xsl:value-of select="concat(FirstName,' ',LastName)"/>
            </div>
            <div>
                <b>Department:</b>
                <xsl:value-of select="Dept"/>
            </div>
        </div>
    </xsl:template>
</xsl:stylesheet>

c#

// Set new value to the parameter
argList.AddParam("SLCT", "", "101");

输出

<div style="border:1px black solid;width:300px;margin:1px">
  <div>
    <b>Employee ID:</b>101
  </div>
  <div>
    <b>Name:</b>Peter Laurence
  </div>
  <div>
    <b>Department:</b>Development
  </div>
</div>

【讨论】:

你不能自己编问题。 让我们等待 OP 的回复。

以上是关于XSLT XPATH 出错(表达式必须计算为节点集)的主要内容,如果未能解决你的问题,请参考以下文章

XPATH使用

优化 xslt xpath 匹配表达式

XSLT/XPath 中的当前节点与上下文节点?

使用 xslt 2 从节点生成 xpath

xpath语法

获取最远的祖先节点 -Xpath, XSLT