Using Xpath to add a new value to attributes - java

I need to use xpath to navigate to the analysis/analysis parameter attributes by add in a new value:
As a first step I have tried retrieving a value from the analysis tag but cannot get this to work (no sample value being retrieved, no output in console). Can anyone see where I am going wrong here and then further show how I can then add a new value.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
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.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class XPathTestReports {
public static void main(String[] args) {
try {
String xpath = "/UserDocument/report-plan-catalog/collection/collection/collection/report-config/report-plan/settings[#analysis]";
FileInputStream file = new FileInputStream(new File("c:/workspace/savedreportscatalog.xml"));
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(file);
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression xPathExpression = xPath.compile(xpath);
String attributeValue = "" + xPathExpression.evaluate(xmlDocument, XPathConstants.STRING);
System.out.println(attributeValue);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}
}
XML Sample
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<UserDocument>
<report-plan-catalog>
<collection timestamp="" title="report plans" uid="">
<collection container-id="" timestamp="2015-04-29" title="*" uid="">
<collection container-id="94533" timestamp="2015-04-29" title="*" uid="5cfc">
<report-config container-id="5cfc" timestamp="2015-04-29" title="Asset" type="Risk" uid="4718">
<configuration>
<reportType>Live</reportType>
</configuration>
<report-plan name="Asset">
<columns>
<column name="Nom" subtotal-function="Sum" total-function="Sum"/>
<column name="Id"/>
<column name="Ref"/>
</columns>
<settings analysis="someValue" analysisParameters="" filtering-enabled="true" object-actions="false" show-object-actions="true" sorting-enabled="true"/>
<viewpoint kind="simple">
<slices/>
</report-plan>
</report-config>
</collection>
</collection>
</collection>
</report-plan-catalog>
</UserDocument>

It's still not very clear what your end goal is, but
/UserDocument/report-plan-catalog/collection/collection/collection/report-config/report-plan/settings[#analysis]
selects a settings element, given that is has an attribute #analysis, that's what the predicate (inside angle brackets) means. If that is not what you want, use
/UserDocument/report-plan-catalog/collection/collection/collection/report-config/report-plan/settings/#analysis
to select the #analysis attribute of the settings element.
Not very familiar with Java, but I'll make two further guesses:
is there perhaps a namespace in your input document that you have not shown?
perhaps this is not the right way to deal with attribute nodes. Try
string(/UserDocument/report-plan-catalog/collection/collection/collection/report-config/report-plan/settings/#analysis)
EDIT: Now tested the Java code, and
String xpath = "/UserDocument/report-plan-catalog/collection/collection/collection/report-config/report-plan/settings/#analysis";
definitely works, after correcting the input XML, which is currently not well-formed - the viewpoint element is not closed.
I get as output:
$ java XPathTestReports
someValue

Related

Create a Complex XMl file using Java

I need to create a XML file using java, I got the file as I like but i am missing relation tag before every end tag, How Can I get that
Expected File:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<FlyBoy>
<learJet>CL-215</learJet>
<rank>2</rank>
<FlyBoy>
<viper>Mark II</viper>
<rank>1</rank>
<FlyBoy>
<viper>Mark II4455</viper>
<rank>2</rank>
<FlyBoy>
<viper>Mark II56666</viper>
<rank>3</rank>
<relation name="Date" table="Sam"/>
</FlyBoy>
<relation name="Date" table="Mark"/>
</FlyBoy>
<relation name="Date" table="sechma"/>
</FlyBoy>
<relation name="Date" table="John"/>
</FlyBoy>
Output I got:
<FlyBoy><learJet>CL-215</learJet><rank>2</rank><FlyBoy><viper>Mark II</viper><rank>1</rank><FlyBoy><viper>Mark II4455</viper><rank>2</rank><FlyBoy><viper>Mark II56666</viper><rank>3</rank></FlyBoy></FlyBoy></FlyBoy></FlyBoy>
Code:
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
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.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class XmlGenerator {
/**
* Render flyboy
*
*/
private Element renderFlyBoy(Element parent, String viper, String rank) {
Element flyBoyEl = document.createElement("FlyBoy");
parent.appendChild(flyBoyEl);
Element viperEl = document.createElement("viper");
viperEl.setTextContent(viper);
flyBoyEl.appendChild(viperEl);
Element rankEl = document.createElement("rank");
rankEl.setTextContent(rank);
flyBoyEl.appendChild(rankEl);
return flyBoyEl;
}
// Test
public static void main(String[] args) {
try {
Document document = null;
Element root = null;
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
document = documentBuilderFactory.newDocumentBuilder().newDocument();
root = document.createElement("FlyBoy");
document.appendChild(root);
Element learJet = document.createElement("learJet");
learJet.setTextContent("CL-215");
root.appendChild(learJet);
Element rank = document.createElement("rank");
rank.setTextContent("2");
root.appendChild(rank);
Element flyBoy1 = renderFlyBoy(root, "Mark II", "1");
Element flyBoy2 = renderFlyBoy(flyBoy1, "Mark II4455", "2");
Element flyBoy3 = renderFlyBoy(flyBoy2, "Mark II56666", "3");
Element relation_schema= doc.createElement("relation");
flyBoy1.appendChild(relation_schema);
Attr join2 = doc.createAttribute("name");
join2.setValue("Date");
relation_schema.setAttributeNode(join2);
Attr type = doc.createAttribute("table");
type.setValue("xxxxxx");
relation_schema.setAttributeNode(type);
DOMSource domSource = new DOMSource(document);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
StreamResult result = new StreamResult(new File("my.xml"));
transformer.transform(domSource, result);
} catch (ParserConfigurationException | TransformerFactoryConfigurationError | TransformerException e) {
e.printStackTrace();
}
System.out.println("done...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Inside the renderFlyBoy method you need to create the relation in the same way you created others. Then relation will be added inside FlyBoy
Element rankEl = document.createElement("relation");
// code to add attributes
flyBoyEl.appendChild(rankEl);

Getting too many child nodes and cant get attributes

I have a simple XML, and I want to get the attributes. There are a few examples on the web, but I still dont understand why I get 17 when I see only 4. I even try to count locations where I think text could be, but still I don't get that number unless is the length of the output . Which leads me to not know how to get the attribute name of all Tag3.
<?xml version="1.0" encoding="UTF-8"?>
<tag1 xmlns="something">
<xxxxxx-Set>
<tag3 Name="a"/>
<tag3 Name="b"/>
<tag3 Name="c"/>
<tag3 Name="d"/>
</xxxxxx-Set>
<tagB>
<tag3 Name="a"/>
<tag3 Name="b"/>
<tag3 Name="c"/>
<tag3 Name="d"/>
</tagB>
</tag1>
This is my java code:
import java.io.File;
import java.util.Arrays;
import java.util.List;
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 ParseXML {
public static void main(String[] args) {
try {
File test= new File("test.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(test);
NodeList tagAs= doc.getElementsByTagName("xxxxxx-Set").item(0).getChildNodes(); //should be all the tag3 elements?
for(int i = 0; i < tagAs.getLength(); i++) {
System.out.println(tagAs);
System.out.println(i);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Note: adding .getAttributes().getNamedItem("Name").getNodeValue() to the print statement gives me null exception.
And the output is:
[xxxxxx-Set: null]
0
[xxxxxx-Set: null]
1
...
[xxxxxx-Set: null]
16
If you want to take all your Name attributes (it's better to name them with lower case), use next approach:
Element xSet = (Element) doc.getElementsByTagName("xxxxxx-Set").item(0);
NodeList xSetTags = xSet.getElementsByTagName("tag3");
for(int i = 0; i < xSetTags.getLength(); i++) {
Element tag3 = (Element) xSetTags.item(i);
System.out.println(tag3.getAttribute("Name"));
}
I made it using org.w3c.dom.Element class. It's not the best idea to work with org.w3c.dom.Node, because this class represents not only xml elements, but attributes, comments and other too. Look documentation to get difference between Node and Element classes.

XML Parser and xpath expression

I am using java default documentbuilder to parse a xml document which has less than 100 lines of code . It will take 35 milliseconds to parse a document , a single xpath expression takes 15 milliseconds to execute . How can I optimize the time taken for both xml and parser? .
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class XMLParser {
public static final Logger LOGGER = Logger.getLogger(XMLParser.class.getName());
private Map<String,List<NamedNodeMap>> fileVsProperties = new HashMap<String, List<NamedNodeMap>>();
private Document document;
public XMLParser(File file){
this.document = XMLUtil.getDocument(file);
}
public void setProperties(Element file){
NodeList properties = file.getElementsByTagName("property");
List<NamedNodeMap> props = new ArrayList<NamedNodeMap>();
String type = file.getAttribute("type");
String path = file.getAttribute("path");
if("".equals(path)){
LOGGER.log(Level.INFO,"Attribute path is required for a file.");
return;
}
path = path+":"+type;
for(int i = 0;i<properties.getLength();i++){
Element property = (Element) properties.item(i);
props.add(property.getAttributes());
}
setProperties(props,path);
}
private void setProperties(List<NamedNodeMap> properties , String path){
List<NamedNodeMap> previousValue = fileVsProperties.get(path);
if(previousValue != null){
previousValue.addAll(properties);
}else{
fileVsProperties.put(path,properties);
}
}
public Element getConfiguration(String branchName) throws XPathExpressionException{
return (Element)XMLUtil.getElements("/configurations/configuration[#name='"+branchName+"']",document.getDocumentElement(),XPathConstants.NODE);
}
public static void main(String[] args) throws XPathExpressionException {
long start = System.currentTimeMillis();
File doc = new File("install.xml");
XMLParser parser = new XMLParser(doc);
long end = System.currentTimeMillis();
System.out.println("Time Taken For Parsing :: "+ (end-start) + " milliseconds");
start = end;
Element configuration = parser.getConfiguration("BHARATHIKANNAN");
end = System.currentTimeMillis();
System.out.println("Time Taken For XPATH Expression TO Finding the Configuration :: "+ (end-start) + " milliseconds");
start = end;
NodeList files = parser.getFiles(configuration);
for(int i=0;i<files.getLength();i++){
parser.setProperties((Element) files.item(i));
}
end = System.currentTimeMillis();
System.out.println(parser.fileVsProperties);
System.out.println("Time Taken For Setting Properties :: "+ (end-start) + " milliseconds");
}
public NodeList getFiles(Element configuration){
return configuration.getElementsByTagName("file");
}
}
class XMLUtil{
private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
private static DocumentBuilder builder;
public static final Logger LOGGER = Logger.getLogger(XMLUtil.class.getName());
private static XPathFactory xpathFactory = XPathFactory.newInstance();
private static XPath xpath;
static {
try {
builder = factory.newDocumentBuilder();
xpath = xpathFactory.newXPath();
} catch (ParserConfigurationException e) {
LOGGER.log(Level.INFO,"");
}
}
public static Document getDocument(File f){
Document doc = null;
try {
doc = builder.parse(f);
} catch (SAXException e) {
LOGGER.log(Level.WARNING,"Invalid XML Document ",e);
} catch (IOException e) {
LOGGER.log(Level.SEVERE,"No Document Found in the given path",e);
}
return doc;
}
public static Object getElements(String xpathExpression , Element ele ,QName dataType) throws XPathExpressionException{
return xpath.evaluate(xpathExpression, ele,dataType);
}
}
XML File
<?xml version="1.0"?>
<!--
Note : Default configuration loaded using your current branch name . You can extend configurations using extend attribute in configuration
node .
-->
<configurations>
<configuration name="default">
<files>
<file type="xml" path="conf/server.xml.orig">
<property regex="(port=).*" replace="\18080" xpath="/Server/Connector"></property>
<property regex="(port=).*" replace="\18080"></property>
</file>
<file type="text" path="conf/system_properties.conf">
<property regex="(username=).*" replace="\1root" ></property>
</file>
</files>
</configuration>
<configuration name="BHARATHIKANNAN" extends="default">
<files>
<file type="text" path="conf/system_properties.conf">
<property regex="(username=).*" replace="\1root" ></property>
</file>
</files>
</configuration>
</configurations>
Output :
Time Taken For Parsing :: 24 milliseconds
Time Taken For XPATH Expression TO Finding the Configuration :: 14 milliseconds
{conf/system_properties.conf:text=[com.sun.org.apache.xerces.internal.dom.AttributeMap#75d9fd51]}
Time Taken For Setting Properties :: 0 milliseconds
Someone asked recently about a very similar task but with a much larger document (2Mb), and I gave some Saxon timings here:
https://stackoverflow.com/questions/12497928/xpath-speed-comparision/12508614#12508614
These timings are much faster than you are seeing, on a much larger document. Since you are already using Java, switching to Saxon should be very straightforward.
One caveat though is that you start your timings immediately on entry to main() which means you are mainly measuring class loading time rather than XML processing time. My measurements took care to warm up the Java VM before measurement started.
Note that if you're using Saxon, it's best by far to use Saxon's native tree model rather than DOM or other alternatives. We recently published some measurements here:
http://dev.saxonica.com/blog/mike/2012/09/index.html#000194
DOM comes out 8 times worse than Saxon's native tree on average, 23 times worse in the worst case.

XPath produces garbled output instead of Unicode characters

I am parsing this XML file:
<?xml version="1.0" encoding="UTF-8"?>
<tests>
<test category="Русский"/>
<test category="ελληνικά"/>
<test category="中文"/>
<test category="English"/>
</tests>
Main class is:
import java.io.File;
import java.io.FileInputStream;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class TestUnicode {
public static void main(String[] args) throws Exception {
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression lolwhy = xpath.compile("//test");
final InputSource inputSource =
new InputSource(
new FileInputStream(
new File("sample.xml")));
NodeList parent = (NodeList) lolwhy.evaluate(
inputSource,
XPathConstants.NODESET);
System.out.println(parent.getLength());
for (int i = 0; i < parent.getLength(); i++) {
System.out.println(parent.item(i).getAttributes().
getNamedItem("category").getNodeValue());
}
}
}
And the output is:
4
???????
????????
??
English
What am I doing wrong here?
EDIT: ok, this issue was related to hebrew appears as question marks in netbeans and the solution is this: Setting the default Java character encoding?
Could be that the parsing is ok, but the output is wrong.
If you you used a font that doesn't contain those characters, or if you output the values to HTML, but specify a wrong encoding, this can be the result.
The font-issue being the more likely one.
System.out.println is the culprit.
See if this helps
http://hints.macworld.com/article.php?story=20050208053951714

how to disable dtd at runtime in java's xpath?

I got dtd in file and I cant remove it. When i try to parse it in Java I get "Caused by: java.net.SocketException: Network is unreachable: connect", because its remote dtd. can I disable somehow dtd checking?
You should be able to specify your own EntityResolver, or use specific features of your parser? See here for some approaches.
A more complete example:
<?xml version="1.0"?>
<!DOCTYPE foo PUBLIC "//FOO//" "foo.dtd">
<foo>
<bar>Value</bar>
</foo>
And xpath usage:
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class Main {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(new EntityResolver() {
#Override
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
System.out.println("Ignoring " + publicId + ", " + systemId);
return new InputSource(new StringReader(""));
}
});
Document document = builder.parse(new File("src/foo.xml"));
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
String content = xpath.evaluate("/foo/bar/text()", document
.getDocumentElement());
System.out.println(content);
}
}
Hope this helps...
This worked for me:
SAXParserFactory saxfac = SAXParserFactory.newInstance();
saxfac.setValidating(false);
try {
saxfac.setFeature("http://xml.org/sax/features/validation", false);
saxfac.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
saxfac.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
saxfac.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxfac.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
}
catch (Exception e1) {
e1.printStackTrace();
}
I had this problem before. I solved it by downloading and storing a local copy of the DTD and then validating against the local copy. You need to edit the XML file to point to the local copy.
<!DOCTYPE root-element SYSTEM "filename">
Little more info here: http://www.w3schools.com/dtd/dtd_intro.asp
I think you can also manually set some sort of validateOnParse property to "false" in your parser. Depends on what library you're using to parse the XML.
More info here: http://www.w3schools.com/dtd/dtd_validation.asp

Categories