I need to design a utility which searches node using input xpath in XML document and set its value as per given input.
import java.io.File;
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.XPathFactory;
import org.apache.commons.io.FileUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
public class XMLUtil
{
private XPath xPath;
private Document xmlDoc;
private String xmlName;
public XMLUtil(String xmlName) {
this.xmlName = xmlName;
System.out.println(System.getProperty("user.dir"));
File srcXMLFile = new File(System.getProperty("user.dir") + xmlName);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
xmlDoc = factory.newDocumentBuilder().parse(srcXMLFile);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
xPath = XPathFactory.newInstance().newXPath();
}
public void changeXMLNodeValue(String xpath, String query) {
try {
Node node = (Node) xPath.compile(xpath).evaluate(xmlDoc, XPathConstants.NODE);
node.setTextContent(query);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
XMLUtil xmlUtil = new XMLUtil("tc01.xml");
xmlUtil.changeXMLNodeValue("//Customer/State/City","Hyderabad");
}
}
XML File:tc01.xml
<?xml version="1.0" encoding="UTF-8"?>
<Customer>
<State>
<value>Andhra</value>
<City>
<value>amravati</value>
</City>
</State>
</Customer>
Running the code is throwing NullPointerException at changeXMLNodeValue method on trying to compile and evalute the xpath to fetch the node.
Node node = (Node) xPath.compile(xpath).evaluate(xmlDoc, XPathConstants.NODE);
This is a simple xml without any namespace requirement.
I am using the API first please let me know if I missed something.
Related
Throw this program I'm trying to unzip my xml file and parse it using SAX, for the parsing part I have an exception which appeared about ClassCastException class com.sun.org.apache.xerces.internal.dom.DeferredTextImpl cannot be cast to class javax.swing.text.Element, here is my code :
import java.io.File;
import java.io.IOException;
import javax.swing.text.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class Parser {
public static void main(String[] args) throws IOException, ZipException {
String source = "C:\\Users\\java_program\\xml.zip";
String destination = "C:\\Users\\java_program\\xml";
ZipFile zipFile = new ZipFile(source);
zipFile.extractAll(destination);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File ("C:\\Users\\java_program\\xml\\xml.xml"));
document.getDocumentElement().normalize();
NodeList productList = document.getElementsByTagName("product");
for(int i = 0; i<productList.getLength(); i++) {
Node product = productList.item(i);
if (product.getNodeType() == Node.ELEMENT_NODE) {
Element productElement = (Element) product; // the problem is here
NodeList productDetails = product.getChildNodes();
for(int j=0; j<productDetails.getLength(); j++) {
Node detail = productDetails.item(j);
if (product.getNodeType() == Node.ELEMENT_NODE) {
Element detailElement = (Element) detail; // the problem is here
System.out.println(" " + ((org.w3c.dom.Element) detailElement).getTagName() + ": " + detailElement.getAttributes());
}
}
}
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
My XML file have this structure :
<?xml version="1.0" encoding="utf-8"?>
<import xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<products>
<product>
<article_sku>000c66dawt</article_sku>
<brand>HUAWEI</brand>
<brand_reference>HUAWEI-NEXUS-6P-64GO-ARGENT</brand_reference>
<category>SMARTP</category>
<ean13>6901443077359</ean13>
<it_cpu_smartphone_type><![CDATA[Qualcomm Snapdragon S810]]></it_cpu_smartphone_type>
</product>
<product>
<article_sku>000cfxlysl</article_sku>
<brand>HERCULES</brand>
<brand_reference>HERCULES-DJCONTROL-COMPACT</brand_reference>
<category>PLATIN-1</category>
<ean13>3362934745288</ean13>
</product>
</products>
</import>
Any help is really appreciated
You imported the wrong Element class.
Change
import javax.swing.text.Element;
to
import org.w3c.dom.Element;
Also you create detail object which is a node and check that if the previously created product variable is an element and then cast detail to Element.
Node detail = productDetails.item(j);
if (product.getNodeType() == Node.ELEMENT_NODE) {
Element detailElement = (Element) detail;
the product line:
Node product = productList.item(i);
I think you should chacnge to conditional to detail.getNodetype() == Node.ELEMENT_NODE
I am using below the code to add new node to my XML file, but after parsing XML file the empty tags was removed.
Input : Input.xml
<company>
<staff id="1">
<user>8976</user>
<status></status>
<nickname>mkyong</nickname>
<lastname>yong</lastname>
<firstname>lee</firstname>
</company>
Code using for parsing(ADDING NEW TAG)
import java.io.File;
import java.io.IOException;
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.xml.sax.SAXException;
public class ModifyXMLFile2 {
public static void main(String argv[]) {
try {
String filepath = "c:\\input.xml";
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(filepath);
// Get the root element
Node company = doc.getFirstChild();
// Get the staff element , it may not working if tag has spaces, or
// whatever weird characters in front...it's better to use
// getElementsByTagName() to get it directly.
// Node staff = company.getFirstChild();
// Get the staff element by tag name directly
Node staff = doc.getElementsByTagName("company").item(0);
// append a new node to staff
Element age = doc.createElement("level");
age.appendChild(doc.createTextNode("3C"));
staff.appendChild(age);
// write the content into xml file
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(filepath));
transformer.transform(source, result);
System.out.println("Done");
} catch (ParserConfigurationException pce) {
pce.printStackTrace();
} catch (TransformerException tfe) {
tfe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (SAXException sae) {
sae.printStackTrace();
}
}
}
Expected output:
<company>
<staff id="1">
<user>8976</user>
<status></status>
<nickname>mkyong</nickname>
<lastname>yong</lastname>
<firstname>lee</firstname>
<level>3C</level>
</company>
But I'm getting the output as
<company>
<staff id="1">
<user>8976</user>
</status>
<nickname>mkyong</nickname>
<lastname>yong</lastname>
<firstname>lee</firstname>
<level>3C</level>
</company>
Status tag is not coming properly I need like <status></status> but I'm getting as <status/>.
I have below webservice response from outside vendor. Need to print each line in console. Below reponse is store in response object.
<?xml version="1.0" encoding="UTF-8"?>
<loginInformation xmlns="http://www.example.com/restapi" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<loginAccounts>
<loginAccount>
<accountId>117072</accountId>
<baseUrl>https://example.net/restapi/v2</baseUrl>
<email>abc#gmail.com</email>
</loginAccount>
</loginAccounts>
</loginInformation>
my output should be like below :
1.<?xml version="1.0" encoding="UTF-8"?>
2.<loginInformation xmlns="http://www.example.com/restapi" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
.
.
.
.
If you parse the XML string, serialize it and then get it back, you can do that in a few steps. Try this piece of code:
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
import org.w3c.dom.Document;
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;
import java.io.StringWriter;
import java.io.Writer;
final class XmlFormatter {
private XmlFormatter() { }
private static Document parse(String in) {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
final DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new InputSource(new StringReader(in)));
} catch (IOException | ParserConfigurationException | SAXException e) {
System.err.printf("Something happened here due to: %s", e);
}
return null;
}
public static String format(final String xml) {
final Document document = XmlFormatter.parse(xml);
final OutputFormat format = new OutputFormat(document);
final Writer out = new StringWriter();
final XMLSerializer serializer = new XMLSerializer(out, format);
format.setIndenting(true);
format.setLineWidth(120);
format.setIndent(2);
try {
serializer.serialize(document);
return out.toString();
} catch (IOException e) {
System.err.printf("Something happened here...this is why: %s", e);
}
return null;
}
public static void main(final String... args) {
System.out.printf("%s", XmlFormatter.format(/* YOUR UNFORMATTED XML */));
}
}
I have an issue when I append a new node to xml dom. The following code is the dom saving code in xmlFactory.java.
import java.io.File;
import java.io.StringWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import com.ism.msgmgt.domain.Post;
import com.ism.msgmgt.domain.User;
public class XmlFactory {
private static Document userdom;
private static Document postdom;
private static File userfile = new File(User.class.getResource("User.xml").getPath());
static {
try{
DocumentBuilderFactory dbuf = DocumentBuilderFactory.newInstance();
DocumentBuilder dbu = dbuf.newDocumentBuilder();
userdom = dbu.parse(userfile);
DocumentBuilderFactory dbpf = DocumentBuilderFactory.newInstance();
DocumentBuilder dbp = dbpf.newDocumentBuilder();
postdom = dbp.parse(postfile);
} catch (Exception e){
e.printStackTrace();
}
}
public static Document getUserDom(){
return userdom;
}
public static void saveUserDom(){
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
System.out.println(userdom.getFirstChild()+"|"+userdom.getFirstChild().getFirstChild());
DOMSource ds = new DOMSource(userdom);
StreamResult sr = new StreamResult(userfile);
trans.transform(ds, sr);
} catch (Exception e){
throw new RuntimeException(e.getMessage(),e);
}
}
}
Note that the printed output is [users: null]|[#text: ]. Next, it is the code in the UserDao.java.
public class UserDao {
public static Element currentUser = null;
public static Document userdom = XmlFactory.getUserDom();
public static void reg(User u){
clrLogin();
u.setUserid(UidUtil.getUid());
u.setPassword(PwdUtil.encode(u.getPassword()));
Element e = userdom.createElement("user");
e.setAttribute("id", u.getUserid());
e.setAttribute("username", u.getUsername());
e.setAttribute("password", u.getPassword());
e.setAttribute("login", u.getLogin());
userdom.getFirstChild().appendChild(e);
System.out.println(e);
System.out.println(userdom.getFirstChild()+"|"+userdom.getFirstChild().getFirstChild());
XmlFactory.saveUserDom();
currentUser = e;
}
}
Note that in this code, I printed the node I want to add, which is [user: null]. Next, I printed the expecting added node. However, I got the result [users: null]|[#text: ], which is the same as the output printed from above code. So it looks like appendChild() didn't add e to the dom's first node. Please help. Thanks.
XML File
<?xml version="1.0" encoding="UTF-8"?>
<users>
</users>
There's too much context missing from your question, for one, we don't know how the userdom is actually created...
Let me demonstrate, with the following code...
import java.io.ByteArrayOutputStream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class Test1 {
public static Document userdom;
public static void main(String[] args) {
try {
userdom = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = userdom.createElement("users");
Node adoptNode = userdom.adoptNode(root);
userdom.appendChild(adoptNode);
reg();
} catch (ParserConfigurationException | DOMException exp) {
exp.printStackTrace();
}
}
public static void reg() {
Element e = userdom.createElement("user");
e.setAttribute("id", "blah");
e.setAttribute("username", "kermit");
e.setAttribute("password", "bunnies in the air");
e.setAttribute("login", "kermmi");
userdom.getFirstChild().appendChild(e);
System.out.println(e);
System.out.println(userdom.getFirstChild() + "|" + userdom.getFirstChild().getFirstChild());
saveUserDom();
}
public static void saveUserDom() {
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
System.out.println(userdom.getFirstChild() + "|" + userdom.getFirstChild().getFirstChild());
DOMSource ds = new DOMSource(userdom);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
StreamResult sr = new StreamResult(baos);
trans.transform(ds, sr);
System.out.println(new String(baos.toByteArray()));
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
I get the following output...
[user: null]
[users: null]|[user: null]
[users: null]|[user: null]
<?xml version="1.0" encoding="UTF-8" standalone="no"?><users><user id="blah" login="kermmi" password="bunnies in the air" username="kermit"/></users>
Which clearly demonstrates that the appendChild method is working fine.
An immediate concern is the relationship between...
public static Document userdom = XmlFactory.getUserDom();
and
DOMSource ds = new DOMSource(userdom);
One has to ask, are they the same reference (of userdom)?
Updated
This...
private static File userfile = new File(User.class.getResource("User.xml").getPath());
coupled with this...
StreamResult sr = new StreamResult(userfile);
Are your problems...
First, you are loading an internal resource and you should NEVER wrap these in File entry, internal/embedded resources are not accessible from a File context and need to be loaded in a different way, instead, maitain the URL refernce that getResource results...
private static URL userfile = User.class.getResource("User.xml");
The next line, (StreamResult sr = new StreamResult(userfile);) is trying to write the resulting DOM back to an embedded resource...embedded resources can not be written to in this fashion, hence the reason it would fail in a normal running environment (it might work in your IDE, but that's a different issue).
Basically, you can't maintain this information as an embedded resource, you need to externalise the file onto the disk.
FYI:
If you did use the URL of the resource properly, you would need to change the way you are loading the XML document to something like...
try (InputStream is = userfile.openStream()) {
userdom = dbu.parse(is);
}
So I have been asking questions here trying to get the answer for myself but I just cant get it to work without running into a new error. If anyone can help me I would appreciate it. I want to replace this portion
"<a>\n" +
"<b>\n" +
"<c id=\"00001\" time=\"1:00\" day=\"Friday\" name1=\"John\" name2=\"Mary\"></c>\n" +
"<c id=\"00002\" time=\"2:00\" day=\"Monday\" name1=\"Ed\" name2=\"Kate\"></c>\n" +
"<c id=\"00003\" time=\"3:00\" day=\"Sunday\" name1=\"Mary\" name2=\"Ed\"></c>\n" +
"<c id=\"00004\" time=\"4:00\" day=\"Friday\" name1=\"Kate\" name2=\"John\"></c>\n" +
"</b>\n" +
"</a>"
with a XML url instead, as that information will be pulled from a server as the data changes.
Here is the source as you can see what I am trying accomplish once I have the data from the xml file. It works fine as it is, but whenever I try and implement a url as the InputSource I get tons of errors that no matter what ive tried does not resolve the problem.
package com.newxpath;
import java.io.StringReader;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import android.app.Activity;
import android.os.Bundle;
import android.widget.EditText;
public class NewxpathActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
InputSource xml = new InputSource(new StringReader("<a>\n" +
"<b>\n" +
"<c id=\"00001\" time=\"1:00\" day=\"Friday\" name1=\"John\" name2=\"Mary\"></c>\n" +
"<c id=\"00002\" time=\"2:00\" day=\"Monday\" name1=\"Ed\" name2=\"Kate\"></c>\n" +
"<c id=\"00003\" time=\"3:00\" day=\"Sunday\" name1=\"Mary\" name2=\"Ed\"></c>\n" +
"<c id=\"00004\" time=\"4:00\" day=\"Friday\" name1=\"Kate\" name2=\"John\"></c>\n" +
"</b>\n" +
"</a>"));
String name = "Ed";
XPath xpath = XPathFactory.newInstance().newXPath();
String expr = String.format("//a/b/c[#name2='%s']", name);
Node c = null;
try {
c = (Node) xpath.evaluate(expr, xml, XPathConstants.NODE);
} catch (XPathExpressionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
NamedNodeMap attribs = c.getAttributes();
String id = attribs.getNamedItem("id").getNodeValue();
String time = attribs.getNamedItem("time").getNodeValue();
// etc.
EditText id2 = (EditText) findViewById(R.id.id2);
EditText time2 = (EditText) findViewById(R.id.time2);
id2.setText(String.valueOf(id));
time2.setText(String.valueOf(time));
}
}
You probably need to add the INTERNET permission to your AndroidManifest.xml file (I assume from the Android imports that this is taking place on Android). Otherwise I don't see why this wouldn't work. I copied your XML to the URL http://pastebin.com/raw.php?i=RF8cL5YZ and then ran it against the following code at a command line and it worked just fine.
import java.io.StringReader;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import java.net.*;
public class test
{
public static void main(String[] args) throws Exception
{
URL url = new URL("http://pastebin.com/raw.php?i=RF8cL5YZ");
InputSource xml = new InputSource(url.openStream());
String name = "Ed";
XPath xpath = XPathFactory.newInstance().newXPath();
String expr = String.format("//a/b/c[#name2='%s']", name);
Node c = null;
try {
c = (Node) xpath.evaluate(expr, xml, XPathConstants.NODE);
} catch (XPathExpressionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
NamedNodeMap attribs = c.getAttributes();
String id = attribs.getNamedItem("id").getNodeValue();
String time = attribs.getNamedItem("time").getNodeValue();
// etc.
System.out.println("["+String.valueOf(id)+"]["+String.valueOf(time)+"]");
}
}