【问题标题】:XSLT - XML to XML Transformation with xsl:for-eachXSLT - 使用 xsl:for-each 进行 XML 到 XML 的转换
【发布时间】:2017-07-07 20:23:52
【问题描述】:

我尝试转换的 XML 如下所示。我无法发布实际文件,因为它包含敏感信息。

   <?xml version="1.0" encoding="utf-16"?>
    <TimesheetDetails>
     <timeAllocations>
       <TimeAllocationEntryDetails>
         <activity>
           <displayText> Value </displayText>
         </activity>
       </TimeAllocationEntryDetails> 
       <TimeAllocationEntryDetails>
          <activity>
           <displayText> Value </displayText>
         </activity>
       </TimeAllocationEntryDetails>
       <TimeAllocationEntryDetails>
          <activity>
           <displayText> Value </displayText>
         </activity>
       </TimeAllocationEntryDetails>
       <TimeAllocationEntryDetails>
          <activity>
           <displayText> Value </displayText>
         </activity> 
       </TimeAllocationEntryDetails>
     </timeAllocations> 
    </TimesheetDetais>

这是我编写的一个简单的 XSLT 样式表,用于从子节点中提取数据。它按预期工作,但是它只返回来自 TimesheetEntryDetails 之一的数据

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/TimeSheetDetails">
   <xsl:apply-templates select="timeAllocations"/>
</xsl:template>

<xsl:template match="timeAllocations">

<xsl:for-each select="TimeAllocationEntryDetails">
      <xsl:for-each select=".">  
          <DisplayText>
          <xsl:value-of select="displayText"/>
          </DisplayText>
      </xsl:for-each>

</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

我的预期输出应该类似于

<DisplayText>
   SomeData
</DisplayText>

timeheetAllocations 下的每个 TimeSheetAllocationEntryDetails 节点都有一个。

任何帮助表示赞赏。我是 XSLT 的新手,我感觉我没有正确使用 xsl:for-each 构造。

我得到的输出看起来像(只有一个元素......第一个)

<DisplayText>
       SomeData
 </DisplayText>

【问题讨论】:

  • 没有更多节点而不是“..一些子节点”,很难猜测应该将什么转换为什么......
  • 我将添加一些实际的节点名称。

标签: xml xslt


【解决方案1】:

示例输出与我得到的不匹配

您的示例输出与我的不匹配。你说你得到了:

<DisplayText>
    SomeData
</DisplayText>

当我针对您的示例输入 XML 运行您的 XSL 时,我得到了:

<DisplayText/><DisplayText/><DisplayText/><DisplayText/>

确实,正如预期的那样,每个输入 &lt;TimeAllocationEntryDetails&gt; 元素都有一个 &lt;DisplayText&gt; 元素。正如预期的那样,这些元素也是空的。

分解代码中的问题

你的第一个模板是有序的:

<xsl:template match="/TimeSheetDetails">
    <xsl:apply-templates select="timeAllocations"/>
</xsl:template>

&lt;TimeSheetDetails&gt; 确实是输入 XML 中最顶层的元素,它有 &lt;timeAllocations&gt; 子元素。

下一个模板有一些问题:

<xsl:template match="timeAllocations">
    <xsl:for-each select="TimeAllocationEntryDetails">
        <xsl:for-each select=".">  
            <DisplayText>
                <xsl:value-of select="displayText"/>
            </DisplayText>
        </xsl:for-each>
    </xsl:for-each>
</xsl:template>

一个风格问题是这里使用&lt;xsl:for-each select="TimeAllocationEntryDetails"&gt;。由于&lt;timeAllocations&gt; 元素有&lt;TimeAllocationEntryDetails&gt; 子元素,因此更常见的是使用&lt;xsl:apply-templates/&gt;,可选择指定select="TimeAllocationEntryDetails",然后定义一个模板来处理这些元素——就像您在第一个模板中所做的那样。

除此之外,您处理每个&lt;TimeAllocationEntryDetails&gt;,然后在每个&lt;xsl:for-each select="."&gt; 中,您有另一个&lt;xsl:for-each select="."&gt;。这将选择.,这意味着“上下文元素”——这又是当前的&lt;TimeAllocationEntryDetails&gt;。第二个嵌套的for-each 因此完全没有必要。

在for-each 结构中,接下来有:

        <DisplayText>
            <xsl:value-of select="displayText"/>
        </DisplayText>

所以对于每个&lt;TimeAllocationEntryDetails&gt; 元素,我们创建一个&lt;DisplayText&gt; 元素。我们在输入 XML 中有四个 &lt;TimeAllocationEntryDetails&gt; 元素,在输出 XML 中有四个 &lt;DisplayText&gt; 元素——所以这很好用。

在该&lt;DisplayText&gt; 元素中,您尝试获取&lt;displayText&gt; 元素的值,该元素是&lt;TimeAllocationEntryDetails&gt; 上下文元素的子元素。因为不存在这样的&lt;displayText&gt; 元素,所以这个value-of 什么也不会产生——所以输出&lt;DisplayText&gt; 元素都是空的。

更新

您的编辑更改了您的输入 XML。这就是我的回应:

<TimesheetDetails>
    <timeAllocations>
        <TimeAllocationEntryDetails>
            .. some child nodes 
        </TimeAllocationEntryDetails> 
        <TimeAllocationEntryDetails>
            .. some child nodes 
        </TimeAllocationEntryDetails>
        <TimeAllocationEntryDetails>
            .. some child nodes 
        </TimeAllocationEntryDetails>
        <TimeAllocationEntryDetails>
            .. some child nodes 
        </TimeAllocationEntryDetails>
    </timeAllocations> 
</TimesheetDetails>

使用您的新输入 XML,&lt;displayText&gt; 元素现在存在,但不在您在 &lt;xsl:value-of select="displayText"/&gt; 中使用的 XPath 中。这又是在寻找 &lt;displayText&gt; 元素的值,它是上下文元素的直接子元素,在您的情况下是 &lt;TimeAllocationEntryDetails&gt; 元素。由于&lt;displayText&gt; 不是直接子级,因此对value-of 的调用不会产生任何结果。

如果您只想输出整个上下文 XML 结构的字符串值,您可以使用 &lt;xsl:value-of select="."/&gt; 代替。

如果您只希望 &lt;displayText&gt; 元素的值排除其他任何内容,则可以 1) 指定该元素的确切 XPath,如 &lt;xsl:value-of select="activity/displayText"/&gt;,或 2) 使用 descendant:: 轴,如 @ 987654362@,或 3) 使用 // 简写为 descendant::,如 &lt;xsl:value-of select=".//displayText"/&gt;(请注意,您需要 . 或 //displayText 等同于在整个文件中的任何位置收集所有 displayText 元素)。

【讨论】:

    【解决方案2】:

    您可以在第二个for-each 中简单地将.(当前节点)替换为*(当前节点的所有子元素)。

    <xsl:for-each select="TimeAllocationEntryDetails">
      <xsl:for-each select="*">          <!-- here -->
        ...
      </xsl:for-each>
    </xsl:for-each>
    

    【讨论】:

    • 明确地说,XPath 表达式* 指定当前节点的所有child 元素。简单地说“所有元素”很容易被误解。
    • @JohnBollinger:我相应地改变了它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-02
    相关资源
    最近更新 更多