【问题标题】:XSLT - how to get the name value of a node?XSLT - 如何获取节点的名称值?
【发布时间】:2018-12-21 10:42:47
【问题描述】:

我们有(可能是异常格式的)XML 文件,它们(非常简化)如下所示:

<Contacts>
    <Contact>
        <Private>
            <Name>John Doe</Name>
            <Address>Somewhere1</Address>
            ...
        </Private>
    </Contact>
    <Contact>
        <Business>
            <Name>John Business</Name>
            <Address>Somewhere2</Address>
            <BusinessAddress>Somewhere3</BusinessAddress>
            ...
        </Business>
    </Contact>
    ...
</Contacts>

实际上,我们有几十种不同类型的“联系人”节点,它们具有更多属性,但其中一些属性在所有类型之间都是通用的......

我们目前使用样式表来格式化 XML 文件,该文件对每种类型的“联系人”节点的所有常见属性重复格式化:

<!-- ... loads of prologue code -->
<xsl:for-each select="Contacts/Contact/.">
    <xsl:choose>
    <xsl:when test="Private">
        <!--    code to format each and every attribute of the current 
                "Contact" element, e.g.: -->
        <td class="header">Private Contact</td>
        <td class="detail"><xsl:value-of select="./Name"/></td>
        <!--   ...  -->
    </xsl:when>

    <xsl:when test="Business">
        <!--    code to format each and every attribute of the current 
                "Contact" element, e.g.: -->
        <td class="header">Business Contact/<td>
        <td class="detail"><xsl:value-of select="./Name"/></td>
        <!--   ...  -->
    </xsl:when>

    <!--   ...  -->

    </xsl:choose>
</xsl:for-each>

但是我们越来越多的&lt;Contact&gt; 类型具有越来越多的属性,导致样式表长达一英里,所以我想将样式表简化如下:

<!-- ... loads of prologue code -->
<xsl:for-each select="Contacts/Contact/.">
    <!-- loads of formatting stuff common to each "Contact" type, but with 
         some wording that's "Contact" type dependant, e.g. -->
    <td class="header">**????** Contact</td>
    <td class="detail"><xsl:value-of select="./Name"/></td>
    <xsl:choose>
    <xsl:when **????** = "Private">
        <xsl:variable name="contactWhen">off</xsl:variable>
    </xsl:when>
    <xsl:when **????** = "Business">
        <xsl:variable name="contactWhen">office</xsl:variable>
    </xsl:when>
    <!-- ...  -->
    </xsl:choose>
     <td class="header">Contact/<td>
     <td class="detail">Only during $contactWhen hours</td>

    <xsl:choose>
    <xsl:when test="Private">
       <!-- code to format attributes specific for the current "Contact" -->
    </xsl:when>

    <xsl:when test="Business">
       <!-- code to format attributes specific for the current "Contact" -->
    </xsl:when>

    <!--    ...  -->

    </xsl:choose>
</xsl:for-each>

如果上面的代码示例中有任何拼写错误或语法错误,我的借口是,但我对 XSLT 语法不是很熟悉,并且上面的大部分代码都是为了示例而手工制作的......

我的问题是我无法获取Contact 下的节点的名称值,在此示例中为PrivateBusiness。我尝试过使用我能想到的所有变体,value-of select= 和简单的select= (a.o. "", ".", "./."Contacts/ContactContacts/Contact/.[local-]name() 甚至Field[@name='.'])。

有些尝试会产生错误,有些会导致空字符串,有些会返回父节点的名称,即Contact,有些会以单个字符串的形式返回所有下级属性的值(尽管没有属性名称)。 . :-(

我应该为**????**编写什么代码来测试当前节点的名称值,并最终根据这个测试的结果为一些变量赋值?

感谢您的帮助,

朱尔

嗨,

感谢您提供宝贵的意见,我肯定会考虑将这个样式表和其他一些类似的样式表转换为使用模板,但现在我面临着交付这个项目的压力。

我在“for-each”循环和“选择”之前尝试了所有 3 个建议,但所有 3 个都失败了:

<td class="content2" colspan="2"><xsl:value-of select="ancestor::Record/child::*[1]name"/></td>

失败并出现 XML 错误:预期的 'EOF' 找到了 'name'

<td class="content2" colspan="2"><xsl:value-of select="name()"/></td>

返回“联系人”,而不是第一个孩子的名字

<xsl:variable name="contactType">
<xsl:choose>
    <xsl:when test="Private">Private</xsl:when>
    <xsl:when test="Business">Business</xsl:when>
</xsl:choose>
<td class="content2" colspan="2"><xsl:value-of select="$contactType"/></td>
</xsl:variable>

失败并出现 XML 错误:无法解析对变量“contactType”的引用。该变量可能未定义或不在范围内。

这种行为是由内联代码而不是模板中的这些构造引起的吗?

请指教,

朱尔

【问题讨论】:

    标签: xslt value-of nodename


    【解决方案1】:

    在 XSLT 中使用 xsl:choose 打开元素名称是一种不好的代码味道:这就是模板规则的用途。我改进此代码的第一步是替换代码

    <xsl:for-each select="Contacts/Contact/.">
        <xsl:choose>
        <xsl:when test="Private">
         ...
        <xsl:when test="Business">
    

    <xsl:apply-templates select="Contacts/Contact"/>
    

    <xsl:template match="Contact[Private]">
      ...
    </xsl:template>
    
    <xsl:template match="Contact[Business]">
      ...
    </xsl:template>
    

    等等

    这并不能立即解决您在下一级重用代码的问题。当您开始在该级别执行应用模板时,就会发生这种情况。

    <xsl:template match="Contact[Private]">
      <td class="header">Private Contact</td>
      <xsl:apply-templates select="Private/*"/>
    </xsl:template>
    
    <xsl:template match="Name | Address">
      <td class="detail"><xsl:value-of select="."/></td>
    </xsl:template> 
    

    模板规则是 XSLT 的设计使用方式。这个例子很好地说明了正确使用它们所带来的好处。

    【讨论】:

    • 我将测试结果添加到原始问题中;请查看并提供建议...预先感谢,Juul
    • 如果您没有足够的时间来实现它,欢迎您忽略我的建议,但请不要让我为您调试代码。
    【解决方案2】:
    <xsl:output method="xml" indent="yes"/>
        <xsl:template match="/">
            <xsl:for-each select="Contacts/Contact">
                <xsl:for-each select="child::*[1]">
                    <td class="header"><xsl:value-of select="name()"/><xsl:text> Contact</xsl:text></td>
                    <td class="detail"><xsl:value-of select="ancestor::Contact/child::*[1]/Name"/></td>
                </xsl:for-each>
            </xsl:for-each>
        </xsl:template>
    Try It
    

    【讨论】:

    • 感谢您的宝贵建议,如果有更多时间,我一定会考虑切换到模板以简化此操作以及其他类似的样式表。
    【解决方案3】:

    将变量更改为:

    <xsl:variable name="contactWhen">
        <xsl:choose>
            <xsl:when test="Private">off</xsl:when>
            <xsl:when test="Business">office</xsl:when>
        </xsl:choose>
    </xsl:variable>
    

    【讨论】:

      【解决方案4】:

      &lt;Contact&gt; 的第一个子节点的节点名称可以使用name() 函数访问。但是,由于有条件检查节点名称,然后执行其他操作,即。在 $contactWhen 中填充不同的值或根据联系人类型格式化属性,您必须为每个唯一联系人使用 &lt;xsl:choose&gt;&lt;xsl:when test=""&gt;&lt;/xsl:when&gt;&lt;/xsl:choose&gt; 条件。

      有不同的方法来实现所需的输出,下面是一个模板方法。下面的模板匹配&lt;Contact&gt; 的第一个孩子并处理数据。

      <xsl:template match="Contact/*[1]">
          <!-- fetching node name -->
          <xsl:variable name="nodeName" select="name()" />
          <td class="header"><xsl:value-of select="concat($nodeName, ' Contact')" /></td>
          <td class="detail"><xsl:value-of select="Name" /></td>
      
          <!-- contact hours -->
          <xsl:variable name="contactWhen">
              <xsl:choose>
                  <xsl:when test="$nodeName = 'Private'">off</xsl:when>
                  <xsl:when test="$nodeName = 'Business'">office</xsl:when>
              </xsl:choose>
          </xsl:variable>
          <td class="header">Contact</td>
          <td class="detail"><xsl:value-of select="concat('Only during ', $contactWhen, ' hours')" /></td>
      
          <!-- adding contact specific nodes and attributes -->
          <xsl:choose>
              <xsl:when test="$nodeName = 'Private'">
                  <!-- code to format attributes specific for the "Private" Contact -->
                  <td class="{$nodeName}">This is a Private Contact</td>
              </xsl:when>
              <xsl:when test="$nodeName = 'Business'">
                  <!-- code to format attributes specific for the "Business" Contact -->
                  <td class="{$nodeName}">This is a Business Contact</td>
              </xsl:when>
          </xsl:choose>
      </xsl:template>
      

      输出看起来像

      <td class="header">Private Contact</td>
      <td class="detail">John Doe</td>
      <td class="header">Contact</td>
      <td class="detail">Only during off hours</td>
      <td class="Private">This is a Private Contact</td>
      
      <td class="header">Business Contact</td>
      <td class="detail">John Business</td>
      <td class="header">Contact</td>
      <td class="detail">Only during office hours</td>
      <td class="Business">This is a Business Contact</td>
      

      为了提高代码的可维护性,&lt;xsl:choose&gt; 代码 sn-ps 可以移动到不同的模板,然后使用&lt;xsl:call-template&gt; 可以调用它们进行处理。这将模块化代码并使其不那么混乱。

      【讨论】:

        【解决方案5】:

        我发现了这个问题......乔尔 2 天前的回答实际上可以解决问题;我只是在声明本身中引用了变量,而引用必须在完整声明之后进行,如下所示:

        <xsl:variable name="contactType">
        <xsl:choose>
            <xsl:when test="Private">Private</xsl:when>
            <xsl:when test="Business">Business</xsl:when>
        </xsl:choose>
        </xsl:variable>
        <td class="content2" colspan="2"><xsl:value-of select="$contactType"/></td>
        

        实际上将“私人”或“商业”添加到格式化的 XML :-)

        感谢所有做出贡献的人,同时也努力提高我有限的 XSLT 技能,

        朱尔

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-12-21
          • 2016-04-30
          • 1970-01-01
          • 2015-08-06
          • 1970-01-01
          相关资源
          最近更新 更多