XSLT - XML到XML的转换,使用xsl:for-each

时间:2022-02-04 14:27:18

The XML I'm trying to transform looks as follows. I can't post the actual file as it contains sensitive information.

我要转换的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>

Here's a simple XSLT stylesheet I wrote to extract the data from the child nodes. It works as expected, however it only returns data from ONE of the TimesheetEntryDetails

下面是我编写的一个简单的XSLT样式表,用于从子节点中提取数据。它按照预期工作,但是它只返回一个时间表细节中的数据

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

My expected output should look something like

我的期望输出应该是这样的

<DisplayText>
   SomeData
</DisplayText>

one for each TimeSheetAllocationEntryDetails node under timeheetAllocations.

在timeheetallocationentrydetails节点下的每个时间表。

Any help appreciated. I'm new to XSLT and I have a feeling I'm not using the xsl:for-each construct correctly.

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

Output I'm getting looks like (just one element ... the first)

输出看起来像(只有一个元素…第一个)

<DisplayText>
       SomeData
 </DisplayText>

2 个解决方案

#1


3  

Sample output not matching what I get

Your sample output does not match mine. You say you're getting:

你的样品产量和我的不一样。你说你得到:

<DisplayText>
    SomeData
</DisplayText>

When I run your XSL against your sample input XML, I get this:

当我对您的示例输入XML运行XSL时,我得到以下信息:

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

I do indeed get one <DisplayText> element per input <TimeAllocationEntryDetails> element, as expected. These elements are also empty, as expected.

我确实得到了每个输入一个 元素 元素,如预期的那样。正如预期的那样,这些元素也是空的。

Breaking down issues in your code

Your first template is in order:

您的第一个模板是:

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

<TimeSheetDetails> is indeed the topmost element in the input XML, and this has <timeAllocations> children.

确实是输入XML中最顶层的元素,它具有< timeallocation >子元素。

The next template has some problems:

下一个模板有一些问题:

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

One stylistic issue is the use of <xsl:for-each select="TimeAllocationEntryDetails"> here. Since the <timeAllocations> element has <TimeAllocationEntryDetails> children, it is more common to use <xsl:apply-templates/>, optionally specifying select="TimeAllocationEntryDetails", and then define a template for handling these elements -- much as you do in your first template.

一个风格上的问题是在这里使用 。由于< timeallocation >元素具有 子元素,所以更常见的方法是使用 ,可以选择性地指定select="TimeAllocationEntryDetails",然后定义一个模板来处理这些元素——就像您在第一个模板中所做的那样。

That aside, you process each <TimeAllocationEntryDetails>, and then within each one of these, you have another <xsl:for-each select=".">. This selects ., which means "the context element" -- which is just the current <TimeAllocationEntryDetails> again. This second nested for-each is thus wholly unnecessary.

除此之外,您将处理每个 ,然后在其中的每一个中,都有另一个

Within the for-each structure, you next have:

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

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

So for each <TimeAllocationEntryDetails> element, we create one <DisplayText> element. We have four <TimeAllocationEntryDetails> elements in the input XML, and four <DisplayText> elements in the output XML -- so this works just fine.

因此,对于每个 元素,我们创建一个 元素。我们在输入XML中有4个 元素,在输出XML中有4个 元素——所以这很好。

Within that <DisplayText> element, you try to get the value of the <displayText> element that is a child of the <TimeAllocationEntryDetails> context element. Since no such <displayText> element exists, this value-of produces nothing -- so the output <DisplayText> elements are all empty.

在这个 元素中,您尝试获取 元素的值,它是 上下文元素的子元素。因为不存在这样的 元素,所以这个值什么都不会产生——所以输出 元素都是空的。

UPDATE

Your edit changed your input XML. This is what I was responding to:

您的编辑更改了输入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>

With your new input XML, the <displayText> elements now exist, but not at the XPath you're using in <xsl:value-of select="displayText"/>. This again is looking for the value of the <displayText> element that is an immediate child of the context element, which in your case would be the <TimeAllocationEntryDetails> element. Since <displayText> is not an immediate child, this call to value-of produces nothing.

使用新的输入XML, 元素现在已经存在,但不在 中使用的XPath上。这同样是在寻找 元素的值,它是上下文元素的直接子元素,在您的例子中,它是 元素。因为 不是一个立即的子元素,所以调用value-of不会产生任何结果。

If you want to just output the string value of the entire context XML structure, you could just use <xsl:value-of select="."/> instead.

如果您只想输出整个上下文XML结构的字符串值,您可以使用

If you want only the value of the <displayText> element to the exclusion of anything else, either 1) specify the exact XPath to this element, as <xsl:value-of select="activity/displayText"/>, or 2) use the descendant:: axis, as <xsl:value-of select="descendant::displayText"/>, or 3) use the // shorthand for descendant::, as <xsl:value-of select=".//displayText"/> (note that you need the ., or //displayText equates to the collection of all displayText elements anywhere in the entire file).

如果您只想要 元素的值而不包含任何其他元素,要么1)指定这个元素的确切XPath,如 ,或者2)使用子代::axis,如 (注意,您需要.或//displayText等于整个文件中任何地方的所有displayText元素的集合)。

#2


1  

You can simply replace the .(current node) with a *(all child elements of the current node) in the second for-each.

您只需在第二个for-each中使用*(当前节点的所有子元素)替换.(当前节点)。

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

#1


3  

Sample output not matching what I get

Your sample output does not match mine. You say you're getting:

你的样品产量和我的不一样。你说你得到:

<DisplayText>
    SomeData
</DisplayText>

When I run your XSL against your sample input XML, I get this:

当我对您的示例输入XML运行XSL时,我得到以下信息:

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

I do indeed get one <DisplayText> element per input <TimeAllocationEntryDetails> element, as expected. These elements are also empty, as expected.

我确实得到了每个输入一个 元素 元素,如预期的那样。正如预期的那样,这些元素也是空的。

Breaking down issues in your code

Your first template is in order:

您的第一个模板是:

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

<TimeSheetDetails> is indeed the topmost element in the input XML, and this has <timeAllocations> children.

确实是输入XML中最顶层的元素,它具有< timeallocation >子元素。

The next template has some problems:

下一个模板有一些问题:

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

One stylistic issue is the use of <xsl:for-each select="TimeAllocationEntryDetails"> here. Since the <timeAllocations> element has <TimeAllocationEntryDetails> children, it is more common to use <xsl:apply-templates/>, optionally specifying select="TimeAllocationEntryDetails", and then define a template for handling these elements -- much as you do in your first template.

一个风格上的问题是在这里使用 。由于< timeallocation >元素具有 子元素,所以更常见的方法是使用 ,可以选择性地指定select="TimeAllocationEntryDetails",然后定义一个模板来处理这些元素——就像您在第一个模板中所做的那样。

That aside, you process each <TimeAllocationEntryDetails>, and then within each one of these, you have another <xsl:for-each select=".">. This selects ., which means "the context element" -- which is just the current <TimeAllocationEntryDetails> again. This second nested for-each is thus wholly unnecessary.

除此之外,您将处理每个 ,然后在其中的每一个中,都有另一个

Within the for-each structure, you next have:

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

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

So for each <TimeAllocationEntryDetails> element, we create one <DisplayText> element. We have four <TimeAllocationEntryDetails> elements in the input XML, and four <DisplayText> elements in the output XML -- so this works just fine.

因此,对于每个 元素,我们创建一个 元素。我们在输入XML中有4个 元素,在输出XML中有4个 元素——所以这很好。

Within that <DisplayText> element, you try to get the value of the <displayText> element that is a child of the <TimeAllocationEntryDetails> context element. Since no such <displayText> element exists, this value-of produces nothing -- so the output <DisplayText> elements are all empty.

在这个 元素中,您尝试获取 元素的值,它是 上下文元素的子元素。因为不存在这样的 元素,所以这个值什么都不会产生——所以输出 元素都是空的。

UPDATE

Your edit changed your input XML. This is what I was responding to:

您的编辑更改了输入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>

With your new input XML, the <displayText> elements now exist, but not at the XPath you're using in <xsl:value-of select="displayText"/>. This again is looking for the value of the <displayText> element that is an immediate child of the context element, which in your case would be the <TimeAllocationEntryDetails> element. Since <displayText> is not an immediate child, this call to value-of produces nothing.

使用新的输入XML, 元素现在已经存在,但不在 中使用的XPath上。这同样是在寻找 元素的值,它是上下文元素的直接子元素,在您的例子中,它是 元素。因为 不是一个立即的子元素,所以调用value-of不会产生任何结果。

If you want to just output the string value of the entire context XML structure, you could just use <xsl:value-of select="."/> instead.

如果您只想输出整个上下文XML结构的字符串值,您可以使用

If you want only the value of the <displayText> element to the exclusion of anything else, either 1) specify the exact XPath to this element, as <xsl:value-of select="activity/displayText"/>, or 2) use the descendant:: axis, as <xsl:value-of select="descendant::displayText"/>, or 3) use the // shorthand for descendant::, as <xsl:value-of select=".//displayText"/> (note that you need the ., or //displayText equates to the collection of all displayText elements anywhere in the entire file).

如果您只想要 元素的值而不包含任何其他元素,要么1)指定这个元素的确切XPath,如 ,或者2)使用子代::axis,如 (注意,您需要.或//displayText等于整个文件中任何地方的所有displayText元素的集合)。

#2


1  

You can simply replace the .(current node) with a *(all child elements of the current node) in the second for-each.

您只需在第二个for-each中使用*(当前节点的所有子元素)替换.(当前节点)。

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