Search code examples
javaxmldomxsitype

Merging Documents while preserving xsi:type


I have 2 Document objects with documents that contain similiar XML's. For example:

<tt:root xmlns:tt="http://myurl.com/">
  <tt:child/>
  <tt:child/>
</tt:root>

And the other one:

<ns1:root xmlns:ns1="http://myurl.com/" xmlns:ns2="http://myotherurl.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ns1:child/>
  <ns1:child xsi:type="ns2:SomeType"/>
</ns1:root>

I need to merge them to 1 document with 1 root element and 4 child elements. Problem is, if I use document.importNode function to do the merging, it properly handles the namespaces everywhere BUT xsi:type element. So what I'm getting in result is this:

<tt:root xmlns:tt="http://myurl.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <tt:child/>
  <tt:child/>
  <ns1:child xmlns:ns1="http://myurl.com/"/>
  <ns1:child xmlns:ns1="http://myurl.com/" xsi:type="ns2:SomeType"/>
</tt:root>

As you can see, ns2 is used in xsi:type but is not defined anywhere. Is there any automated way to solve this problem?

Thanks.

ADDED:

If this task is impossible to complete using the default java DOM libraries, maybe there is some other library I can use to complete my task?


Solution

  • A single-line of XQuery could do the job: construct a new node named as the context root element, then import its children together with those from the other document:

    declare variable $other external; element {node-name(*)} {*/*, $other/*/*}
    

    Though in XQuery you don't have full control over namespace nodes (at least in XQuery 1.0), it has a copy-namespaces mode setting that can be used to ask for keeping the namespace context intact, in case the implementation does preserve it by default.

    If XQuery is a viable option, then saxon9he.jar could be the "magic xml library" that you are after.

    Here is sample code exposing some context, using the s9api API:

    import javax.xml.parsers.DocumentBuilderFactory;
    import net.sf.saxon.s9api.*;
    import org.w3c.dom.Document;
    
    ...
    
      Document merge(Document context, Document other) throws Exception
      {
        Processor processor = new Processor(false);
        XQueryExecutable executable = processor.newXQueryCompiler().compile(
          "declare variable $other external; element {node-name(*)} {*/*, $other/*/*}");
        XQueryEvaluator evaluator = executable.load();    
        DocumentBuilder db = processor.newDocumentBuilder();
        evaluator.setContextItem(db.wrap(context));
        evaluator.setExternalVariable(new QName("other"), db.wrap(other));
        Document doc =
          DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        processor.writeXdmValue(evaluator.evaluate(), new DOMDestination(doc));
        return doc;
      }