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'))

Это выдает мне "err: XPTY0004 ... не является подтипом element () ..." Я изменил код:

let $rows:= doc("employees.xml")//row
        return 
             local:my-remove-elements($rows, ('first_name', 'last_name'))

на этот раз все еще: "err: XPTY0004: фактическое количество элементов для параметра 1 не соответствует количеству элементов в сигнатуре функции ...". Вы знаете, как это исправить и заставить работать?


person Judah Flynn    schedule 10.03.2018    source источник


Ответы (2)


Вот одно из возможных решений:

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')
)
person Christian Grün    schedule 10.03.2018

Ошибка 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
      }
};
person Mads Hansen    schedule 10.03.2018