不同的属性名称

时间:2015-03-12 13:30:30

标签: xquery

使用XQuery我想从产品中的每篇文章中选择一个特殊值。

我现在有什么:

输入XML(摘录):

<product type="product" id="2246091">
<product type="article">
  <attribute identifier="EXAMPLE1" type="BOOLEAN">0</attribute>
  <attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
</product>
<product type="article">
  <attribute identifier="EXAMPLE1" type="BOOLEAN">1</attribute>
  <attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
</product>
<product type="article">
  <attribute identifier="EXAMPLE1" type="BOOLEAN">0</attribute>
  <attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
</product>
</product>

的XQuery:

 for $i in //product
                 [@type = 'product' 
                 and @id = '2246091']
                 //attribute
                 [@type='BOOLEAN' 
                 and @identifier= ('EXAMPLE1', 'EXAMPLE2') ]
 where $i = '1'
 return $i

这会返回attribute下每篇文章的每个product元素,其中内容为&#39; 1&#39;其标识符为EXAMPLE1或EXAMPLE2。 可能的是,在第1条中,与第2条中的attribute标识符相同(例如,EXAMPLE1)。

我得到了什么:

<?xml version="1.0" encoding="UTF-8"?>
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE1" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>

我尝试在我的for循环周围添加一个明确的值,但这只会返回给我&#39; 1&#39;。

我想要的只是获取每个属性一次:

<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE1" type="BOOLEAN">1</attribute>

1 个答案:

答案 0 :(得分:2)

听起来好像你想要的是在内容为1的attribute元素中找到identifier属性的每个不同值的一个attribute元素。(或者,更具挑战性的是,每组等效attribute元素有一个attribute元素,其中等价由deep-equals()定义。)

distinct-values()函数在这里没有帮助,因为它将任何输入节点强制转换为简单值(此处为1)。

如果identifier属性上的匹配就足够了

如果identifier属性足以在元素之间建立等价,那么类似下面的内容就足够了(未经测试):

let $ones := //product[@type = 'product' 
                       and @id = '2246091']
             //attribute[@type='BOOLEAN' 
                         and @identifier = 
                         ('EXAMPLE1', 'EXAMPLE2') ],
    $ids := distinct-values($ones/@identifier)
for $id in $ids
return ($ones[@identifier = $id])[1]

如果需要更一般的等效性测试

如果@identifier不足以为您的目的建立等同性,那么您将不得不做一些更复杂的事情;在一般情况下,一种方法是编写两个参数的函数(我称之为local:equivalent()),如果两个参数对于您的目的是等效的,则返回true。然后编写第二个函数来接受一系列项目并从序列中删除重复项(其中&#39;是重复的&#39;表示&#39;在local:equivalent()上返回true)。像这样的东西可能作为第一个近似值(未经测试):

(: dedup#1:  remove duplicates from a sequence :)
declare function local:dedup(
  $items as item()*
) as xs:boolean {
  local:dedup($items, ())
};

(: dedup#2: work through the input sequence one
   by one, removing duplicates and accumulating
   non-duplicates.  Cost is n^2 / 2. :)
declare function local:dedup(
  $in as item()*,
  $out as item()*
) as xs:boolean {
  if (empty($in)) 
  then $out
  else let $car := head($in)
       return if (some $i in $in
                  satisfies
                  local:equivalent($i, $car))
              then local:dedup(tail($in), $out)
              else local:dedup(tail($in), ($car, $out))
};

(: equivalent#2:  true iff arguments are equivalent :)
declare function local:equivalent(
  $x, $y : item()
) as xs:boolean {
  // determine application-specific equivalence
  // however you like ...
  deep-equal($x, $y)
};

(: Now do the work :)
let $ones := //product[@type = 'product' 
                       and @id = '2246091']
             //attribute[@type='BOOLEAN' 
                         and @identifier = 
                         ('EXAMPLE1', 'EXAMPLE2') ]
return local:dedup($ones)

那些对高阶函数感兴趣的人希望更进一步,通过允许两个local:equivalent函数接受提供等价函数的附加参数来消除对具有名为local:dedup的函数的依赖性。