5

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());
    }
}

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Isn't that what I am doing right now? I find it, and I append it. Please help me Alex
Thanks for the advice, the XML APIs have so many different calls that trial and error takes a long time. Copying the nodes turns out to be important.
5

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.

3 Comments

Welcome to Stack Overflow! Code-only answers are not very helpful. Please edit your answer to explain why your code solves the original problem.
Why are you removing refChild and assigning it to the refChild node?
You are right, the assignment to the refChild node is not necessary on removing

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.