Преобразование повторяющихся элементов XML, содержащих различные значения, с использованием XSLT - PullRequest
0 голосов
/ 04 февраля 2019

Hello Stackoverflowers -

Это моя первая публикация.Я столкнулся с разочаровывающей проблемой XSLT, которую, несмотря на различные предполагаемые решения, не удалось решить.Сценарий представляет собой базовое преобразование одного XML-документа (# 1) в формат другого (# 2) для обеспечения взаимодействия между двумя системами.

XML doc # 1 содержит несколько повторяющихся элементов, каждый из которыхсодержит отдельное значение.Эти повторяющиеся элементы необходимо преобразовать в альтернативный элемент для документа № 2 и включить соответствующее значение из документа № 1.

Несмотря на множество экспериментов, я не могу заставить это работать.Соответствующие элементы в документе № 1:

 <mods:extension> 
  <rx:funder>Funder A</rx:funder> 
  <rx:projectid>Funder A code number</rx:projectid> 
  <rx:funder>Funder B</rx:funder> 
  <rx:projectid>Funder B code number</rx:projectid> 
 </mods:extension> 

I необходимо в XML для преобразования в документ № 2 для преобразования следующим образом:

<project_input>
  <item>
    <project>Funder A code number</project>
    <funder_name>Funder A</funder_name>
  </item>
  <item>
    <project>Funder B code number</project>
    <funder_name>Funder B</funder_name>
  </item>
</project_input>

Но, к сожалению, результат моего преобразования всегда является вариантом этого:

<project_input>
  <item>
    <project>Funder A code numberFunder B code number</project>
    <funder_name>Funder AFunder B</funder_name>
  </item>
</project_input>

... со значениями, вставленными в один элемент.

Преобразование в данный момент выглядит следующим образом:

<xsl:if test="v3:extension">
<project_input>
<item>      
<xsl:for-each select="v3:extension/rx:projectid">
    <project>
        <xsl:value-of select="."/>   
    </project>
</xsl:for-each> 
<xsl:for-each select="v3:extension/rx:funder">
    <funder_name>
        <xsl:value-of select="."/>      
    </funder_name>
</xsl:for-each>
</item> 
</project_input>
</xsl:if>

Таким образом, проблема в том, что, хотя правильные значения успешно повторяются в doc # 1, они не выводятся должным образом в doc # 2,

Я пытался использовать варианты, включающие в себя различные значения, но это тоже не удалось.Разочарование в том, что я делал такие вещи раньше, но не могу на всю жизнь вспомнить!В стеке уже описаны похожие сценарии, но предложенное там решение, похоже, не работает в этом случае.

Кто-нибудь может указать на очевидное, что я скучаю?Спасибо

ОБНОВЛЕННЫЙ вопрос - см. Комментарии ниже.Это должно работать в XSLT 1.0.Мне удалось что-то сделать вместе.Это наполовину работает, поскольку правильные данные извлекаются, упорядочиваются и группируются, но указанные элементы XML отсутствуют.Возможно, что-то действительно очевидное я пропустил.

Кто-нибудь может мне помочь, пожалуйста?Вот XSLT 1.0:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:mods="http://www.loc.gov/mods/v3" xmlns:rx="http://example.com/rx" 
version="1.0">
<xsl:output indent="yes"/>
<xsl:key use="rx:funder" match="mods:mods/mods:extension" name="groups" />
<xsl:template match="/">
<xsl:apply-templates select="mods:mods/mods:extension" />
</xsl:template>
<xsl:template match="mods:mods">
<rioxx2_project_output>
  <xsl:for-each select="/mods:extension[generate-id(.)=generate- 
  id(key('groups', rx:funder))]">
    <item>
      <funder_name>
        <xsl:value-of select="mods:extension/rx:funder/text()" />
      </funder_name>
      <project>
        <xsl:value-of select="mods:extension/rx:projectid/text()" />
      </project>
     </item>
     </xsl:for-each>
     </project_input>
   </xsl:template>
  </xsl:stylesheet>

Вот в XSLT Fiddle: https://xsltfiddle.liberty -development.net / bFN1y8S / 1

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019
    <xsl:strip-space elements="*"/>
        <xsl:template match="node()">
            <xsl:copy>
                <xsl:apply-templates />
            </xsl:copy>
        </xsl:template>
        <xsl:template match="mods:extension">
            <xsl:element name="project_input">
                <xsl:for-each-group select="node()" group-starting-with="rx:funder">

                       <item>
                           <project>
                               <xsl:value-of select="./following-sibling::*[1][self::rx:projectid]"/>
                       </project>
                           <funder_name>
                               <xsl:value-of select="."/>
                           </funder_name>

                       </item>

                </xsl:for-each-group>
            </xsl:element>  
        </xsl:template>
Kindly chek it.
0 голосов
/ 04 февраля 2019

Это похоже на классический пример использования for-each-group group-starting-with или group-ending-with, поэтому в вашем случае используйте, например,

  <xsl:template match="mods:extension">
      <project_input>
          <xsl:for-each-group select="*" group-starting-with="rx:funder">
              <item>
                  <project>
                      <xsl:value-of select="current-group()[2]"/>
                  </project>
                  <funder_name>
                      <xsl:value-of select="."/>
                  </funder_name>
              </item>
          </xsl:for-each-group>
      </project_input>
  </xsl:template>

, что предполагает некоторую регулярность в последовательности дочерних элементов модов: extensionс rx:funder, чтобы иметь возможность идентифицировать группу.

Полный пример https://xsltfiddle.liberty -development.net / bFN1y8S с использованием XSLT 3 (для процессора XSLT 2 вам нужно будет прописатьшаблон преобразования идентификаторов вместо использования xsl:mode)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mods="http://example.com/mods"
    xmlns:rx="http://example.com/rx"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:output indent="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="mods:extension">
      <project_input>
          <xsl:for-each-group select="*" group-starting-with="rx:funder">
              <item>
                  <project>
                      <xsl:value-of select="current-group()[2]"/>
                  </project>
                  <funder_name>
                      <xsl:value-of select="."/>
                  </funder_name>
              </item>
          </xsl:for-each-group>
      </project_input>
  </xsl:template>

</xsl:stylesheet>

Для достижения аналогичного подхода в XSLT 1 одним из способов является использование ключа, который назначает любые элементы, отличные от rx:funder, на идентификаторе предыдущегородной брат rx:funder:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:mods="http://example.com/mods"
    xmlns:rx="http://example.com/rx"
    exclude-result-prefixes="mods rx"
    version="1.0">

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

  <xsl:key name="group" match="mods:extension/*[not(self::rx:funder)]" 
     use="generate-id(preceding-sibling::rx:funder[1])"/>

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

  <xsl:template match="mods:extension">
      <project_input>
          <xsl:for-each select="rx:funder">
              <item>
                  <project>
                      <xsl:value-of select="key('group', generate-id())[self::rx:projectid]"/>
                  </project>
                  <funder_name>
                      <xsl:value-of select="."/>
                  </funder_name>
              </item>
          </xsl:for-each>
      </project_input>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty -development.net / bFN1y8S / 3

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