404or505
404or505

Reputation: 165

Is there anyway Nodes can be re-ordered or removed relative to an element node using Java?

In the following I would like to order the <table>..</table> node according to the input (inputY, inputX in this case) in id attribute or remove <table>..</table> if only one input is sent. How can I achieve it using DOM parser.

<employees>
    <table>
        <employee>
            <id attr="inputY">
                <firstName>Lokesh</firstName>
                <lastName>Gupta</lastName>
                <department>
                    <id>101</id>
                    <name>IT</name>
                </department>
            </id>
        </employee>
    </table>
    <table>
        <employee>
            <id attr="inputX">
                <firstName>Brian</firstName>
                <lastName>Schultz</lastName>
                <department>
                    <id>102</id>
                    <name>HR</name>
                </department>
            </id>
        </employee>
    </table>
<employees>

If input is passed in the order of inputX and inputY then the XML would be like the following :

<employees>
    <table>
        <employee>
            <id attr="inputX">
                <firstName>Brian</firstName>
                <lastName>Schultz</lastName>
                <department>
                    <id>102</id>
                    <name>HR</name>
                </department>
            </id>
        </employee>
    </table>
    <table>
        <employee>
            <id attr="inputY">
                <firstName>Lokesh</firstName>
                <lastName>Gupta</lastName>
                <department>
                    <id>101</id>
                    <name>IT</name>
                </department>
            </id>
        </employee>
    </table>
<employees>

This is what I have done so far :

public static void main(String... args) throws Exception {
    DocumentBuilder db = DocumentBuilderFactory.newInstance().
            newDocumentBuilder();
    Document doc = db.parse("src/main/resources/some1.xml");
    doc.getDocumentElement().normalize();
    ArrayList<String> ids = new ArrayList<String>();
    ids.add("inputY");
    ids.add("inputX");
    Element root = doc.getDocumentElement();
    Node employees = root.getElementsByTagName("employees").item(0);
    NodeList moveList = doc.getElementsByTagName("table");
    for (int k = 0; k < moveList.getLength(); k++) {
        System.out.println(moveList.item(k));
        Node move = moveList.item(k);
        Element eMove = (Element) move;
        NodeList idList = eMove.getElementsByTagName("id");
        for (int i = 0; i < idList.getLength(); i++) {
            if (i < ids.size()) {
                boolean result = ids.contains(
                        idList.item(0).getAttributes().item(0).
                                getNodeValue());
                if (result) {
                    //System.out.println("parent node : " + move.getParentNode().getFirstChild());
                    Node currentFirstNode = employees.getFirstChild();
                    Node copyNode = move.cloneNode(true);
                    Node placeholder = currentFirstNode.getParentNode();
                    placeholder.insertBefore(copyNode,currentFirstNode);
                    placeholder.removeChild(move);
                }
            }
        }
    }
}

Update 2:

Here is my new code: Still it fails to order the nodes properly. node with attribute inputX comes before inputZ, even though I have inputZ before input X in the list. Any suggestion?

DocumentBuilder db = DocumentBuilderFactory.newInstance().
                newDocumentBuilder();
        Document doc = db.parse("src/main/resources/some1.xml");
        doc.getDocumentElement().normalize();

        ArrayList<String> ids = new ArrayList<String>();
        ids.add("inputZ");
        ids.add("inputX");
        Element root = doc.getDocumentElement();
        Node employees = root.getElementsByTagName("employees").item(0);
        NodeList moveList = doc.getElementsByTagName("table");
        for (int k = 0; k < moveList.getLength(); k++) {
            System.out.println("# of table nodes : " + moveList.getLength());
            Node move = moveList.item(k);
            Element eMove = (Element) move;
            NodeList idList = eMove.getElementsByTagName("id");
                System.out.println("id attribute : " + idList.item(0).getAttributes().item(0));
                    boolean result = ids.contains(
                            idList.item(0).getAttributes().item(0).
                                    getNodeValue());
                    if (result) {
                        System.out.println(result);
                        Node currentFirstNode = employees.getFirstChild();
                        Node placeholder = currentFirstNode.getParentNode();
                        placeholder.insertBefore(move, currentFirstNode);
                    } else {
                        System.out.println(result);
                        System.out.println("Employees child node : " + employees.getChildNodes());
                        employees.removeChild(move);
                    }
        }

XML :

<root>
    <employees>
        <table>
            <employee>
                <id attr="inputZ">
                    <firstName>Ben</firstName>
                    <lastName>Smith</lastName>
                    <department>
                        <id>103</id>
                        <name>Business</name>
                    </department>
                </id>
            </employee>
        </table>
        <table>
            <employee>
                <id attr="inputX">
                    <firstName>Brian</firstName>
                    <lastName>Schultz</lastName>
                    <department>
                        <id>102</id>
                        <name>HR</name>
                    </department>
                </id>
            </employee>
        </table>
        <table>
            <employee>
                <id attr="inputY">
                    <firstName>Lokesh</firstName>
                    <lastName>Gupta</lastName>
                    <department>
                        <id>101</id>
                        <name>IT</name>
                    </department>
                </id>
            </employee>
        </table>
    </employees>
</root>

Upvotes: 1

Views: 119

Answers (2)

Michael Kay
Michael Kay

Reputation: 163342

You say "using Java", but by far the easiest way to do this sort of thing is using XSLT, and of course that can easily be invoked from Java.

This one can be done easily enough even in XSLT 1.0:

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <employees>
    <xsl:for-each select="employees/table">
      <xsl:sort select="employee/id/@attr">
      <xsl:copy-of select="."/>
    </xsl:for-each>
  </employees>
</xsl:template>

</xsl:transform>

I'm afraid I don't understand what you mean by this part of the requirement: "or remove .. if only one input is sent".

Upvotes: 0

Andreas
Andreas

Reputation: 159114

You can do it like this:

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

public class Test {
    public static void main(String[] args) throws Exception {
        // Load XML file into DOM tree and verify root element name
        Element root = DocumentBuilderFactory.newInstance()
                .newDocumentBuilder()
                .parse("test.xml")
                .getDocumentElement();
        if (! root.getTagName().equals("employees"))
            throw new IllegalArgumentException("Invalid root element name: " + root.getTagName());

        // Locate all <table> elements, identify "inputX" and "inputY" tables, and their relative order
        boolean inputYBeforeInputX = false;
        Node inputX = null, inputY = null;
        List<Node> toBeRemoved = new ArrayList<>();
        for (Node child = root.getFirstChild(); child != null; child = child.getNextSibling()) {
            if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("table")) {
                String id = getFirstChildByTagName(child, "employee")
                        .flatMap(e -> getFirstChildByTagName(e, "id"))
                        .map(e -> e.getAttribute("attr"))
                        .orElse(null);
                if (inputX == null && "inputX".equals(id)) {
                    inputX = child;
                } else if (inputY == null && "inputY".equals(id)) {
                    inputY = child;
                    inputYBeforeInputX = (inputX == null);
                } else {
                    toBeRemoved.add(child);
                }
            }
        }

        // If only one of "inputX" and "inputY" was found, mark it to be removed
        if (inputX != null && inputY == null)
            toBeRemoved.add(inputX);
        else if (inputY != null && inputX == null)
            toBeRemoved.add(inputY);

        // Remove superfluous <table> elements
        for (Node nodeToRemove : toBeRemoved)
            root.removeChild(nodeToRemove);

        // Swap "inputX" and "inputY" if "inputY" was before "inputX"
        if (inputYBeforeInputX && inputX != null && inputY != null) {
            if (inputY.getNextSibling() == inputX) {
                root.insertBefore(inputX, inputY);
            } else {
                Node inputXnext = inputX.getNextSibling();
                root.insertBefore(inputX, inputY.getNextSibling());
                root.insertBefore(inputY, inputXnext);
            }
        }

        // Print result XML
        TransformerFactory.newInstance()
                .newTransformer()
                .transform(new DOMSource(root), new StreamResult(System.out));
    }
    private static Optional<Element> getFirstChildByTagName(Node parent, String tagName) {
        for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling())
            if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(tagName))
                return Optional.of((Element) child);
        return Optional.empty();
    }
}

Upvotes: 1

Related Questions