13

Given the following XML file:

<?xml version="1.0" encoding="UTF-8"?>
<process
    name="TestSVG2"
    xmlns="http://www.example.org"
    targetNamespace="http://www.example.org"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <sequence>
      <receive name="Receive1" createInstance="yes"/>
      <assign name="Assign1"/>
      <invoke name="Invoke1"/>
      <assign name="Assign2"/>
      <reply name="Reply1"/>
   </sequence>
</process>

I want to add a new element inside the <sequence></sequence> after a certain pre-existing element. For example if I want to add the node after "Assign1", the new XML should like this:

   <sequence>
      <receive name="Receive1" createInstance="yes"/>
      <assign name="Assign1"/>
      <newtype name="NewNode"/>
      <invoke name="Invoke1"/>
      <assign name="Assign2"/>
      <reply name="Reply1"/>
   </sequence>

I have to do this by using Java DOM, in a function. The function signature should like this:

 public void addActionDom(String name, String stepType, String stepName)

Where:

  • name is the pre-existing element, after which the insertion will be made;
  • stepType is the inserted element type;
  • stepName is the name attribute of the newly inserted element.

Currently I am lacking experience with JDOM, or any other Java XML library. Can you please give a sample code, or point me to a tutorial where an insertion after a certain element is made.

This is the code I have until now:

    public void addActionDom(String name, String stepType, String stepName) {
        File xmlFile = new File(path + "/resources/" + BPELFilename);
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db;
        try {
            /* Load XML */
            db = dbf.newDocumentBuilder();
            Document doc = db.parse(xmlFile);
            doc.getDocumentElement().normalize();

            /* Iterate throughout the type tags and delete */
            for (String cTag : typeTags) {
                NodeList cnl = doc.getElementsByTagName(cTag);
                for (int i = 0; i < cnl.getLength(); ++i) {
                    Node cnode = cnl.item(i);
                    if (cnode.getNodeType() == Node.ELEMENT_NODE) {
                        Element elem = (Element)cnode; // 'elem' Element after which the insertion should be made
                        if (elem.getAttribute("name").equals(name)) { 
                            Element newElement = doc.createElement(stepType); // Element to be inserted 
                            newElement.setAttribute("name", stepName);
                            // CODE HERE
                        }
                    }
                }
            }

            /* Save the editing */
            Transformer transformer =
                TransformerFactory.newInstance().newTransformer();
            StreamResult result =
                new StreamResult(new FileOutputStream(path + "/resources/" +
                                                      BPELFilename));
            DOMSource source = new DOMSource(doc);
            transformer.transform(source, result);
        } catch (Exception e) {
            /* ParserConfigurationException */
            /* SAXException */
            /* IOException */
            /* TransformerConfigurationException */
            /* TransformerException */
            /* Exception */
            e.printStackTrace();
        }
    }
}
Andrei Ciobanu
  • 12,500
  • 24
  • 85
  • 118

4 Answers4

24

Ok, Aaron Digulla beat me regarding speed. Had to figure it out myself as well. I didnt use cnl.item(i+1) but nextSibling():

Element newElement = doc.createElement(stepType); // Element to be inserted 
newElement.setAttribute("name", stepName);
elem.getParentNode().insertBefore(newElement, elem.getNextSibling());

You cannot insert Nodes at a specified index. The only node-inserting methods are

appendChild(Node node) //appends the given child to the end of the list of children

and

insertBefore(Node new, Node child) //inserts "new" into the list, before the 'child' node.

If there was a insertAfter(Node new, Node child) method, this would be very easy for you. But there isn't, unfortunately.

MatEngel
  • 13
  • 4
f1sh
  • 11,489
  • 3
  • 25
  • 51
  • 1
    What happen if `elem.getNextSibling()==null`? – Stephan Nov 17 '16 at 18:04
  • 2
    @Stephan from [the docs of insertBefore](https://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Node.html#insertBefore(org.w3c.dom.Node,%20org.w3c.dom.Node)): ``If [the second parameter] is null, insert newChild at the end of the list of children.`` – f1sh Nov 18 '16 at 09:38
4

It's simple but the org.w3c.dom API is a bit ... odd for this:

Node next = cnl.item(i + 1);
Node newChild = createChild();
next.getParent().insertBefore(newChild, next);

With JDom, it's more simple:

Node newChild = createChild();
cnl.getParent().addContent(i, newChild);
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • Thanks for the answer @Aaron, But what if I want to insert imediately after "Reply1" . There's no next for Reply1 (or I may understand it wrong). – Andrei Ciobanu Aug 04 '10 at 11:45
  • @Andrei: In that case, `cnl.item(i+1)` returns `null`. That might sound dangerous, but the javadoc for `insertBefore(Node newChild, Node refChild)` says: "If refChild is null, insert newChild at the end of the list of children", which results in exactly what you want. – f1sh Aug 04 '10 at 11:51
2

As others have pointed out, the DOM API is quite verbose for such simple operations. If you use something like jOOX to wrap the DOM API, you could write any of the following:

// Find the element using XPath, and insert XML text after it
$(document).xpath("//sequence/assign[@name='Assign1']")
           .after("<newtype name=\"NewNode\"/>");

// Find the element using jOOX API, and insert an object after it
$(document).find("sequence")
           .find("assign")
           .filter(attr("name", "Assign1"))
           .after($("newtype").attr("name", "NewNode"));

Note how the API resembles that of jQuery.

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
2

This is not tested but you should be able to do:

elem.getParentNode().insertBefore(newElement, elem.getNextSibling());
Garett
  • 16,632
  • 5
  • 55
  • 63