xQuery удалить невыбранные элементы
У меня есть XML-документ с именем employee.xml:
<employees>
<row>
<emp_no>10001</emp_no>
<first_name>Georgi</first_name>
<last_name>Facello</last_name>
</row>
<row>
<emp_no>10002</emp_no>
<first_name>Bezalel</first_name>
<last_name>Simmel</last_name>
</row>
</employees>
Я хочу написать функцию с именем my-remove-elements
это удалит невыбранные атрибуты. Например, только держать first_name
а также last_name
в документе XML:
<employees>
<row>
<first_name>Georgi</first_name>
<last_name>Facello</last_name>
</row>
<row>
<first_name>Bezalel</first_name>
<last_name>Simmel</last_name>
</row>
</employees>
Определение моей функции:
declare function local:my-remove-elements($input as element(), $remove-names as xs:string*) as element() {
element {node-name($input) }
{$input/@*,
for $child in $input/node()[name(.)=$remove-names]
return
if ($child instance of element())
then local:my-remove-elements($child, $remove-names)
else $child
}
};
Вот как я это называю:
let $doc := doc("employees.xml")
return
local:my-remove-elements($doc, ('first_name', 'last_name'))
Это выдает мне "ошибка:XPTY0004 ... не является подтипом element()..." Я изменил код:
let $rows:= doc("employees.xml")//row
return
local:my-remove-elements($rows, ('first_name', 'last_name'))
это время все еще равно: "err:XPTY0004: фактическое количество элементов для параметра 1 не соответствует количеству элементов в сигнатуре функции...". Вы знаете, как это исправить и заставить это работать?
2 ответа
Ошибка err:XPTY0004
потому что вы вызываете функцию, передавая document-node()
в качестве первого параметра, но первый параметр должен быть element()
,
Изменить способ его вызова, передав элемент документа (т.е. $doc/*
) вместо document-node()
Затем вам нужно настроить логику в вашей функции. Если бы вы только выбрать $child
узлы, которые равны $remove-names
(что сбивает с толку, рассмотрите возможность переименования во что-то вроде $keep-leaf-names), затем employees
элементы будут исключены. Переместите этот фильтр вниз и добавьте логику для обработки, если у элемента есть дочерние элементы или он name()
равно любому из $remove-names
,
declare function local:my-remove-elements($input as element(), $remove-names as xs:string*) as element() {
element {node-name($input) }
{$input/@*,
for $child in $input/node()
return
if ($child instance of element()) then
if ($child/* or name($child)=$remove-names) then
local:my-remove-elements($child, $remove-names)
else ()
else $child
}
};
Вот одно из возможных решений:
declare function local:remove-elements(
$node as node(),
$keep-names as xs:string*
) as node()? {
if($node instance of element()) then (
let $name := name($node)
where $name = $keep-names
return element { $name } {
$node/@*,
for $child in $node/node()
return local:remove-elements($child, $keep-names)
}
) else (
$node
)
};
local:remove-elements(
doc("employees.xml")/employees,
('employees', 'row', 'first_name', 'last_name')
)
Вот еще один, который использует XQuery Update:
declare function local:remove-elements(
$node as node(),
$keep-names as xs:string*
) as node()? {
copy $c := $node
modify delete node $c//*[not(name() = $keep-names)]
return $c
};
local:remove-elements(
doc("employees.xml")/employees,
('employees', 'row', 'first_name', 'last_name')
)