user3676506
user3676506

Reputation: 102

Java find and remove child nodes

I have an XML file as below:

<CourseList>
<Course>
    <CourseName>CoreJava</CourseName>
    <Teacher>Bui Duy Linh</Teacher>
    <Duration>90 minutes</Duration>
    <Student>
        <StudentID>C001</StudentID>
        <StudentName>Nam</StudentName>
        <DateRegister>15/11/2016</DateRegister>
    </Student>
    <Student>
        <StudentID>C002</StudentID>
        <StudentName>Vi</StudentName>
        <DateRegister>13/11/2016</DateRegister>
    </Student>
</Course>

And I want to remove a student with ID C001, but my search function seem to return null

public static Node searchByID(String id, Document doc) {
    try {
        XPathFactory xpf = XPathFactory.newInstance();
        XPath xp = xpf.newXPath();
        NodeList list = (NodeList) xp.evaluate("CourseList/Course/Student/StudentID", doc, XPathConstants.NODESET);
        for (int i = 0; i < list.getLength(); i++) {
            String content = list.item(i).getTextContent();
            if (content.equalsIgnoreCase(id)) {
                Node p = list.item(i).getParentNode();
                return p;
            }
        }

    } catch (XPathExpressionException ex) {
        Logger.getLogger(AssignmentXML.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

Is there a way to fix it? Should I use DOM instead? Appreciate anyone help

Upvotes: 1

Views: 2694

Answers (3)

Parfait
Parfait

Reputation: 107587

For OP and future readers, consider using XSLT when needing to restructure or re-design an XML document. No looping or if/then logic is needed. XSLT is a native special-purpose language designed for this exact need. Like general purpose languages including C#, Python, Perl, PHP, and VB, Java maintains an XSLT 1.0 processor. Below is a working example for OP's request.

XSLT Script (save as .xsl or .xslt to be used in Java)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

  <!-- Identity Transform -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- Removes all Student nodes with C001 ID condition -->
  <xsl:template match="Student[StudentID='C001']"/>

</xsl:transform>

Java Script

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import javax.xml.transform.*;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.OutputKeys;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class CourseList {
    public static void main(String[] args) throws IOException, URISyntaxException,
                                                  SAXException, 
                                                  ParserConfigurationException,
                                                  TransformerException {

        // LOAD XML AND XSL DOCUMENTS
        String inputXML = "C:\\Path\\To\\Input.xml";
        String xslFile =  "C:\\Path\\To\\XSLTScript.xsl";
        String outputXML =  "C:\\Path\\To\\Output.xml";                                   

        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();            
        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
        Document doc = docBuilder.parse (new File(inputXML));
        Source xslt = new StreamSource(new File(xslFile)); 

        // XSLT TRANSFORMATION WITH PRETTY PRINT
        TransformerFactory prettyPrint = TransformerFactory.newInstance();
        Transformer transformer = prettyPrint.newTransformer(xslt);

        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        transformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");                        

        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(new File(outputXML));        
        transformer.transform(source, result);
    }
}

Output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CourseList>
    <Course>
        <CourseName>CoreJava</CourseName>
        <Teacher>Bui Duy Linh</Teacher>
        <Duration>90 minutes</Duration>
        <Student>
            <StudentID>C002</StudentID>
            <StudentName>Vi</StudentName>
            <DateRegister>13/11/2016</DateRegister>
        </Student>
    </Course>
</CourseList>

Upvotes: 1

user3676506
user3676506

Reputation: 102

Well, turn out nothing wrong with the search but with the remove function. My old code:

Node delNode = searchByID(id, doc);
doc.getDocumentElement().removeChild(delNode);

which only remove the Node in the document, not the Element of the node. I did this to fix:

Node delNode = searchByID(id, doc);
delNode.getParentNode().removeChild(delNode);

Thanks for you guys help anyway :)

Upvotes: 1

Andreas
Andreas

Reputation: 159086

You say it returns null, but it doesn't. It returns a Node object for the <Student> node with the given ID. If you print the node, you get:

[Student: null]

That is not a null value, but a Node object, with a toString() method like this:

public String toString() {
    return "["+getNodeName()+": "+getNodeValue()+"]";
}

And according to the Node javadoc for an Element node:

  • nodeName is "same as Element.tagName"
  • nodeValue is "null"

If you actually transform the node to text using:

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new DOMSource(node), new StreamResult(System.out));

You will get:

<Student>
        <StudentID>C001</StudentID>
        <StudentName>Nam</StudentName>
        <DateRegister>15/11/2016</DateRegister>
    </Student>

Upvotes: 0

Related Questions