2016-11-27 8 views
0

Мне нужна функция XQuery, которая может переписывать документ, в частности, возвращать подмножество дочерних узлов на основе массива строк, определяющих имена узлов для извлечения. Если должно удовлетворять определению, как показано ниже, и ему необходимо работать с любым произвольным документом.Переписать документ MarkLogic xml динамически на основе строк в XQuery

local:apply-node-includes($document, $includedNodeNames as xs:string*) 

Учитывая я имел некоторые XML Doc

let $doc := 
    <foo a="b"> 
    <bar>hello</bar> 
    <baz>1</baz> 
    <bang> 
     <text>world</text> 
    </bang> 
    </foo>') 

Тогда функция должна преобразовать документ таким образом, что только дочерние узлы с именем, указанным в $ includedNodes возвращаются.

Пример local:apply-node-includes($doc, 'baz') возвратит

<foo a="b"> 
    <baz>1</baz> 
</foo> 

Пример local:apply-node-includes($doc, ('bar','bang'))) возвратит

<foo a="b"> 
    <bar>hello</bar> 
    <bang> 
    <text>world</text> 
    </bang> 
</foo> 

Я экспериментировал итерацию по узлам, и/или с использованием той или иной форме рекурсивного typeswitch, но до сих пор не смогли получить это правильно. Было бы здорово, если бы он работал полностью рекурсивно, поэтому «bang.text» включал бы только внуки, а не братья и сестры, но, возможно, это слишком много!

+1

Рассмотрите возможность выполнения вашей функцией последовательности xs: QNames, а не строк. У вас есть работы, потому что ваш XML использует пространство имен по умолчанию, но при наличии пространств имен у вас возникнет проблема. –

ответ

1

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

declare function local:apply-node-includes($doc as item(), $includedNodeNames as xs:string*) as item() 
{ 
    (: Recreate the root element :) 
    element {name($doc)} 
    { (: Replicate root element's attributes :) 
    (for $attribute in $doc/@* return $attribute), 
    (: Replicate root element's immediate children having any of given names :) 
    (for $element in $doc/* where name($element) = $includedNodeNames 
     return $element) 
    } 
}; 

let $doc := 
    <foo a="b"> 
    <bar>hello</bar> 
    <baz>1</baz> 
    <bang> 
     <text>world</text> 
    </bang> 
    </foo> 
return local:apply-node-includes($doc, ('bar','bang')) 

Выход:

<foo a="b"><bar>hello</bar><bang><text>world</text></bang></foo> 
+0

Я исправил эти опечатки и благодарю за ответ. Я согласился с вами, но отправил утонченный отзыв, чтобы другие следовали. – alastairtree

1

@DavidDeneberg дал очень хороший ответ, но мне удалось упростить его дальше с помощью некоторых XPath так проводки для других.

declare function local:apply-node-includes($doc as element(), $includedNodeNames as xs:string*) as element()? 
{ 
    element {node-name($doc) } 
    { 
    $doc/@*, 
    $doc/*[name(.)=$include-names] 
    } 
}; 

Кроме того, эта книга очень полезна на тему https://en.wikibooks.org/wiki/XQuery/Filtering_Nodes и демонстрирует вид рекурсивных тождественного преобразование вы должны быть в состоянии справиться с внуками частью вопроса.