Why XPath.evaluate is returning NULL? - java

When I use below code to modify an xml file I receive this error :
Exception in thread "main" java.lang.NullPointerException
at ModifyXMLFile.updateFile(ModifyXMLFile.java:44)
at ModifyXMLFile.main(ModifyXMLFile.java:56)
The error occurs at line : node.setTextContent(newValue);
Am I not using xpath correctly ?
Here is the code and the xml file I'm attempting to update
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
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.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.io.FileUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class ModifyXMLFile {
public void updateFile(String newValue){
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
try {
File f = new File("C:\\pom.xml");
InputStream in = new FileInputStream(f);
InputSource source = new InputSource(in);
Node node = (Node)xpath.evaluate("/project/parent/version/text()", source, XPathConstants.NODE);
node.setTextContent(newValue);
} catch (XPathExpressionException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String argv[]) {
new ModifyXMLFile().updateFile("TEST");
}
}
xml file :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>testgroup</groupId>
<artifactId>testartifact</artifactId>
<version>update</version>
</parent>
</project>

xmlns="http://maven.apache.org/POM/4.0.0"
means that the un-prefixed element names in the XML file are in this namespace. In XPath 1.0 unprefixed node names always refer to nodes in no namespace, so /project/parent/version correctly matches nothing.
To match namespaced nodes in XPath you need to bind a prefix to the namespace URI and then use that prefix in the expression. For javax.xml.xpath this means creating a NamespaceContext. Unfortunately there are no default implementations of this interface in the standard Java library, but the Spring framework provides a SimpleNamespaceContext that you can use
XPath xpath = xPathfactory.newXPath();
SimpleNamespaceContext nsCtx = new SimpleNamespaceContext();
xpath.setNamespaceContext(nsCtx);
nsCtx.bindNamespaceUri("pom", "http://maven.apache.org/POM/4.0.0");
// ...
Node node = (Node)xpath.evaluate("/pom:project/pom:parent/pom:version/text()", source, XPathConstants.NODE);
That said, you'll still need to do a bit more work to actually modify the file, as you're currently loading it and modifying the DOM but then not saving the modified DOM anywhere.
An alternative approach might be to use XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:pom="http://maven.apache.org/POM/4.0.0">
<xsl:param name="newVersion" />
<!-- identity template - copy input to output unchanged except when
overridden -->
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<!-- override for the version value -->
<xsl:template match="pom:version/text()">
<xsl:value-of select="$newVersion" />
</xsl:template>
</xsl:stylesheet>
Then you can use the Transformer API to call this stylesheet with an appropriate parameter
StreamSource xslt = new StreamSource(new File("transform.xsl"));
Transformer transformer = TransformerFactory.newInstance().newTransformer(xslt);
transformer.setParameter("newVersion", newValue);
StreamSource input = new StreamSource(new File("C:\\pom.xml"));
StreamResult output = new StreamResult(new File("C:\\updated-pom.xml"));
transformer.transform(input, output);

Since your XML document is namespace qualified (see xmlns attribute in the project element):
<project xmlns="http://maven.apache.org/POM/4.0.0" ...
You will need to set an implementation of javax.xml.namespace.NamespaceContext on your XPath object. This is used to return the namespace information for an individual step in the XPath.
// SET A NAMESPACECONTEXT
xpath.setNamespaceContext(new NamespaceContext() {
#Override
public Iterator getPrefixes(String namespaceURI) {
return null;
}
#Override
public String getPrefix(String namespaceURI) {
return null;
}
#Override
public String getNamespaceURI(String prefix) {
if("pom".equals(prefix)) {
return "http://maven.apache.org/POM/4.0.0";
}
return null;
}
});
You need to change your XPath to include the prefixes used in the NamespaceContext. Now you are just not looking for an element called project, you are looking for a namespace qualified element with local name project, the NamespaceContext will resolve the prefix to match the actual URI you are looking for.
Node node = (Node)xpath.evaluate("/pom:project/pom:parent/pom:version/text()", source, XPathConstants.NODE);

#Adrain Please use the following xpath and you should be able to fetch or change the value
/parent/version/text()

Related

Java - Split XML using XPath but with its Parent Tag

I have following XML String :
<Aaaa>
<Bbbb>
<GroupC>
<KeyId>10001</KeyId>
</GroupC>
<DetailC>
<Dddd>
<Eeee>Eeee 001</Eeee>
<Ffff>Ffff 001</Ffff>
</Dddd>
</DetailC>
<DetailC>
<Dddd>
<Eeee>Eeee 002</Eeee>
<Ffff>Ffff 002</Ffff>
</Dddd>
</DetailC>
</Bbbb>
</Aaaa>
I would like to split "DetailC" it into the smaller XML:
XML 01:
<Aaaa>
<Bbbb>
<GroupC>
<KeyId>10001</KeyId>
</GroupC>
<DetailC>
<Dddd>
<Eeee>Eeee 001</Eeee>
<Ffff>Ffff 001</Ffff>
</Dddd>
</DetailC>
</Bbbb>
</Aaaa>
XML 02:
<Aaaa>
<Bbbb>
<GroupC>
<KeyId>10001</KeyId>
</GroupC>
<DetailC>
<Dddd>
<Eeee>Eeee 002</Eeee>
<Ffff>Ffff 002</Ffff>
</Dddd>
</DetailC>
</Bbbb>
</Aaaa>
Can I know how can I do so using Java?
Currently I only able to split into separate XML,
but it is without <Aaaa>, <Bbbb>, <GroupC>
Java code:
package message;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
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 org.apache.xpath.CachedXPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;
import org.xml.sax.InputSource;
public class mainClass {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
String path = "D:\\abc.xml";
String xml = readFile(path);
List<String> xmlList2 = splitXML(xml, "/Aaaa/Bbbb/DetailC");
for (String xmlC : xmlList2) {
System.out.println("xmlC: " + xmlC);
}
}
private static List<String> splitXML(String xmlMessage, String xPath) throws Exception {
List<String> xmlList = new ArrayList<>();
Transformer xform = TransformerFactory.newInstance().newTransformer();
xform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
InputSource parameterSource = new InputSource(new StringReader(xmlMessage));
Document doc = dBuilder.parse(parameterSource);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true); // never forget this!
CachedXPathAPI cachedXPathAPI = new CachedXPathAPI();
NodeIterator nl = cachedXPathAPI.selectNodeIterator(doc, xPath);
Node node;
while ((node = nl.nextNode()) != null) {
StringWriter buf = new StringWriter();
DOMSource dom = new DOMSource(node);
xform.transform(dom, new StreamResult(buf));
xmlList.add(buf.toString());
}
return xmlList;
}
private static String readFile(String path) {
String content = "";
try (Stream<String> lines = Files.lines(Paths.get(path))) {
content = lines.collect(Collectors.joining(System.lineSeparator()));
} catch (IOException e) {
e.printStackTrace();
}
return content;
}
}
If you use Saxon 9 HE (availabe on Sourceforge and Maven for Java) you can solve that with XSLT 3, see the approach from Split XML file into multiple files using XSLT where you can change the code to
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0"
exclude-result-prefixes="xs">
<xsl:template match="DetailC">
<xsl:variable name="pos" as="xs:integer">
<xsl:number/>
</xsl:variable>
<xsl:result-document href="XML{format-number($pos, '000')}.xml">
<xsl:apply-templates select="/" mode="split">
<xsl:with-param name="this-detail" select="." tunnel="yes"/>
</xsl:apply-templates>
</xsl:result-document>
</xsl:template>
<xsl:template match="#* | node()" mode="split">
<xsl:copy>
<xsl:apply-templates select="#* | node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="DetailC" mode="split">
<xsl:param name="this-detail" tunnel="yes"/>
<xsl:if test=". is $this-detail">
<xsl:next-match/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
To run Saxon 9 in Java you can use either the JAXP transformation API http://saxonica.com/html/documentation/using-xsl/embedding/jaxp-transformation.html or the Saxon 9 specific s9api http://saxonica.com/html/documentation/using-xsl/embedding/s9api-transformation.html.
Keep in mind that Transformer can directly transform a file with StreamSource (e.g. https://docs.oracle.com/javase/8/docs/api/javax/xml/transform/stream/StreamSource.html#StreamSource-java.lang.String- or https://docs.oracle.com/javase/8/docs/api/javax/xml/transform/stream/StreamSource.html#StreamSource-java.io.File-) so there is no need to read in the file contents in a string or to build a DOM by hand, you can load any XML file directly as the input to XSLT.

How do I delete XML tags from XML document in java

How do I delete XML tags from XML document in java?
For Example, I have the following XML:
<root>
<item>
<code>100001</code>
<price>456</price>
<name>ABC</name>
</item>
<item>
<code>100002</code>
<price>123</price>
<name>DEF</name>
</item>
<item>
<code>100003</code>
<price>887</price>
<name>XYZ</name>
</item>
</root>
I want to delete the price tag from the XML and produce the XML in the following manner:
<root>
<item>
<code>100001</code>
<name>ABC</name>
</item>
<item>
<code>100002</code>
<name>DEF</name>
</item>
<item>
<code>100003</code>
<name>XYZ</name>
</item>
</root>
Additionally the requirement is such that the XML tags can change as I get this information from a webservice which can change the information it sends.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.util.ArrayList;
public class JAXBExample {
static Root root = new Root();
public static void main(String[] args) throws JAXBException {
File file = new File("C:\\file.xml");
File file1 = new File("C:\\result.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Root rootEle = (Root) jaxbUnmarshaller.unmarshal(file);
ArrayList<item> itemList =new ArrayList<>();
for (int i = 0; i < rootEle.getItemList().size() ; i++){
item itemLocal = new item();
itemLocal.setCode(rootEle.getItemList().get(i).getCode());
itemLocal.setName(rootEle.getItemList().get(i).getName());
itemList.add(itemLocal);
}
root.setItemList(itemList);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(root, file1);
jaxbMarshaller.marshal(root, System.out);
}
}
Root class:
import org.apache.activemq.kaha.impl.data.Item;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
#XmlRootElement(name = "root")
public class Root {
public ArrayList<item> getItem() {
return itemList;
}
public void setItem(ArrayList<item> item) {
this.itemList = item;
}
private ArrayList<item> itemList;
}
Item calss:
public class item {
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String code;
private String name;
}
I hope this will help.
As commented, consider XSLT that runs the Identity Transform and an empty template on the node you wish to remove. No for loops, if logic, or use of data structures (e.g., arraylist, hashmaps) needed for this solution.
As information, XSLT is a special purpose language (sibling to XPath) designed to transform xml files into other xml, html, even text files (csv/txt). General purpose languages like Java, PHP, Python, and others maintain libraries to run XSLT 1.0 scripts and can even call external dedicated XSLT processors like libxslt/Saxon. Additionally, XSLT scripts are themselves well-formed XML files and can be parsed from file or string for any dynamic needs.
XSLT (save as .xsl script to be parsed from file in Java below)
<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 price tags -->
<xsl:template match="price"/>
</xsl:transform>
Java
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);
}
}

Split XML using a JDOM Java

I have following XML string.
<Engineers>
<Engineer>
<Name>JOHN</Name>
<Position>STL</Position>
<Team>SS</Team>
</Engineer>
<Engineer>
<Name>UDAY</Name>
<Position>TL</Position>
<Team>SG</Team>
</Engineer>
<Engineer>
<Name>INDRA</Name>
<Position>Director</Position>
<Team>PP</Team>
</Engineer>
</Engineers>
I need to split this xml into smaller xml strings when Xpath is given as Engineers/Enginner.
Smaller xml strings are as follows
<Engineer>
<Name>INDRA</Name>
<Position>Director</Position>
<Team>PP</Team>
</Engineer>
<Engineer>
<Name>JOHN</Name>
<Position>STL</Position>
<Team>SS</Team>
</Engineer>
I have implemented following using saxon xpath and JDOM.
import net.sf.saxon.Configuration;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.om.DocumentInfo;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.XPathCompiler;
import net.sf.saxon.s9api.XPathSelector;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.xpath.XPathFactoryImpl;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.junit.Test;
import org.xml.sax.InputSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.StringReader;
import java.util.Iterator;
import java.util.List;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathFactoryConfigurationException;
public void testXML() throws XPathFactoryConfigurationException, XPathExpressionException, Exception {
System.setProperty("javax.xml.xpath.XPathFactory:" + NamespaceConstant.OBJECT_MODEL_JDOM,
"net.sf.saxon.xpath.XPathFactoryImpl");
XPathFactory xPathFactory = XPathFactory.newInstance(NamespaceConstant.OBJECT_MODEL_JDOM);
XPath xPath = xPathFactory.newXPath();
InputSource inputSource = new InputSource(new File(filename).toURI().toString());
SAXSource saxSource = new SAXSource(inputSource);
Configuration config = ((XPathFactoryImpl) xPathFactory).getConfiguration();
DocumentInfo document = config.buildDocument(saxSource);
XPathExpression xPathExpression = xPath.compile("//Engineers/Engineer");
List matches = (List) xPathExpression.evaluate(document, XPathConstants.NODESET);
if (matches != null) {
for (Iterator iter = matches.iterator(); iter.hasNext(); ) {
NodeInfo node = (NodeInfo) iter.next();
System.out.println(node.getDisplayName() + " - " + node.getStringValue());
}
}
}
It gives following result.
Engineer -
JOHN
STL
SS
Engineer -
UDAY
TL
SG
Engineer -
INDRA
Director
PP
How can I change the code so that I get my desired output?Or is there a way to get the names of child attributes(Name,Position,Team) inside Engineer
If you are using JDOM for this work, you should consider using the native JDOM methods instead of the abstraction run through Saxon.
Consider something like:
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.xpath.XPathFactory;
import org.jdom2.xpath.XPAthExpression;
import org.jdom2.output.XMLOutputter;
import org.jdom2.input.SAXBuilder;
import org.jdom2.filter.Filters;
....
XPathExpression xpe = XPathFactory.instance()
.compile("//Engineers/Engineer", Filters.element());
Document doc = new SAXBuilder().build(new File(filename));
XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
for (Element e : xpe.evaluate(doc)) {
xout.output(e, System.out);
}
I would do the splitting in XSLT:
<xsl:stylesheet ....>
<xsl:template match="Engineeers/Engineer">
<xsl:result-document href="{position()}.xml">
<xsl:copy-of select="."/>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>
If you want the result as a list of JDOM documents then you can supply Saxon with an OutputURIResolver:
Controller controller = transformer.getUnderlyingController();
final Configuration config = controller.getConfiguration();
List<Document> jdomDocuments = new ArrayLis<Document>();
Controller.setOutputURIResolver(new OutputURIResolver() {
public Result resolve(href, base) {
return new JDOM2Writer(config.makePipelineConfiguration());
}
public void close(Result result) {
jdomDocuments.add(((JDOM2Writer)result).getDocument());
}
}
and on completion the results will be in jdomDocuments.

why xml file does not aligned properly after append the string in beginning and end of the file using java?

I have added the string in beginning and end of the XML File.but the alignment is not proper after getting the result.
My XML File:
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
import com.google.common.io.Resources;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URL;
import java.nio.charset.Charset;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
public class ModifyXMLFile {
public static void main(String args[]) {
try {
// Point the file directory path here
String directory = "C:\\Users\\n444479\\Desktop\\SA";
int test = new File("C:\\Users\\n444479\\Desktop\\SA").listFiles().length;
File[] files = new File(directory).listFiles();
// Loop the file to run all the XML files
for (int j = 0; j < test; j++) {
System.out.println(files[j]);
String filepathext = files[j].toString();
DocumentBuilderFactory docFactory = DocumentBuilderFactory
.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(filepathext);
/*
// XML file update starts here
// 1.Add the attribute element with value between the two XML
// Child elements
NodeList nodes = doc.getElementsByTagName("dummySegmentOne");
Text a = doc.createTextNode("value"); Element p =
doc.createElement("newNode"); p.appendChild(a);
nodes.item(0).getParentNode().insertBefore(p, nodes.item(0));
// 2.Add the attribute element without value between the two XML
// Child elements
NodeList nodesa =
doc.getElementsByTagName("customerLevelRegDocs"); Element q =
doc.createElement("dummySegmentOne");
nodesa.item(0).getParentNode().insertBefore(q,
nodesa.item(0));
// 3.Rename the element in parent and child both using the JAXP
// Parser
*/
// XSLT File:
String xsltResource = "C:\\Users\\n444479\\Desktop\\AB\\test.xml";
StringWriter xmlResultResource = new StringWriter();
Transformer xmlTransformer = TransformerFactory.newInstance().newTransformer(
new StreamSource(new File(xsltResource)));
xmlTransformer.transform(new StreamSource(new File(filepathext)),new StreamResult(xmlResultResource));
// XML file update end here
// write the content into XML file
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new String(filepathext));
transformer.transform(source, result);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
// Successful output once get it "Done"
String output = xmlResultResource.getBuffer().toString();
// Writing the transformed XML to a file
FileWriter fileWriter = new FileWriter(filepathext);
fileWriter.write(output);
fileWriter.close();
System.out.println("Done");
}
}
// Exception handling
catch (ParserConfigurationException pce) {
pce.printStackTrace();
} catch (TransformerException tfe) {
tfe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (SAXException sae) {
sae.printStackTrace();
}
}
}
Once the program executed the string get appended properly but the XML File alignment changed.
The output i am getting as below
<?xml version="1.0" encoding="UTF-8"?>{% from lxml import etree %}{% from StringIO import StringIO %}{% set tree = parse_xml(request_text) %}{% set namespaces = {'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/', 'wbs': 'http://xml.ama.com/ws/2009/01/WBS_Session-2.0.xsd'}%}<soap:Envelope xmlns:xalan="http://xml.apache.org/xalan" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><Session><SessionId>{{tree.xpath('//soapenv:Header/wbs:Session/wbs:SessionId', namespaces=namespaces)[0].text}}</SessionId><SequenceNumber>{{int(tree.xpath('//soapenv:Header/wbs:Session/wbs:SequenceNumber', namespaces=namespaces)[0].text)+1}}</SequenceNumber><SecurityToken>{{tree.xpath('//soapenv:Header/wbs:Session/wbs:SecurityToken', namespaces=namespaces)[0].text}}</SecurityToken></Session></soap:Header><soap:Body><reRoot><reNode> world</reNode></reRoot></soap:Body></soap:Envelope>
why the file not getting appeneded
The reason is that DataInputStream.readLine discards the newline character. So, you'll have to append it yourself.
Moreover:
Take in account that the resulting XML is incorrect, because the original XML header turns out to be included in one node. The header (if present) must stay always before the root node.
To properly transform your input XML to a SOAP, I recommend you two alternatives:
Either transform it through an XSL stylesheet.
Or either build the resulting XML with DOM and include the source XML as one child node. Then, when serializing the whole document, you may specify indentation parameters.
Update
I suggest you this XSL:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">{% from lxml import etree %}{% from StringIO import StringIO %}{% set tree = parse_xml(request_text) %}{% set namespaces = {'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/', 'wbs': 'http://xml.ama.com/ws/2009/01/WBS_Session-2.0.xsd'}%}<soap:Envelope xmlns:xalan="http://xml.apache.org/xalan" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<Session>
<SessionId>{{tree.xpath('//soapenv:Header/wbs:Session/wbs:SessionId', namespaces=namespaces)[0].text}}</SessionId>
<SequenceNumber>{{int(tree.xpath('//soapenv:Header/wbs:Session/wbs:SequenceNumber', namespaces=namespaces)[0].text)+1}}</SequenceNumber>
<SecurityToken>{{tree.xpath('//soapenv:Header/wbs:Session/wbs:SecurityToken', namespaces=namespaces)[0].text}}</SecurityToken>
</Session>
</soap:Header>
<soap:Body><xsl:copy-of select="."/>
</soap:Body>
</soap:Envelope>
</xsl:template>
</xsl:stylesheet>
In other words: It creates a SOAP message with a fixed format, and sets the input xml as the contents of the <soap:Body> node.
And the transformation code:
private static void transform(org.w3c.dom.Document doc, java.io.InputStream inputXsl, java.io.OutputStream out)
throws java.io.IOException,
javax.xml.transform.TransformerConfigurationException,
javax.xml.transform.TransformerException
{
javax.xml.transform.Templates templates=javax.xml.transform.TransformerFactory.newInstance().newTemplates(new javax.xml.transform.stream.StreamSource(inputXsl));
javax.xml.transform.Transformer transformer=templates.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
javax.xml.transform.Result result=new javax.xml.transform.stream.StreamResult(out);
javax.xml.transform.Source source=new javax.xml.transform.dom.DOMSource(doc);
if (doc.getInputEncoding() != null)
{
transformer.setOutputProperty("encoding", doc.getInputEncoding());
}
transformer.transform(source, result);
}
The whole result is re-indented by means of two causes:
The xsl:strip-space in the XSL.
The indent and indent-amount properties in the transformation.
The below XSL Stylesheet will be formatting the xml properly
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="/">
{% from lxml import etree %}{% from StringIO import StringIO %}{% set tree = parse_xml(request_text) %}{% set namespaces = {'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/', 'wbs': 'http://xml.ama.com/ws/2009/01/WBS_Session-2.0.xsd'}%}
<soap:Envelope xmlns:xalan="http://xml.apache.org/xalan" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<Session>
<SessionId>{{tree.xpath('//soapenv:Header/wbs:Session/wbs:SessionId', namespaces=namespaces)[0].text}}</SessionId>
<SequenceNumber>{{int(tree.xpath('//soapenv:Header/wbs:Session/wbs:SequenceNumber', namespaces=namespaces)[0].text)+1}}</SequenceNumber>
<SecurityToken>{{tree.xpath('//soapenv:Header/wbs:Session/wbs:SecurityToken', namespaces=namespaces)[0].text}}</SecurityToken>
</Session>
</soap:Header>
<soap:Body><xsl:copy-of select="."/>
</soap:Body>
</soap:Envelope>
</xsl:template>
</xsl:stylesheet>
#Little Santi thanks for helping me and getting understand things.

JDom returns null

I am trying to parse an XML and the code that I use is as follows:
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
public class Main {
public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:comm3=\"http://www.csapi.org/schema/parlayx/common/v3_1\" xmlns:sms7=\"http://www.csapi.org/schema/parlayx/sms/notification/v3_1/local\">\r\n <SOAP-ENV:Header>\r\n <comm3:NotifySOAPHeader>\r\n <spId>37</spId>\r\n </comm3:NotifySOAPHeader>\r\n </SOAP-ENV:Header>\r\n <SOAP-ENV:Body SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n <sms7:notifySmsDeliveryReceipt>\r\n <sms7:correlator>123</sms7:correlator>\r\n <sms7:deliveryStatus>\r\n <address>tel:959257118550</address>\r\n <deliveryStatus>DeliveredToTerminal</deliveryStatus>\r\n </sms7:deliveryStatus>\r\n </sms7:notifySmsDeliveryReceipt>\r\n </SOAP-ENV:Body>\r\n</SOAP-ENV:Envelope>\r\n";
System.out.println("xml:");
System.out.println(xml);
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder loader = factory.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xml));
Document doc = loader.parse(is);
NodeList nodeList = doc.getElementsByTagNameNS("http://www.csapi.org/schema/parlayx/sms/notification/v3_1/local", "correlator");
Element correlatorElement = (Element) nodeList.item(0);
System.out.println(correlatorElement);
NodeList addressNodeList = doc.getElementsByTagName("address");
System.out.println(addressNodeList.item(0));
}
}
Without throwing any exception, the code prints the results but what we get is not what we would expect.
xml:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:comm3="http://www.csapi.org/schema/parlayx/common/v3_1" xmlns:sms7="http://www.csapi.org/schema/parlayx/sms/notification/v3_1/local">
<SOAP-ENV:Header>
<comm3:NotifySOAPHeader>
<spId>37</spId>
</comm3:NotifySOAPHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<sms7:notifySmsDeliveryReceipt>
<sms7:correlator>123</sms7:correlator>
<sms7:deliveryStatus>
<address>tel:959257118550</address>
<deliveryStatus>DeliveredToTerminal</deliveryStatus>
</sms7:deliveryStatus>
</sms7:notifySmsDeliveryReceipt>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
[sms7:correlator: null]
[address: null]
Process finished with exit code 0
Why are the values null?
There is 3 reason, why ist not working:
The node really doesn't exist!
The node is in a different namespace and you need to use the version of the method that also accepts the namespace: Element#getChild(String, Namespace) or Element#getChildText(String, Namespace).
Most importantly, remember that these methods return only immediate descendents of a node! So if you're looking to get a node nested at the nth level, you will need to walk the tree to its parent and only then can you call #getChild() and actually get the child!

Categories

Resources