Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reorder a node using XML Java DOM

Tags:

java

xml

Here is what I have,

<animation_state>
<state>run</state>
<animation_sequence>
<pose duration="10" image_id="1"/>
<pose duration="10" image_id="2"/>
<pose duration="10" image_id="3"/>
</animation_sequence>

I would like to give the user the ability to move a certain image up and down, however, since they are stored in XML, that implies I have to change the image ids around. If suppose the user wants the image_id = 3, to be the first in the sequence, or in the middle, or wherever depending on his needs, how can i manipulate the XML? I am using DOM.

If the user wants image 3, to be the first, this is how my XML should appear:

<animation_state>
<state>run</state>
<animation_sequence>
<pose duration="10" image_id="3"/>
<pose duration="10" image_id="1"/>
<pose duration="10" image_id="2"/>
</animation_sequence>

My attempt:

Document dom = parser.getDocument();
for (int i = 0; i < dom.getElementsByTagName("animation_state").getLength(); i++) 
{
    if (dom.getElementsByTagName("animation_state").item(i).getChildNodes().item(0).getTextContent().equalsIgnoreCase(target)) {
        posVal = i;
    }
}
NodeList list = dom.getElementsByTagName("animation_sequence").item(posVal).getChildNodes();

for(int b=0; b<list.getLength(); b++)
{
    if(list.item(b).getAttributes().item(1).getNodeValue().equalsIgnoreCase(PoseSelectionListener.imageIDOfSelectedPose))
    {
        Node toBeMoved = list.item(b);
        dom.getElementsByTagName("animation_sequence").item(posVal).appendChild(toBeMoved);
        System.out.println(toBeMoved.getAttributes().item(0).getNodeName());
    }
}
like image 278
user1888502 Avatar asked Dec 08 '12 21:12

user1888502


2 Answers

Use Node.insertBefore and/or Node.appendChild Just locate the Node to be moved and locate where it should be moved and insert that node before it.

It may be easier for you to create a copy of the node to be moved, insert it at the correct location and delete the old node afterwards.

See the sample code below:

public class SO13782330 {
    /** Move the image whose imageId is given at first position in sequence */
    public static void moveImageFirst(Document doc, int imageId) throws Exception {
        XPath xpath = XPathFactory.newInstance().newXPath();
        // get the image to move
        XPathExpression poseXPath = xpath.compile("//pose[@image_id='" + imageId + "']");
        Node pose = (Node)poseXPath.evaluate(doc, XPathConstants.NODE);
        // get the first image
        XPathExpression firstPoseXPath = xpath.compile("//pose[position() = 1]");
        Node firstPose = (Node)firstPoseXPath.evaluate(doc, XPathConstants.NODE);
        // copy the image to be moved
        Node poseCopy = pose.cloneNode(true);
        // insert it before the first one
        Node sequence = firstPose.getParentNode();
        sequence.insertBefore(poseCopy, firstPose);
        // delete the old one
        sequence.removeChild(pose);
    }

    /** Print the document on stdout */
    public static void showDocument(Document doc) throws Exception {
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        StringWriter sw = new StringWriter();
        transformer.transform(new DOMSource(doc), new StreamResult(sw));
        System.out.println(sw.getBuffer().toString());
    }

    public static void main(String... args) throws Exception {
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = db.parse(new InputSource(new StringReader("<animation_state>\n" +
                "<state>run</state>\n" +
                "<animation_sequence>\n" +
                "<pose duration=\"10\" image_id=\"1\"/>\n" +
                "<pose duration=\"10\" image_id=\"2\"/>\n" +
                "<pose duration=\"10\" image_id=\"3\"/>\n" +
                "</animation_sequence>\n" +
                "</animation_state>")));
        moveImageFirst(doc, 3);
        showDocument(doc);
    }
}

It will move the pose element having image_id attribute equals to 3 before the first one.

like image 132
Alex Avatar answered Sep 23 '22 04:09

Alex


You dont need to copy/clone the node.

Simply do following:

public void addNodeAfter(Node newNode, Node refChild) {
    Node parent = refChild.getParent();
    parent.insertBefore(newNode, refChild);
    refChild = parent.remove(refChild);
    parent.insertBefore(refChild, newNode); 
}

May be a better solution than cloning.

like image 39
Stiebl Avatar answered Sep 22 '22 04:09

Stiebl