2016-03-06 207 views
3

我有一个遵循此DTD结构的XML文件。XQuery循环条件

<!DOCTYPE report [ 
<!ELEMENT report (title,section+)> 
<!ELEMENT section (title,body?,section*)> 
<!ELEMENT title (#PCDATA)> 
<!ELEMENT body (para+)> 
<!ELEMENT para(#PCDATA)> 
<!ATTLIST book version CDATA #REQUIRED> 
<!ATTLIST section number ID CDATA #REQUIRED> 
]> 

我想用XQuery查询以下两件事情。
1.获取至少出现两次的所有标题(两个标题相同的标题)。

for $x in /report/section/ 
for $y in /report/section/ 
where $x/@title = $y/@title 
return $x/@title 

2.获取正文或5个嵌套节中至少有10个段落的所有节的编号和标题。

for $x in /report/section/ 
where $x/para >= 10 or count(/section) > 10 
return <large>$x/number $x/title</large> 

但我的查询似乎并不正确。我是XQuery或XPath的初学者,有人能告诉我如何解决我的疑问吗?

编辑:示例XML

<?xml version="1.0" encoding="UTF-8"?> 
<report version = '1'> 
    <title>Harry Potter</title> 
    <section number = '1'> 
     <title>sec1</title> 
     <body> 
      <para>1</para> 
      <para>2</para> 
      <para>3</para> 
      <para>4</para> 
      <para>5</para> 
      <para>6</para> 
      <para>7</para> 
      <para>8</para> 
      <para>9</para> 
      <para>10</para> 
      <para>11</para>   
     </body>   
    </section> 

    <section number = '2'> 
     <title>sec2</title> 
     <body><para>test</para></body> 
     <section number = '2.1'> 
      <title>sec21</title> 
      <body> 
       <para>test</para> 
       <para>test</para> 
       <para>test</para> 
       <para>test</para> 
       <para>test</para> 
       <para>test</para> 
       <para>test</para> 
       <para>test</para> 
       <para>test</para> 
       <para>test</para> 
       <para>test</para> 
      </body> 
     </section> 
     <section number = '2.2'> 
      <title>sec21</title> 
      <body><para>test</para></body> 
     </section> 
     <section number = '2.3'> 
      <title>sec23</title> 
      <body><para>test</para></body> 
     </section> 
     <section number = '2.4'> 
      <title>sec24</title> 
      <body><para>test</para></body> 
     </section> 
     <section number = '2.5'> 
      <title>sec25</title> 
      <body><para>test</para></body> 
     </section> 
     <section number = '2.6'> 
      <title>sec1</title> 
      <body><para>test</para></body> 
     </section> 
    </section>  
</report> 
+0

邮政样本XML和相应的预期输出请 – har07

+2

对于示例XML,部分1和2.6具有相同的标题,和2.1和2.2节具有相同的标题。对于第二个问题,1和2.1在正文中有超过10个段落,并且2个具有超过5个嵌套段。 –

回答

3

在你的第一个例子中,有两个问题。首先,您没有获取嵌套部分,因为您只是迭代报表元素的直接子元素的部分元素。其次,你在同一个内容上使用两个循环。 $x$y都可能是相同的元素,所以where条件将为每个部分至少匹配一次。我会写这样的:

for $x in distinct-values(/report//section/title) 
    where count(/report//section[title=$x]) > 1 
    return $x 

循环得到所有独特的游戏,并遍历它们(注意,我们使用report//section让所有后代部分)。然后,对于其中的每一个,我们计算它使用了多少次,以保持不止一次出现的次数。然后我们返回循环变量(绑定到标题)。

运行它,我们回去

sec1 sec21 

在第二种情况下,我们还没有得到所有后代的同样的问题。我们也需要重视。我会用我所选择$x/body/para得到的部分(它们出现为主体元素的儿童)的段落

for $x in /report//section 
    where count($x/body/para) > 9 or count($x/section) > 4 
    return <large>{$x/@number} {string($x/title)}</large> 

通知。这个计数直接后代,但可以修改,以获得所有后代,如果有必要。还要注意在直接元素构造函数中使用大括号。当我们构造一个直接元素时,所有文本都是按字面读取的。大括号用于评估xquery表达式而不是文本文本。

我在标题中使用了字符串函数来提取元素的文本内容。如果我们不这样做,我们会得到一个实际的标题元素,而不是它的内容(这可能是一个期望的行为)。当我们提取数字属性时,它将是我们构造元素的属性(如果我们希望它是文本,我们可以将字符串函数应用于它)。

在这种情况下,它返回

<large number="1">sec1</large> 
<large number="2">sec2</large> 
<large number="2.1">sec21</large> 

的此处示例是使用使用撒克逊-HE 9.7.0.2J的OP的提供XML(的example.xml)进行测试。只有相关部件上面出现,但完整的第一个例子跑看起来像

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization"; 
declare option output:method "text"; 
declare context item := doc("example.xml"); 
for $x in distinct-values(/report//section/title) 
    where count(/report//section[title=$x]) > 1 
    return $x 

和完整的第二个例子看起来像

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization"; 
declare option output:method "xml"; 
declare context item := doc("example.xml"); 
for $x in /report//section 
    where count($x/body/para) > 9 or count($x/section) > 4 
    return <large>{$x/@number} {string($x/title)}</large> 
+0

现在我回头再比较我写的内容,这样做更有意义。非常感谢你! –

+0

Matthew,你使用在线的xquery工具来测试它吗? –

+0

@TT。不,我使用** Saxon-HE 9.7 **对OP提供的xml进行了测试。 – Matthew

2

对于XQuery中3.0第一个例子,我会用

declare context item := doc("example.xml"); 
for $x in /report//section/title/data() 
group by $x 
where count($x) > 1 
return $x[1]