I have an xml that should be split into smaller chunks by unique BookId node. Basically I need to filter out each book into separate xml having the same structure of the initial XML.
The purpose of that is - requirement to validate each smaller XML against XSD to determine which Book/PendingBook is not valid.
Note that Books node can contain both Book and PendingBook nodes.
Initial XML:
<Main xmlns="http://some/url/name">
<Books>
<Book>
<IdentifyingInformation>
<ID>
<Year>2021</Year>
<BookId>001</BookId>
<BookDateTime>2021-05-10T12:35:00</BookDateTime>
</ID>
</IdentifyingInformation>
</Book>
<Book>
<IdentifyingInformation>
<ID>
<Year>2020</Year>
<BookId>002</BookId>
<BookDateTime>2021-05-10T12:35:00</BookDateTime>
</ID>
</IdentifyingInformation>
</Book>
<PendingBook>
<IdentifyingInformation>
<ID>
<Year>2020</Year>
<BookId>003</BookId>
<BookDateTime>2021-05-10T12:35:00</BookDateTime>
</ID>
</IdentifyingInformation>
</PendingBook>
<OtherInfo>...</OtherInfo>
</Books>
</Main>
The result should be like next xmls:
Book_001.xml (BookId = 001):
<Main xmlns="http://some/url/name">
<Books>
<Book>
<IdentifyingInformation>
<ID>
<Year>2021</Year>
<BookId>001</BookId>
<BookDateTime>2021-05-10T12:35:00</BookDateTime>
</ID>
</IdentifyingInformation>
</Book>
<OtherInfo>...</OtherInfo>
</Books>
</Main>
Book_002.xml (BookId = 002):
<Main xmlns="http://some/url/name">
<Books>
<Book>
<IdentifyingInformation>
<ID>
<Year>2020</Year>
<BookId>002</BookId>
<BookDateTime>2021-05-10T12:35:00</BookDateTime>
</ID>
</IdentifyingInformation>
</Book>
<OtherInfo>...</OtherInfo>
</Books>
</Main>
PendingBook_003.xml (BookId = 003):
<Main xmlns="http://some/url/name">
<Books>
<PendingBook>
<IdentifyingInformation>
<ID>
<Year>2021</Year>
<BookId>003</BookId>
<BookDateTime>2021-05-10T12:35:00</BookDateTime>
</ID>
</IdentifyingInformation>
</PendingBook>
<OtherInfo>...</OtherInfo>
</Books>
</Main>
So far I fetched only each ID node into smaller xmls. And created root element manually.
Ideally I want to copy all elements from initial xml and put into Books node single Book/PendingBook node.
My java sample:
package com.main;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class ExtractXmls {
/**
* #param args
*/
public static void main(String[] args) throws Exception
{
String inputFile = "C:/pathToXML/Main.xml";
File xmlFile = new File(inputFile);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true); // never forget this!
XPathFactory xfactory = XPathFactory.newInstance();
XPath xpath = xfactory.newXPath();
XPathExpression allBookIdsExpression = xpath.compile("//Books/*/IdentifyingInformation/ID/BookId/text()");
NodeList bookIdNodes = (NodeList) allBookIdsExpression.evaluate(doc, XPathConstants.NODESET);
//Save all the products
List<String> bookIds = new ArrayList<>();
for (int i = 0; i < bookIdNodes.getLength(); ++i) {
Node bookId = bookIdNodes.item(i);
System.out.println(bookId.getTextContent());
bookIds.add(bookId.getTextContent());
}
//Now we create and save split XMLs
for (String bookId : bookIds)
{
//With such query I can find node based on bookId
String xpathQuery = "//ID[BookId='" + bookId + "']";
xpath = xfactory.newXPath();
XPathExpression query = xpath.compile(xpathQuery);
NodeList bookIdNodesFiltered = (NodeList) query.evaluate(doc, XPathConstants.NODESET);
System.out.println("Found " + bookIdNodesFiltered.getLength() + " bookId(s) for bookId " + bookId);
//We store the new XML file in bookId.xml e.g. 001.xml
Document aamcIdXml = dBuilder.newDocument();
Element root = aamcIdXml.createElement("Main"); //Here I'm recreating root element (don't know if I can avoid it and copy somehow structure of initial xml)
aamcIdXml.appendChild(root);
for (int i = 0; i < bookIdNodesFiltered.getLength(); i++) {
Node node = bookIdNodesFiltered.item(i);
Node copyNode = aamcIdXml.importNode(node, true);
root.appendChild(copyNode);
}
//At the end, we save the file XML on disk
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(aamcIdXml);
StreamResult result = new StreamResult(new File("C:/pathToXML/" + bookId.trim() + ".xml"));
transformer.transform(source, result);
System.out.println("Done for " + bookId);
}
}
}
Consider XSLT, the special purpose language designed to transform XML files including extracting needed nodes. Additionally, you can pass parameters from application layer like Java into XSLT (just like SQL)!
Specifically, iteratively passed in the XPath retrieved BookIds by Java into XSLT named param. By the way, no extensive code re-factoring is needed since you already have the transformer set up to run XSLT!
XSLT (save as .xsl, a special .xml)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<!-- INITIALIZE PARAMETER -->
<xsl:param name="param_bookId"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Books">
<xsl:copy>
<xsl:apply-templates select="Book[descendant::BookId = $param_bookId] |
PendingBook[descendant::BookId = $param_bookId]"/>
<xsl:apply-templates select="OtherInfo"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Online Demo
Java (no rebuild of trees)
// ... same code as reading XML input ...
// ... same code as creating bookIdNodes ...
String curr_bookId = null;
String outputXML = null;
String xslFile = "C:/Path/To/XSL/Style.xsl";
Source xslt = new StreamSource(new File(xslFile));
// ITERATE THROUGH EACH BOOK ID
for (int i = 0; i < bookIdNodes.getLength(); ++i) {
Node bookId = bookIdNodes.item(i);
System.out.println(bookId.getTextContent());
curr_bookId = bookId.getTextContent();
// CONFIGURE TRANSFORMER
TransformerFactory prettyPrint = TransformerFactory.newInstance();
Transformer transformer = prettyPrint.newTransformer(xslt);
transformer.setParameter("param_bookId", curr_bookId); // PASS PARAM
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
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");
// TRANSFORM AND OUTPUT FILE TO DISK
outputXML = "C:/Path/To/XML/BookId_" + curr_bookId + ".xml";
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(outputXML));
transformer.transform(source, result);
}
You almost got it to work. You could change your XPath in your loop iterating the book IDs to get the Book or PendingBook Element and then use it. Also you need to create Books element in addition to Main and append Book or PendingBook to the newly created Books Element.
The XPath is : //ancestor::*[IdentifyingInformation/ID/BookId=bookId]
It gets the ancestor of the element whose bookId matches to that of the ID in the current iteration i.e. the Book or PendingBook element.
//Now we create and save split XMLs
for (String bookId : bookIds)
{
//With such query I can find node based on bookId
String xpathQuery = "//ancestor::*[IdentifyingInformation/ID/BookId=" + bookId + "]";
xpath = xfactory.newXPath();
XPathExpression query = xpath.compile(xpathQuery);
NodeList bookIdNodesFiltered = (NodeList) query.evaluate(doc, XPathConstants.NODESET);
System.out.println("Found " + bookIdNodesFiltered.getLength() + " bookId(s) for bookId " + bookId);
//We store the new XML file in bookId.xml e.g. 001.xml
Document aamcIdXml = dBuilder.newDocument();
Element root = aamcIdXml.createElement("Main");
Element booksNode = aamcIdXml.createElement("Books");
root.appendChild(booksNode);
//Here I'm recreating root element (don't know if I can avoid it and copy somehow structure of initial xml)
aamcIdXml.appendChild(root);
String bookName = "";
for (int i = 0; i < bookIdNodesFiltered.getLength(); i++) {
Node node = bookIdNodesFiltered.item(i);
Node copyNode = aamcIdXml.importNode(node, true);
bookName = copyNode.getNodeName();
booksNode.appendChild(copyNode);
}
//At the end, we save the file XML on disk
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(aamcIdXml);
StreamResult result = new StreamResult(new File(bookName + "_" + bookId.trim() + ".xml"));
transformer.transform(source, result);
System.out.println("Done for " + bookId);
}
And also I modified code to name the file as you needed like Book_001.xml.
Related
I am trying to get a specific value from an xml. When I iterate over the nodes, the value is never returned. Here is the xml sample
<Fields>
<Field FieldName="NUMBER">
<String>1234</String>
</Field>
<Field FieldName="TYPE">
<String>JAVA</String>
</Field>
<Field FieldName="ATYPE">
<String>BB</String>
</Field>
</Fields>
Here is what I have attempted based on this online resource that looks like my sample xml file
private static void updateElementValue(Document doc) {
NodeList employees = doc.getElementsByTagName("Field");
Element emp = null;
//loop for each
for(int i=0; i<employees.getLength();i++){
emp = (Element) employees.item(i);
System.out.println("here is the emp " + emp);
Node name = emp.getElementsByTagName("NUMBER").item(0).getFirstChild();
name.setNodeValue(name.getNodeValue().toUpperCase());
}
}
This is the online resource guiding my attempts
https://www.journaldev.com/901/modify-xml-file-in-java-dom-parser
Please assist
If you want to get a specific value from XML, XPath API may be more convenient in compare to DOM parser API. Here an example for retrieving value of a "String" elements, which are children of "Field" elements, having attribute "FieldName" with value "NUMBER":
// parse XML document from file
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(new FileInputStream(fileName));
// prepare an XPath expression
XPathExpression xpath = XPathFactory.newInstance().newXPath().compile("/Fields/Field[#FieldName='NUMBER']/String");
// retrieve from XML nodes using XPath
NodeList list = (NodeList)xpath.evaluate(doc, XPathConstants.NODESET);
// iterate over resulting nodes and retrieve their values
for(int i = 0; i < list.getLength(); i ++) {
Node node = list.item(i);
// udate node content
node.setTextContent("New text");
}
// output edited XML document
StringWriter writer = new StringWriter(); // Use FileWriter to output to the file
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(writer));
System.out.println(writer.toString());
Hi I have requirement like parsing xml and get all child nodes and there data between nodes <Employees> and </Employees>
I have xml like:
<?xml version="1.0"?>
<Employees>
<Employee emplid="1111" type="admin">
<firstname>test1</firstname>
<lastname>Watson</lastname>
<age>30</age>
<email>johnwatson#sh.com</email>
</Employee>
<Employee emplid="2222" type="admin">
<firstname>Sherlock</firstname>
<lastname>Homes</lastname>
<age>32</age>
<email>sherlock#sh.com</email>
</Employee>
</Employees>
I need response like
<Employee emplid="1111" type="admin">
<firstname>test1</firstname>
<lastname>Watson</lastname>
<age>30</age>
<email>johnwatson#sh.com</email>
</Employee>
<Employee emplid="2222" type="admin">
<firstname>Sherlock</firstname>
<lastname>Homes</lastname>
<age>32</age>
<email>sherlock#sh.com</email>
</Employee>
I have tried below code
FileInputStream file = new FileInputStream(new File("E:\\test.xml"));
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(file);
XPath xPath = XPathFactory.newInstance().newXPath();
System.out.println("*************************");
String expression = "/Employees/*";
System.out.println(expression);
String email = xPath.compile(expression).evaluate(xmlDocument);
System.out.println(email);
but I am getting response like
test1
Watson
30
johnwatson#sh.com
I have used expression like /Employees/* but its not working
can anyone help me in doing this?
First, if you want to match each Employee, ideally your XPath expression should be Employee not /Employees/*. If you know the tag name, you also don't need XPath, you can just do xmlDocument.getElementsByTagName("Employee").
If you want to serialize a node to a String, you can use an Transformer, something like this:
Transformer t = TransformerFactory.newTransformer();
NodeList nodes = xmlDocument.getElementsByTagName("Employee");
for(int i = 0; i < nodes.getLength(); i++) {
StringWriter sw = new StringWriter();
t.transform(new DOMSource(nodes.item(i)), new StreamResult(sw));
String serialized = sw.toString();
System.out.println(serialized);
}
This would be a possible XSLT transformation:
<xsl:template match="Employees">
<xsl:copy-of select = "Employee" />
</xsl:template>
If you want to serialize a DOM node to a string use e.g.
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.Document;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
...
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS impl =
(DOMImplementationLS)registry.getDOMImplementation("LS");
LSSerializer writer = impl.createLSSerializer();
String str = writer.writeToString(node);
So returning a NodeList you could use
String expression = "/Employees/*";
System.out.println(expression);
NodeList elements = (NodeList)xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
for (int i = 0; i < elements.getLength(); i++)
{
System.out.println(writer.writeToString(element.item(i));
}
I'm trying to read from xml by this code and its not working .. I need to read from id in the specific tag (getelementbyid)
what I need to change in my java code
THIS is my xml code
<book>
<person>
<name>
<first id=1 name="Kiran"/>
<first id=2 name="Pai"/>
</name>
<age>22</age>
</person>
<person>
<name>
<first id=1 name="Bill"/>
<first id=2 name="Gates"/>
</name>
<age>46</age>
</person>
<person>
<name>
<first id=1 name="Steve"/>
<first id=2 name="Jobs"/>
</name>
<age>40</age>
</person>
</book>
and this is my java code
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class XMLReader {
public static void main(String argv[]) {
try {
File file = new File("c:\\MyXMLFile.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);
doc.getDocumentElement().normalize();
System.out.println("Root element " + doc.getDocumentElement().getNodeName());
NodeList nodeLst = doc.getElementsByTagName("employee");
System.out.println("Information of all employees");
for (int s = 0; s < nodeLst.getLength(); s++) {
Node fstNode = nodeLst.item(s);
if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
Element fstElmnt = (Element) fstNode;
NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("firstname");
Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
NodeList fstNm = fstNmElmnt.getChildNodes();
System.out.println("First Name : " + ((Node) fstNm.item(0)).getNodeValue());
NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("lastname");
Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
NodeList lstNm = lstNmElmnt.getChildNodes();
System.out.println("Last Name : " + ((Node) lstNm.item(0)).getNodeValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
You can take a look at JAXB. It is part of Java SE. You only need to map your XML schema to few POJO classes and describe them with JAXB annotations.
Possible code to map an XML to an object, and vice versa, will take ~5 lines.
You can read about it here: http://docs.oracle.com/javase/tutorial/jaxb/intro/
This should work:
public class XMLReader {
public static void main(String argv[]) {
try {
File file = new File("c:\\MyXMLFile.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);
doc.getDocumentElement().normalize();
System.out.println("Root element " + doc.getDocumentElement().getNodeName());
NodeList nodeLst = doc.getElementsByTagName("name");
System.out.println("Information of all employees");
for (int s = 0; s < nodeLst.getLength(); s++) {
Node fstNode = nodeLst.item(s);
if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
Element fstElmnt = (Element) fstNode;
NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("first");
Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
System.out.println("First Name : " +fstNmElmnt.getAttributeNode("name").getNodeValue());
Element lstNmElmnt = (Element) fstNmElmntLst.item(1);
System.out.println("Last Name : " +lstNmElmnt.getAttributeNode("name").getNodeValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
And correct your xml to quote the attributes:
<book>
<person>
<name>
<first id="1" name="Kiran"/>
<first id="2" name="Pai"/>
</name>
<age>22</age>
</person>
<person>
<name>
<first id="1" name="Bill"/>
<first id="2" name="Gates"/>
</name>
<age>46</age>
</person>
<person>
<name>
<first id="1" name="Steve"/>
<first id="2" name="Jobs"/>
</name>
<age>40</age>
</person>
</book>
One of the problems is that the built in Java XML libraries are only so-so. While meant primarily for for HTML, if you are not using xpaths, JSoup is a lot easier to work with.
String yourXml = ...
Document doc = Jsoup.parse(yourXml, "", Parser.xmlParser());
for (Element person : doc.getElementsByTag("person")) {
String firstName = person.getElementById("1").text();
String lastName = person.getElementById("2").text();
}
I have the following xml:
<version>
<name>2.0.2</name>
<description>
-Stop hsql database after close fist <br />
-Check for null category name before adding it to the categories list <br />
-Fix NPE bug if there is no updates <br />
-add default value for variable, change read bytes filter, and description of propertyFile <br />
-Change HTTP web Proxy (the “qcProxy” field ) to http://web-proxy.isr.hp.com:8080 <br />
</description>
<fromversion>>=2.0</fromversion>
</version>
I want to return description tag string content using Java?
This is pretty standard Java XML parsing, you can find it anywhere on the internet, but it goes like this using XPath in standard JDK.
String xml = "your XML";
// load the XML as String into a DOM Document object
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
ByteArrayInputStream bis = new ByteArrayInputStream(xml.getBytes());
Document doc = docBuilder.parse(bis);
// XPath to retrieve the content of the <version>/<description> tag
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("/version/description");
Node description = (Node)expr.evaluate(doc, XPathConstants.NODE);
System.out.println("description: " + description.getTextContent());
Edit
Since you are having XML <br/> in your text content, it cannot be retrieved from Node.getTextContent(). One solution is to transform that Node to XML String equivalent, stripping the root node <description>.
This is a complete example:
String xml = "<version>\r\n" + //
" <name>2.0.2</name>\r\n" + //
" <description>\r\n" + //
"-Stop hsql database after close fist <br />\r\n" + //
"-Check for null category name before adding it to the categories list <br />\r\n" + //
"-Fix NPE bug if there is no updates <br />\r\n" + //
"-add default value for variable, change read bytes filter, and description of propertyFile <br />\r\n" + //
"-Change HTTP web Proxy (the “qcProxy” field ) to http://web-proxy.isr.hp.com:8080 <br />\r\n" + //
"</description>\r\n" + //
" <fromversion>>=2.0</fromversion>\r\n" + //
"</version>";
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
ByteArrayInputStream bis = new ByteArrayInputStream(xml.getBytes());
Document doc = docBuilder.parse(bis);
// XPath to retrieve the <version>/<description> tag
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("/version/description");
Node descriptionNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
// Transformer to convert the XML Node to String equivalent
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
StringWriter sw = new StringWriter();
transformer.transform(new DOMSource(descriptionNode), new StreamResult(sw));
String description = sw.getBuffer().toString().replaceAll("</?description>", "");
System.out.println(description);
prints:
-Stop hsql database after close fist <br/>
-Check for null category name before adding it to the categories list <br/>
-Fix NPE bug if there is no updates <br/>
-add default value for variable, change read bytes filter, and description of propertyFile <br/>
-Change HTTP web Proxy (the “qcProxy” field ) to http://web-proxy.isr.hp.com:8080 <br/>
Edit 2
In order to have them all you need to get a NODESET of the different nodes and iterate over it to do the exact same operation as above.
// XPath to retrieve the content of the <version>/<description> tag
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("//description");
NodeList descriptionNode = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
List<String> descriptions = new ArrayList<String>(); // hold all the descriptions as String
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
for (int i = 0; i < descriptionNode.getLength(); ++i) {
Node descr = descriptionNode.item(i);
StringWriter sw = new StringWriter();
transformer.transform(new DOMSource(descr), new StreamResult(sw));
String description = sw.getBuffer().toString().replaceAll("</?description>", "");
descriptions.add(description);
}
// here you can do what you want with the List of Strings `description`
Here is my code, maybe you will notice right away what I'm missing :
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(fileName));
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//CustomerId");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
Nodelist nodes = (NodeList) result;
Text a = doc.createTextNode("value");
Element p = doc.createElement("newNode");
p.appendChild(a);
for (int i = 0; i < nodes.getLength(); i++) {
nodes.item(i).insertBefore(p, nodes.item(i));
}
I'm trying to insert new node(<newNode>value</newNode>) before CustomerId existing node. Here is my XML sample file :
<Customer>
<names>
<firstName>fName</firstName>
<lastName>lName</lastName>
<middleName>nName</middleName>
<nickName/>
</names>
<addressList>
<address>
<streetInfo>
<houseNumber>22</houseNumber>
<baseName>Street base name</baseName>
<district>kewl district</district>
</streetInfo>
<zipcode>22231</zipcode>
<state>xxx</state>
<country>xxxz</country>
<primary>true</primary>
</address>
</addressList>
<CustomerId/>
<SSN>561381</SSN>
<phone>
<homePhone>123123123</homePhone>
<officePhone/>
<homePhone>21319414</homePhone>
</phone>
<preferred>true</preferred>
</Customer>
This is an exception getting thrown I just don't know what else to try :
NOT_FOUND_ERR: An attempt is made to
reference a node in a context where it
does not exist.
Here an example I just tested using the xml sample you provided.
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setIgnoringComments(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(new File("XmlTest.xml"));
NodeList nodes = doc.getElementsByTagName("CustomerId");
Text a = doc.createTextNode("value");
Element p = doc.createElement("newNode");
p.appendChild(a);
nodes.item(0).getParentNode().insertBefore(p, nodes.item(0));
Here is the result:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Customer>
<names>
<firstName>fName</firstName>
<lastName>lName</lastName>
<middleName>nName</middleName>
<nickName/>
</names>
<addressList>
<address>
<streetInfo>
<houseNumber>22</houseNumber>
<baseName>Street base name</baseName>
<district>kewl district</district>
</streetInfo>
<zipcode>22231</zipcode>
<state>xxx</state>
<country>xxxz</country>
<primary>true</primary>
</address>
</addressList>
<newNode>value</newNode>
<CustomerId/>
<SSN>561381</SSN>
<phone>
<homePhone>123123123</homePhone>
<officePhone/>
<homePhone>21319414</homePhone>
</phone>
<preferred>true</preferred>
</Customer>
If you're interested, here's the sample code I used to show the result:
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
String xmlOutput = result.getWriter().toString();
System.out.println(xmlOutput);
I think you want to insert into the parent, not the child:
nodes.item(i).getParentNode().insertBefore(p, nodes.item(i));