XSL для итерации плоской структуры XML - PullRequest
2 голосов
/ 23 января 2012

У меня есть следующий XML:

<p:persons>
<p:surname>Surname1</p:surname>
<p:forename>Forename1</p:forname>
<p:surname>Surname2</p:surname>
</p:persons>

Обе фамилии и имена не являются обязательными. Я хотел бы преобразовать этот XML в HTML или обычный текст и отобразить что-то вроде:

-- Person1 --
surname: Surname1
forename: Forename1

-- Person2 --
surname: Surname2
forename: UNKNOWN

Обратите внимание, что XML также может выглядеть следующим образом, поскольку имя и фамилия необязательны:

<p:persons>
<p:forename>Forename1</p:surname>
<p:surname>Surname1</p:surname>
<p:forename>Forename2</p:forname>
</p:persons>

Спасибо!

Ответы [ 3 ]

2 голосов
/ 23 января 2012

Хотя я думаю, что эта спецификация сильно неверна, вот фрагмент кода, который делает то, что вы хотите:

<xsl:template name="writePersons">
  <xsl:param name="list"/>
  <xsl:param name="index" select="1"/>

  <xsl:if test="count($list/p:forename[$index]|$list/p:surname[$index]) &gt; 0">
    <xsl:text>-- Person</xsl:text>
    <xsl:value-of select="$index"/>
    <xsl:text> --&#x0A;surname: </xsl:text>
    <xsl:choose>
      <xsl:when test="$list/p:surname[$index]">
        <xsl:value-of select="$list/p:surname[$index]"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>UNKNOWN</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:text>&#x0A;forename: </xsl:text>
    <xsl:choose>
      <xsl:when test="$list/p:forename[$index]">
        <xsl:value-of select="$list/p:forename[$index]"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>UNKNOWN</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:text>&#x0A;&#x0A;</xsl:text>
    <xsl:call-template name="writePersons">
      <xsl:with-param name="index" select="$index + 1"/>
      <xsl:with-param name="list" select="$list"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>
0 голосов
/ 23 января 2012

Я брошу свою шляпу в кольцо, даже если вы приняли один ответ ... потому что до сих пор я не думаю, что какое-либо из других решений является правильным (хотя одно из них соответствует заданному примеру вывода).

Вот моя реализация:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="2.0" xmlns:p="p">
   <xsl:output method="text" />

   <xsl:template match="/p:persons">
      <xsl:apply-templates select="*[1]">
         <xsl:with-param name="i" select="1" />
      </xsl:apply-templates>
   </xsl:template>

   <!-- This template is applied to an element whenever it is
      for a new person, i.e. when there is not a preceding
      sibling element belonging to the same person. -->
   <xsl:template match="p:forename | p:surname">
      <xsl:param name="i"/>
      <xsl:variable name="next" select="following-sibling::*[1]"/>
      <xsl:variable name="surname" select="if (self::p:surname) then . else
            if ($next[self::p:surname]) then $next else 'UNKNOWN'"/>
      <xsl:variable name="forename" select="if (self::p:forename) then . else
         if ($next[self::p:forename]) then $next else 'UNKNOWN'"/>
      <xsl:value-of select="concat(if ($i > 1) then '&#10;' else '',
         '--- Person', $i, '---&#10;surname: ',
         $surname, '&#10;forename: ', $forename, '&#10;')"/>

      <xsl:choose>
         <xsl:when test="local-name($next) = local-name(.)">
            <!-- Only one name is supplied. Start processing
            the next sibling as a new person. -->
            <xsl:apply-templates select="following-sibling::*[1]">
               <xsl:with-param name="i" select="$i + 1"/>
            </xsl:apply-templates>
         </xsl:when>
         <xsl:otherwise>
            <!-- Both names are supplied. Skip the second name
            and start processing the sibling after it.-->
            <xsl:apply-templates select="following-sibling::*[2]">
               <xsl:with-param name="i" select="$i + 1"/>
            </xsl:apply-templates>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

Моя интерпретация неполной спецификации такова: начинайте с первого элемента имени / фамилии. Если первые два элемента - это имя и фамилия (в любом порядке), используйте как для первого лица, так и начните обработку третьего элемента как новый человек.

В противном случае используйте первый элемент и введите «НЕИЗВЕСТНО» для имени, которое не указано. Затем начните обработку следующего элемента как нового человека.

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

Первый приведенный выше пример дает желаемый результат. Второй (с исправленными тегами) дает:

--- Person1---
surname: Surname1
forename: Forename1

--- Person2---
surname: UNKNOWN
forename: Forename2

Более полный пример:

<p:persons xmlns:p="p">
   <p:surname>Surname1</p:surname>
   <p:forename>Forename1</p:forename>
   <p:surname>Surname2</p:surname>
   <p:surname>Surname3</p:surname>
   <p:surname>Surname4</p:surname>
   <p:forename>Forename4</p:forename>
   <p:forename>Forename5</p:forename>
   <p:surname>Surname5</p:surname>
</p:persons>

дает

--- Person1---
surname: Surname1
forename: Forename1

--- Person2---
surname: Surname2
forename: UNKNOWN

--- Person3---
surname: Surname3
forename: UNKNOWN

--- Person4---
surname: Surname4
forename: Forename4

--- Person5---
surname: Surname5
forename: Forename5
0 голосов
/ 23 января 2012

Вот более простое решение (без явной рекурсии, нет xsl:choose, xsl:otherwise, xsl:call-template, xsl:param, xsl:with-param):

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

 <xsl:param name="pSurnameUnknown">surname: UNKNOWN</xsl:param>

 <xsl:template match=
 "p:surname
    [not(preceding-sibling::*[1][self::p:forename])]">

     --- Person<xsl:text/>
     <xsl:number level="single" count=
     "p:forename|p:surname[not(preceding-sibling::*[1]
                                     [self::p:forename])]"/>
     <xsl:text>---</xsl:text>
     forename: UNKNOWN
     surname: <xsl:value-of select="."/>
 </xsl:template>

 <xsl:template match="p:forename">

     --- Person<xsl:text/>
     <xsl:number level="single" count=
     "p:forename|p:surname[not(preceding-sibling::*[1]
                                    [self::p:forename])]"/>
     <xsl:text>---</xsl:text>
     forename: <xsl:value-of select="."/>
   <xsl:if test="not(following-sibling::*[1][self::p:surname])">
     <xsl:copy-of select="$pSurnameUnknown"/>
   </xsl:if>
   <xsl:apply-templates mode="second"
        select="following-sibling::*[1][self::p:surname]"/>
 </xsl:template>

 <xsl:template match="p:surname" mode="second">
     surname: <xsl:value-of select="."/>
 </xsl:template>
 <xsl:template match="p:surname"/>
</xsl:stylesheet>

при применении к этому XML-документу (предоставленный, правильно оформленный):

<p:persons xmlns:p="p">
    <p:surname>Surname1</p:surname>
    <p:forename>Forename2</p:forename>
    <p:surname>Surname2</p:surname>
</p:persons>

желаемый, правильный результат получается :

 --- Person1---
 forename: UNKNOWN
 surname: Surname1

 --- Person2---
 forename: Forename2
 surname: Surname2

Объяснение

  1. Правильное использование шаблонов и сопоставление с образцом.

  2. Правильное использование <xsl:number>

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