0

我们有(可能是异常格式的)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>

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

<!-- ... 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/Contact, Contacts/Contact/.,[local-]name()甚至Field[@name='.'])。

有些尝试会产生错误,有些会导致空字符串,有些会返回父节点的名称 ie 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”的引用。该变量可能未定义或不在范围内。

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

请指教,

尤尔

4

5 回答 5

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 设计使用的方式。这个例子很好地说明了正确使用它们所获得的好处。

于 2018-07-13T22:07:41.163 回答
0

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

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

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

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

于 2018-07-13T09:15:34.623 回答
0
<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
于 2018-07-13T07:11:15.650 回答
0

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

<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 技能,

尤尔

于 2018-07-16T08:52:49.383 回答
0

将您的变量更改为:

<xsl:variable name="contactWhen">
    <xsl:choose>
        <xsl:when test="Private">off</xsl:when>
        <xsl:when test="Business">office</xsl:when>
    </xsl:choose>
</xsl:variable>
于 2018-07-13T07:13:19.170 回答