Search code examples
javaxmldom4j

Moving an element at the same level of XML using Dom4J


I have the following XML:

<Vehicle xmlns="http://www.cartest.co.uk">
  <Car>
    <EngineSize>2100</EngineSize>
    <Color>Green</Color>
    <NoOfDoors>5</NoOfDoors>
    <MaxSpeed>150</MaxSpeed>
    <Interior>Leather</Interior>
  </Car>
  <Car>
    <EngineSize>1000</EngineSize>
    <Color>Red</Color>
    <NoOfDoors>3</NoOfDoors>
    <MaxSpeed>120</MaxSpeed>
    <Interior>Leather</Interior>
  </Car>
  <Car>
    <EngineSize>1400</EngineSize>
    <Color>Blue</Color>
    <MaxSpeed>100</MaxSpeed>
    <Interior>Fabric</Interior>
  </Car>
</Vehicle>

What I want to do is parse the document and if the NoOfDoors element exists, move the MaxSpeed element above it.

So taking the above example XML, I would get the following output:

<Vehicle xmlns="http://www.cartest.co.uk">
  <Car>
    <EngineSize>2100</EngineSize>
    <Color>Green</Color>
    <MaxSpeed>150</MaxSpeed>
    <NoOfDoors>5</NoOfDoors>
    <Interior>Leather</Interior>
  </Car>
  <Car>
    <EngineSize>1000</EngineSize>
    <Color>Red</Color>
    <MaxSpeed>120</MaxSpeed>
    <NoOfDoors>3</NoOfDoors>
    <Interior>Leather</Interior>
  </Car>
  <Car>
    <EngineSize>1400</EngineSize>
    <Color>Blue</Color>
    <MaxSpeed>100</MaxSpeed>
    <Interior>Fabric</Interior>
  </Car>
</Vehicle>

I have created the following snippet of code (among lots of other attempts) and I just cant seem to get it to work:

// Parse the XML document
doc = ParseDocument(fileName);

// Deal with default namespace
HashMap map = new HashMap();
map.put( "ns", "http://www.cartest.co.uk");
Dom4jXPath xpath = new Dom4jXPath( "//ns:Car");
xpath.setNamespaceContext(new SimpleNamespaceContext(map));

List<Node> nodes = xpath.selectNodes(doc);

for(Node node : nodes)
{
    Element element = (Element)node;
    Iterator<Element> iterator = element.elementIterator();

    while(iterator.hasNext())
    {
      Element currentElement = (Element)iterator.next();

      if(currentElement.getName().equals("NoOfDoors"))
      {
         List<Element> elementList = currentElement.getParent().elements();      

         for(Element elements : elementList)
         {
           if(elements.getName().equals("MaxSpeed"))
           {
              Node moveNode = elements.detach(); 
              elementList.add(elementList.indexOf(elements), (Element) moveNode);
           } 
         }
      }
    }
}

The code when run gives the following error:

java.lang.IndexOutOfBoundsException: Index: -1

Anyone know a way how to do this using Dom4J? The problem I seem to have is when I detach the element, I just cant seem to add it back into the list that I have.


Solution

  • I managed to get this working by creating a new list and then iterating over this list using a new iterator as shown in the following code:

    // Parse the XML document
    doc = ParseDocument(fileName);
    
    // Deal with default namespace
    HashMap map = new HashMap();
    map.put( "ns", "http://www.cartest.co.uk");
    Dom4jXPath xpath = new Dom4jXPath( "//ns:Car");
    xpath.setNamespaceContext(new SimpleNamespaceContext(map));
    
    List<Node> nodes = xpath.selectNodes(doc);
    
    for(Node node : nodes)
    {
        Element element = (Element)node;
        Iterator<Element> iterator = element.elementIterator();
    
        while(iterator.hasNext())
        {
          Element currentElement = (Element)iterator.next();
    
          if(currentElement.getName().equals("NoOfDoors"))
          {
             List<Element> elementList = currentElement.getParent().elements();      
    
             Iterator<Element> iterator2 = element.elementIterator();
    
             while(iterator2.hasNext())
             {
    
                Element newCurrentElement = (Element)iterator2.next();
    
                if(newCurrentElement.getName().equals("MaxSpeed"))
                {
                     newCurrentElement.detach();
                     elementList.add(elementList.indexOf(currentElement), newCurrentElement);
                }
    
          }
        }
    }