有条件地将子节点移动到属性

Move child Nodes to attributes conditionally

我正在尝试转换此文档,但我对 xslt 还很陌生,并且在尝试将其正确处理时获得了很多乐趣。核心节点(为简单起见被截断)如下所示

<Product prod_id="6352">
    <brandId>221</brandId>
    <brand>Oscar Mayer</brand>
    <images>
       <smallimage>text</simage>
       <medimage>text</medimage>
       <largeimage>text</limage>
    </images>
    <nutrition>
        <nutritionShow>Y</nutritionShow>
        <servingSize>1 SLICE</servingSize>
        <servingsPerContainer>12</servingsPerContainer>
        <totalCalories>60</totalCalories>
        <fatCalories>35</fatCalories>
        <totalFat>4</totalFat>
        <totalFatPercent>6</totalFatPercent>
        <totalFatUnit>g</totalFatUnit>
        <saturatedFat>1.5</saturatedFat>
        <saturatedFatPercent>8</saturatedFatPercent>
        <saturatedFatUnit>g</saturatedFatUnit>
        <transFat>0</transFat>
        <transFatUnit>g</transFatUnit>
        <cholesterolUnit>mg</cholesterolUnit>
    </nutrition>
    <prodId>6352</prodId>
</Product>

最后我想将逻辑分组的子节点作为具有适当属性名称的单个节点。

最终结果应该是这样的

<Product prod_id="6352">
<brandId>221</brandId>
<brand>Oscar Mayer</brand>
<images>
   <smallimage>text</smallimage>
   <medimage>text</medimage>
   <largeimage>text</largeimage>
</images>
<nutrition>
    <nutritionShow>Y</nutritionShow>
    <servingSize>1 SLICE</servingSize>
    <servingsPerContainer>12</servingsPerContainer>
    <totalCalories>60</totalCalories>
    <fatCalories>35</fatCalories>
    <totalFat amount="4" percent="6" unit="g" />
    <saturatedFat amount="1.5" percent="8" unit="g"/>
    <transFat amount="0" unit="g"</>
</nutrition>
<prodId>6352</prodId>

一些主要功能是

  1. 将相似的属性分组(注意 saturatedFat 和 transFat ... 略有不同)我有这些集合的离散列表。您可以使用列表或基于关系的更动态的东西,但要注意差异。
  2. 保留其他(不可分组)属性
  3. 忽略数量不足的群体attribute/only有单位属性(注意胆固醇)

提前感谢您帮助我理解这个相当复杂的转换。

一种可能的解决方案是遵循 XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes" />
 <xsl:strip-space elements="*"/>
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="nutrition/*">
    <xsl:variable name="cName" select="name()"/>
    <xsl:choose>
      <xsl:when test="following-sibling::node()[name()=concat($cName,'Unit')]">
        <xsl:copy>
          <xsl:attribute name="amount">
            <xsl:value-of select="."/>
          </xsl:attribute>
          <xsl:if test="following-sibling::node()[name()=concat($cName,'Percent')]">
            <xsl:attribute name="percent">
              <xsl:value-of select="following-sibling::node()[name()=concat($cName,'Percent')]"/>
            </xsl:attribute>
          </xsl:if>
          <xsl:attribute name="unit">
            <xsl:value-of select="following-sibling::node()[name()=concat($cName,'Unit')]"/>
          </xsl:attribute> 
        </xsl:copy>
      </xsl:when>
      <xsl:when test="contains(name() ,'Unit') or contains(name() ,'Percent')"/>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates />
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

当应用到您的输入时 XML 产生输出

<Product prod_id="6352">
  <brandId>221</brandId>
  <brand>Oscar Mayer</brand>
  <images>
    <smallimage>text</smallimage>
    <medimage>text</medimage>
    <largeimage>text</largeimage>
  </images>
  <nutrition>
    <nutritionShow>Y</nutritionShow>
    <servingSize>1 SLICE</servingSize>
    <servingsPerContainer>12</servingsPerContainer>
    <totalCalories>60</totalCalories>
    <fatCalories>35</fatCalories>
    <totalFat amount="4" percent="6" unit="g"></totalFat>
    <saturatedFat amount="1.5" percent="8" unit="g"></saturatedFat>
    <transFat amount="0" unit="g"></transFat>
  </nutrition>
  <prodId>6352</prodId>
</Product>

第一个模板是 Identity transform 并复制所有节点和属性而不做任何更改。
第二个模板匹配 nutrition.
的所有子 elements/nodes 如果当前元素有一个本地名称与当前本地名称匹配并以 Unit

结尾的后续兄弟元素
<xsl:when test="following-sibling::node()[name()=concat($cName,'Unit')]">

当前节点必须是包含 amount.
的节点 当前节点的值被写为 amount 属性的值

<xsl:attribute name="amount">
    <xsl:value-of select="."/>
</xsl:attribute>

如果存在匹配 Percent 的下一个兄弟姐妹

<xsl:if test="following-sibling::node()[name()=concat($cName,'Percent')]">

Percent属性相应地写成:

<xsl:attribute name="percent">
    <xsl:value-of select="following-sibling::node()[name()=concat($cName,'Percent')]"/>
  </xsl:attribute>

同样适用于 Unit,无需预先检查是否存在匹配的 Unit(如有必要,可以添加)。

<xsl:when test="contains(name() ,'Unit') or contains(name() ,'Percent')"/>

删除已写入属性的UnitPercent节点以及cholesterolUnit.
最后,仅复制所有其他不可分组的 nutrition 元素:

<xsl:otherwise>
  <xsl:copy>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:otherwise> 

继续评论...

The example shows each of the 3 types of attributes. The others are as one would expect ... you see saturated fat also could expct unsaturated and monounsaturated and polyunsaturated There are 5-12 in each category. Categories being 1. amount, unit and percent 2. amount and unit 3. standalone

就我个人而言,我更喜欢把已知的东西拼出来,所以对于给定的例子:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>


<!-- category #1: amount, unit and percent -->
<xsl:template match="totalFat">
    <totalFat amount="{.}" percent="{../totalFatPercent}" unit="{../totalFatUnit}" />
</xsl:template>

<xsl:template match="saturatedFat">
    <saturatedFat amount="{.}" percent="{../saturatedFatPercent}" unit="{../saturatedFatUnit}" />
</xsl:template>


<!-- category #2: amount and percent -->
<xsl:template match="transFat">
    <transFat amount="{.}" unit="{../transFatUnit}" />
</xsl:template>


<!-- suppress all units and percents -->
<xsl:template match="totalFatPercent | totalFatUnit | saturatedFatPercent | saturatedFatUnit | transFatUnit | cholesterolUnit | cholesterolPercent"/>

</xsl:stylesheet>

请注意,类别 #3 由 身份转换 模板处理,无需例外。


另请注意,已知出现在每个产品中的项目不需要它们自己的模板;您可以将它们作为匹配 nutrition 的模板中的 文字结果元素 写出来,并将它们的名称添加到抑制空模板中。