Расширенный XPath-запрос - PullRequest
       22

Расширенный XPath-запрос

2 голосов
/ 13 января 2011

У меня есть XML-файл, который выглядит так:

<?xml version="1.0" encoding="utf-8" ?>
<PrivateSchool>

     <Teacher id="teacher1">
         <Name>
           teacher1Name
         </Name>
    </Teacher>

    <Teacher id="teacher2">
        <Name>
            teacher2Name
        </Name>
    </Teacher>

  <Student id="student1">
    <Name>
      student1Name
    </Name>
  </Student>

  <Student id="student2">
    <Name>
      student2Name
    </Name>
  </Student>

    <Lesson student="student1" teacher="teacher1"  />
    <Lesson student="student2" teacher="teacher2"  />
    <Lesson student="student3" teacher="teacher3"  />
    <Lesson student="student1" teacher="teacher2"  />
    <Lesson student="student3" teacher="teacher3"  />
    <Lesson student="student1" teacher="teacher1"  />
    <Lesson student="student2" teacher="teacher4"  />
    <Lesson student="student1" teacher="teacher1"  />

</PrivateSchool>

Есть также DTD, связанный с этим XML, но я предполагаю, что он не очень важен для моего вопроса. Давайте предположим, что все необходимые учителя и ученики четко определены.

Что такое запрос XPath, который возвращает Имена учителей, у которых хотя бы один ученик взял с собой более 10 уроков?

Я просматривал множество сайтов / примеров XPath. Ничто не казалось достаточно продвинутым для такого рода вопросов.

Ответы [ 4 ]

1 голос
/ 13 января 2011

Это решение XPath 2.0:

(/PrivateSchool
   /Lesson)
      [index-of(
          /PrivateSchool
            /Lesson
               /concat(@student, '|', @teacher),
          concat(@student, '|', @teacher)
       )[10]
      ]/(for $teacher in @teacher
         return /PrivateSchool
                   /Teacher[@id = $teacher]
                      /Name)
1 голос
/ 13 января 2011

Используйте это выражение XPath 2.0 :

for $limit in 2,
    $t in /*/Teacher,
    $id in $t/@id,
    $s in /*/Student/@id,
    $numLessons in
       count(/*/Lesson[@teacher eq $id
                     and @student eq $s])
 return
    if($numLessons gt $limit)
      then
        (string-join(($t/Name, $s, xs:string($numLessons)), ' '),
          '&#xA;'
         )
      else ()

здесь я установил $limit в 2, чтобы при вычислении этого выражения XPath по отношению к предоставленному документу XML:

<PrivateSchool>
    <Teacher id="teacher1">
        <Name>teacher1Name</Name>
    </Teacher>
    <Teacher id="teacher2">
        <Name>teacher2Name</Name>
    </Teacher>
    <Student id="student1">
        <Name>student1Name</Name>
    </Student>
    <Student id="student2">
        <Name>student2Name</Name>
    </Student>
    <Lesson student="student1" teacher="teacher1"  />
    <Lesson student="student2" teacher="teacher2"  />
    <Lesson student="student3" teacher="teacher3"  />
    <Lesson student="student1" teacher="teacher2"  />
    <Lesson student="student3" teacher="teacher3"  />
    <Lesson student="student1" teacher="teacher1"  />
    <Lesson student="student2" teacher="teacher4"  />
    <Lesson student="student1" teacher="teacher1"  />
</PrivateSchool>

это дает правильный результат :

teacher1Name student1 3 

В вашем реальном выражении у вас будет $limit установлен на10 и возвращает только имена учителей :

for $limit in 10,
    $t in /*/Teacher,
    $id in $t/@id,
    $s in /*/Student/@id,
    $numLessons in
        count(/*/Lesson[@teacher eq $id
                      and @student eq $s])
 return
    if($numLessons gt $limit)
      then ($t/Name, '&#xA;')
      else ()
1 голос
/ 13 января 2011

Возможно сложное соединение в одном XPath, но вы бьетесь головой о кирпичную стену. XQuery или XSLT гораздо больше подходят для такого рода вещей. Вот это в XQuery:

declare variable $doc as doc('data.xml');

declare function local:numLessons($teacher, $student) {
  return count($doc//Lesson[@teacher = $teacher and @student = $student])
};

$doc//Teacher[some $s in //Lesson/@student satisfies local:numLessons(@id, $s) gt 10]/Name

Сделав это, если вы действительно настроены, вы можете уменьшить его до XPath 2.0:

doc('data.xml')//Teacher[
   for $t in . return 
     some $s in //Lesson/@student satisfies 
       count(//Lesson[@teacher = $t and @student = $s]) gt 10] /Name

Не тестировалось.

0 голосов
/ 27 мая 2014

Решение, опубликованное Майклом Кейем для xpath 2.0, является правильным, но приблизительным. Точное решение для xml, размещенного в вопросе, будет (без абсолютных путей):

//Teacher[
           for $t in . return 
             some $s in //Student satisfies 
               count(//Lesson[@teacher = $t/@id and @student = $s/@id]) gt 1
         ]/Name

(я использовал "gt 1" вместо "gt 10", чтобы получить некоторый результат)

...