1
votes

I have an XML document in MarkLogic with namespace 1.Now I would like to copy a node from it and paste it in another document that lives in namespace 2.

I have written a function to get this piece of data (the node) out of a document. The function returns a node inside document 2 that lives in namespace 1.

I want this node to also be in the same namespace as the root document.

Can I strip a node from its namespace? And then insert it in another doc? Any other procedure to get the desired result?

Here my result, notice the root namespace is different from the category elements namespace...

<wijk id="1027K01" xmlns="http://www.nvsp.nl/wijk">
  <meta-data>
    <!--Generated by DIKW for NetwerkVSP STT!P -->
    <version>0.4</version>
    <dateCreated>2015-02-22+01:00</dateCreated>
  </meta-data>
  <category name="Geografie" xmlns="http://www.cendris.nl/postcode">
    <variable name="Postcode">
      <segment name="6 positie postcode">1027AA</segment>
      <segment name="5 positie postcode">1027A</segment>
      <segment name="4 positie postcode">1027</segment>
    </variable>
    <variable name="Gemeente">
      <segment name="Gemeentecode">363</segment>
      <segment name="Gemeentenaam">Amsterdam</segment>
    </variable>
    <variable name="Plaats">
      <segment name="Plaatsnaam">AMSTERDAM</segment>
    </variable>
    <variable name="Provincie">
      <segment name="Provinciecode">27</segment>
      <segment name="Provincienaam">Noord-Holland</segment>
    </variable>
    <variable name="Cebuco">
      <segment name="Cebuco naam">(21) Amsterdam-Purmerend</segment>
      <segment name="Cebuco code">21</segment>
    </variable>
    <variable name="Bible Belt">
      <segment name="nauwelijks">100</segment>
      <segment name="enigszins">0</segment>
      <segment name="redelijk sterk">0</segment>
      <segment name="sterk">0</segment>
      <segment name="zeer sterk">0</segment>
    </variable>
    <variable name="Urbanisatiegraad">
      <segment name="Zeer sterk stedelijk">100</segment>
      <segment name="Sterk stedelijk">0</segment>
      <segment name="Matig stedelijk">0</segment>
      <segment name="Weinig stedelijk">0</segment>
      <segment name="Niet stedelijk">0</segment>
    </variable>
  </category>
</wijk>
3

3 Answers

3
votes

There's an easy way: use the functx:change-element-ns-deep function. The functx libraries are distributed with MarkLogic. Import them, call that function with "" as the new namespace and off you go.

0
votes

You can strip a namespace from a node by recursively re-constructing the node and it's children using only local names. You can use a similar approach to move a node from one namespace to another.

Note: stripping a namespace from a node puts that node into the "empty" namespace. That node will stay in the "empty" namespace even if it is placed as the child of a different namespace'd node.

Here's some example namespace manipulation in XQuery:

xquery version "1.0-ml";

declare namespace html = "http://www.w3.org/1999/xhtml";
declare namespace other = "other";

declare function local:remove-namespace($x as node()) as node()?
{
  if ($x instance of element())
  then
    element { fn:local-name($x) }  {
      $x/(@*,node()) ! local:remove-namespace(.)
    }
  else $x
};

declare function local:set-namespace($x as node(), $prefix as xs:string) as node()?
{
  if ($x instance of element())
  then
    element { xs:QName($prefix || ":" || fn:local-name($x)) }  {
      $x/(@*,node()) ! local:set-namespace(., $prefix)
    }
  else $x
};

let $x := <html:p>blah, blah <html:strong>__</html:strong> blah</html:p>
return (
  $x,
  local:remove-namespace($x),
  local:set-namespace($x, "other")
)

Evaluating that sample results in the following sequence:

<html:p xmlns:html="http://www.w3.org/1999/xhtml">blah, blah <html:strong>__</html:strong> blah</html:p>,
<p>blah, blah <strong>__</strong> blah</p>,
<other:p xmlns:other="other">blah, blah <other:strong>__</other:strong> blah</other:p>
0
votes

Namespace nodes are not really first-class citizens in the XQuery data model. The XQuery working group apparently didn't think we should be allowed to manipulate them. But there are some tricks available.

Here's one example, tested with 8.0-1.1:

declare function local:do(
  $n as node(), $ns as xs:string)
as node()
{
  typeswitch($n)
  case document-node() return document { local:do($n/node(), $ns) }
  case element() return element { QName($ns, local-name($n)) } {
    $ns,
    $n/@*,
    local:do($n/node(), $ns) }
  default return $n
};

<test xmlns="a">
  <fubar><baz/></fubar>
</test>
! local:do(., "b")

Once your XML element(s) have the desired namespaces, you can manipulate them as usual: xdmp:node-insert-child for example, or one of the libraries for in-memory update: https://github.com/ryanjdew/XQuery-XML-Memory-Operations etc.

Another useful namespace trick involves the XPath namespace axis. This can also be helpful if you have noisy namespace declarations.

<test xmlns="a">
  <b:fubar xmlns:b="b"><baz/></b:fubar>
  <b:fubar xmlns:b="b"><buz/></b:fubar>
</test>
! element { node-name(.) } {
  node()/namespace::*,
  @*,
  node() }

This has the effect of bringing the xmlns:b declaration up to the level of the root element.

Finally you can sometimes do tricks with xdmp:quote and xdmp:unquote, which has a default namespace option.