Неструктурированный (Adlib) XML в структурированный XML с использованием XSLT, группирующий похожие узлы - PullRequest
1 голос
/ 30 августа 2011

У меня есть неструктурированный файл 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>

Но это не делает группировку по части заметки, которая бы выполняла обработкурезультат немного проще (теперь я решаю это в коде, но у XSLT должен быть способ сделать это, верно?).Любая помощь (указатели на соответствующую информацию или соответствующие фрагменты XSLT) будет принята с благодарностью ...

Кстати, я перевел части XML / XSLT, чтобы упростить понимание, когда: test фактически проверяет описания на голландскоми превращает их в эквивалентные английские теги ...

Ответы [ 2 ]

2 голосов
/ 30 августа 2011

Вот решение 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 голос
/ 30 августа 2011

Вот пример использования 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>

Я не уверен, что это решит вашу проблему, так как «...» в вашем примере может потребовать более сложного кодирования, в зависимости от того, какие элементыточно может произойти там и что вы хотите с ними делать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...