XSLT-фильтрующие узлы по условной логике - PullRequest
2 голосов
/ 19 августа 2011

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

<StudentSet>
 <Student>
  <StudentName>Kapil</StudentName>
  <Subject>English</Subject>
  <Subject>History</Subject>
  <Subject>Mathematics</Subject>
  <Subject>Economics</Subject>
 </Student>
 <Student>
  <StudentName>Atul</StudentName>
  <Subject>English</Subject>
  <Subject>History</Subject>
  <Subject>Economics</Subject>
 </Student>
 <Student>
  <StudentName>Nisha</StudentName>
  <Subject>English</Subject>
  <Subject>History</Subject>
 </Student>
 <Student>
  <StudentName>Manish</StudentName>
 </Student>
</StudentSet>

Применяются следующие правила.

1) Если учащийся записался на математику, укажите его имя и математику

2) Если студент поступил в экономику, но не в математику, покажите имя и экономику студента

3) Если студент не учился ни на математике, ни на экономике, выберите имя студента и какой-либо один предмет.

4) Если студент не записался на какой-либо предмет, не выбирайте его.

Я хочу получить следующий вывод: -

<StudentSet>
 <Student>
  <StudentName>Kapil</StudentName>
  <Subject>Mathematics</Subject>
 </Student>
 <Student>
  <StudentName>Atul</StudentName>
  <Subject>Economics</Subject>
 </Student>
 <Student>
  <StudentName>Nisha</StudentName>
  <Subject>English</Subject>
 </Student>
</StudentSet>

Может кто-нибудь помочь, какой XSLT можно использовать для этого?

Ответы [ 2 ]

4 голосов
/ 19 августа 2011

Самый простой способ - просто использовать шаблоны с предикатами, например:

<xsl:template match="Student[Subject='Economics']">
  <!-- Handle economics students -->
</xsl:template>

<xsl:template match="Student[Subject='Mathematics']">
  <!-- Handle maths students -->
  <!-- overrides maths+econ students, as it comes later -->
</xsl:template>

<xsl:template match="Student[not(Subject='Economics') and not(Subject='Mathematics')]">
  <!-- Handle other students. Use 'Subject[1]' to refer to first subject -->
</xsl:template>

<!-- Handle students with no subject, outputting nothing. -->
<xsl:template match="Student[not(Subject)]" />

В каждом случае вы, вероятно, захотите сделать

<xsl:copy> <!-- copies the 'Student' element -->
  <xsl:copy-of select="StudentName" />
  <xsl:copy-of select="Subject[text()='subjectname']" />
  <!-- or -->
  <xsl:copy-of select="Subject[1]" /> <!-- first subject in list -->
</xsl:copy>

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

<xsl:template match="Student[Subject='Mathematics']/Subject[not(text()='Mathematics')]" />
<xsl:template match="Student[Subject='Economics' and not(Subject='Mathematics')]/Subject[not(text()='Economics')]" />
<xsl:template match="Student[not(Subject='Economics') and not(Subject='Mathematics')]/Subject[position() != 1]" />
<xsl:template match="Student[not(Subject)]" />

Что они делают, так это описывают элементы, которые не должны выводиться.Например, первый находит любые элементы Student, которые имеют предмет математики, и внутри него находит любые элементы Subject, которые не имеют текста «Математика».Шаблон соответствует этим узлам и ничего не выводит.На самом деле вы могли бы сделать это с помощью одного большого правила шаблона, например:

<xsl:template match="
  Student[Subject='Mathematics']/Subject[not(text()='Mathematics')]
| Student[Subject='Economics' and not(Subject='Mathematics')]/Subject[not(text()='Economics')]
| Student[not(Subject='Economics') and not(Subject='Mathematics')]/Subject[position() != 1]
| Student[not(Subject)]
  " />

Хотя оно становится менее читаемым, если делать это таким образом.

0 голосов
/ 19 августа 2011

Это более простое решение, которое работает с любым количеством приоритетных предметов и использует только один шаблон фиксированного размера . Его можно легко использовать для решения гораздо более сложных задач, таких как: " Показывать толькопервые k предметы из списка приоритетных N предметов":

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

 <xsl:param name="vSubs" select="'x|Economics|Mathematics|'"/>

 <xsl:template match="Student[Subject]">
     <Student>
      <xsl:copy-of select="StudentName"/>

      <xsl:for-each select="Subject">
       <xsl:sort
         select="string-length(substring-before($vSubs,
                                                concat('|',.,'|')
                                                )
                             )"
       data-type="number" order="descending"/>
       <xsl:if test="position()=1">
        <xsl:copy-of select="."/>
       </xsl:if>
      </xsl:for-each>
     </Student>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

При применении к предоставленному документу XML :

<StudentSet>
    <Student>
        <StudentName>Kapil</StudentName>
        <Subject>English</Subject>
        <Subject>History</Subject>
        <Subject>Mathematics</Subject>
        <Subject>Economics</Subject>
    </Student>
    <Student>
        <StudentName>Atul</StudentName>
        <Subject>English</Subject>
        <Subject>History</Subject>
        <Subject>Economics</Subject>
    </Student>
    <Student>
        <StudentName>Nisha</StudentName>
        <Subject>English</Subject>
        <Subject>History</Subject>
    </Student>
    <Student>
        <StudentName>Manish</StudentName>
    </Student>
</StudentSet>

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

<Student>
   <StudentName>Kapil</StudentName>
   <Subject>Mathematics</Subject>
</Student>
<Student>
   <StudentName>Atul</StudentName>
   <Subject>Economics</Subject>
</Student>
<Student>
   <StudentName>Nisha</StudentName>
   <Subject>English</Subject>
</Student>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...