非结构化(Adlib)XML使用XSLT对结构化XML进行分组,对类似节点进行分组

时间:2011-08-30 10:41:42

标签: xml xslt

我有一个非结构化的Adlib XML文件,其中包含以下格式的数据:

<record>
    ...
    <dimension.type>height</dimension.type>
    <dimension.type>width</dimension.type>
    <dimension.type>height</dimension.type>
    <dimension.type>width</dimension.type>
    <dimension.type>depth</dimension.type>
    <dimension.notes>without frame</dimension.notes>
    <dimension.notes>without frame</dimension.notes>
    <dimension.notes>with frame</dimension.notes>
    <dimension.notes>with frame</dimension.notes>
    <dimension.notes>with frame</dimension.notes>
    <dimension.value>28.0</dimension.value>
    <dimension.value>47.9</dimension.value>
    <dimension.value>41.4</dimension.value>
    <dimension.value>62.9</dimension.value>
    <dimension.value>8.0</dimension.value>
    ...
</record>

我想做的是将其转换为以下格式:

<record>
    ...
    <dimension>
       <notes>without frame</notes>
       <height>28.0</height>
       <width>47.9</width>
    </dimension>
    <dimension>
       <notes>with frame</notes>
       <height>41.4</height>
       <width>62.9</width>
       <depth>8.0</depth>
    </dimension>
    ...
</record>

但是我有点卡住了,因为这些节点引用了同一位置的其他节点中的信息。我确实提出了以下XSLT:

<xsl:template match="dimension.value">
  <xsl:variable name="pos" select="position()"/>
  <dimension>
  <xsl:choose>
    <xsl:when test="../dimension.type[$pos] = 'height'">
      <height><xsl:value-of select="."/></height>
    </xsl:when>
    <xsl:when test="../dimension.type[$pos] = 'width'">
      <width><xsl:value-of select="."/></width>
    </xsl:when>
    <xsl:when test="../dimension.type[$pos] = 'depth'">
      <depth><xsl:value-of select="."/></depth>
    </xsl:when>
  </xsl:choose>
  <notes>
    <xsl:value-of select="../dimension.notes[$pos]"/>
  </notes>
  </dimension>
</xsl:template>

以下列格式生成数据:

<dimension>
   <height>28.0</height>
   <notes>without frame</notes>
</dimension>
<dimension>
    <width>47.9</width>
    <notes>without frame</notes>
</dimension>
<dimension>
    <height>41.4</height>
    <notes>with frame</notes>
</dimension>
<dimension>
    <width>62.9</width>
    <notes>with frame</notes>
</dimension>
<dimension>
    <depth>8.0</depth>
    <notes>with frame</notes>
</dimension>

但是这不会对note部分进行分组,这会使处理结果更容易一些(现在我在代码中解决了这个问题,但是XSLT必须有一种方法来实现它,对吧?)。任何帮助(指向相关信息或相关XSLT片段)将不胜感激......

BTW我翻译了部分XML / XSLT以便于理解,when:test实际检查荷兰语描述并将它们转换为等效的英语标签......

2 个答案:

答案 0 :(得分:2)

这是一个XSLT 1.0解决方案:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

  <xsl:template match="record">
    <xsl:copy>
      <xsl:apply-templates select="dimension.notes[1]" mode="group"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="dimension.notes" mode="group">
    <dimension>
      <notes>
        <xsl:value-of select="."/>
      </notes>
      <xsl:apply-templates select="."/>
    </dimension>
    <xsl:apply-templates select="following-sibling::dimension.notes[not(. = current())][1]" mode="group"/>
  </xsl:template>

  <xsl:template match="dimension.notes">
    <xsl:variable name="pos">
      <xsl:number/>
    </xsl:variable>
    <xsl:apply-templates select="../dimension.type[position() = $pos]">
      <xsl:with-param name="pos" select="$pos"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="following-sibling::dimension.notes[1][. = current()]"/>
  </xsl:template>

  <xsl:template match="dimension.type">
    <xsl:param name="pos"/>
    <xsl:element name="{.}">
      <xsl:value-of select="../dimension.value[position() = $pos]"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

以下是使用XSLT 2.0的一些示例:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs"
  version="2.0">

  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

  <xsl:template match="record">
    <xsl:copy>
      <xsl:for-each-group select="dimension.notes" group-adjacent=".">
        <dimension>
          <notes>
            <xsl:value-of select="current-grouping-key()"/>
          </notes>
          <xsl:apply-templates select="current-group()"/>
        </dimension>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="dimension.notes">
    <xsl:variable name="pos" as="xs:integer">
      <xsl:number/>
    </xsl:variable>
    <xsl:apply-templates select="../dimension.type[position() eq $pos]">
      <xsl:with-param name="pos" select="$pos"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="dimension.type">
    <xsl:param name="pos"/>
    <xsl:element name="{.}">
      <xsl:value-of select="../dimension.value[position() eq $pos]"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

我不确定它是否解决了您的问题,因为您的示例中的“...”可能需要更复杂的编码,具体取决于那里可能出现的元素类型以及您想要对它们执行的操作。