I'm parsing large XMLs using Java 8 and XmlPath 1.0. I want to extract, name of the Test, his measured values and the outcome (Passed or Failed).
Each Test can have many TestResult which contains one of the two types of limits:
SingleLimit, which will have only one < Limit comparator="XYZ">
LimitPair which will have always two limits
<tr:Test ID = "282" name = "DIP1-8 High">
<tr:Extension>
<ts:TSStepProperties>
...
</ts:TSStepProperties>
</tr:Extension>
<tr:Outcome value = "Passed" /> <!-- value -->
<tr:TestResult ID = "xyz" name = "TC 8.7.4 - Current breaker output J10:1-2"> <!-- name -->
<tr:TestLimits>
<tr:Limits>
<c:LimitPair operator = "AND">
<c:Limit comparator = "GE">
<!-- value -->
<c:Datum nonStandardUnit = "V" value = "2.8" xsi:type="ts:TS_double" flags = "0x0000"/>
</c:Limit>
<c:Limit comparator = "LE">
<!-- value -->
<c:Datum nonStandardUnit = "V" value = "3.5" xsi:type="ts:TS_double" flags = "0x0000"/>
</c:Limit>
</c:LimitPair>
</tr:Limits>
</tr:TestLimits>
</tr:TestResult>
</tr:Test>
Currently I'm using these paths to extract PairLimit measurements and to create String containing values.
My question is how I should write code/xPaths to take care of possible many TestResults inside one Test.
I assumed at the beginning that Test can have only PairLimit or SingleLimit, which was wrong.
My current code extract all values correctly, but assigned measurements are incorrect when there are many TestResults inside Test.
For instance, if Test ID = 1 contains 3 (three) TestResults then in the final String containing measurements, I will have values from first Test inside second, because it will "override" the values.
private ArrayList<String> preparePairLimitPaths() {
final ArrayList<String> list = new ArrayList<>();
list.add("//Test[TestResult//LimitPair]/#name");
list.add("//Test/TestResult[TestLimits//LimitPair]/TestData/Datum/#value");
list.add("//Test/TestResult/TestLimits/Limits/LimitPair/Limit[*]/Datum/#value");
list.add("//Test/TestResult/TestLimits/Limits/LimitPair/Limit[*]/Datum/#value");
list.add("//Test[TestResult//TestLimits//LimitPair]/Outcome/#value");
return list;
}
for (String expr : preparePairLimitPaths) {
try {
final NodeList evaluate = (NodeList) xPath.evaluate(expr, parse, XPathConstants.NODESET);
for (int i = 0; i < evaluate.getLength(); i++) {
final String textContent = evaluate.item(i).getTextContent();
if (textContent != null && !textContent.isEmpty()) {
stringBuilder.append(textContent).append(";");
}
}
stringBuilder.append("###");
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}
You can just iterate over each Test and then iterate over each TestResult and then put the logic with TestLimits etc.
NodeList allTests = (NodeList) xPath.evaluate("/xml/Test", xmlDocument, XPathConstants.NODESET);
for (int i = 0; i < tests.getLength(); i++) {
Element singleTest = (Element) tests.item(i);
// Here, you can extract some values from your test like:
// testOutcome = xPath.evaluate("Outcome/#value", singleTest);
NodeList testResults = (NodeList) xPath.evaluate("TestResult",test, XPathConstants.NODESET);
for (int j=0; j<testResults.getLength(); j++) {
// Now you can iterate over all your testResults from test
// testResultName = xPath.evaluate("#name",testResults.item(j)));
}
}
Related
I'm trying to parse the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<docusign-cfg>
<tagConfig>
<tags>
<approve>approve</approve>
<checkbox>checkbox</checkbox>
<company>company</company>
<date>date</date>
<decline>decline</decline>
<email>email</email>
<emailAddress>emailAddress</emailAddress>
<envelopeID>envelopeID</envelopeID>
<firstName>firstName</firstName>
<lastName>lastName</lastName>
<number>number</number>
<ssn>ssn</ssn>
<zip>zip</zip>
<signHere>signHere</signHere>
<checkbox>checkbox</checkbox>
<initialHere>initialHere</initialHere>
<dateSigned>dateSigned</dateSigned>
<fullName>fullName</fullName>
</tags>
</tagConfig>
</docusign-cfg>
I want to read either the name or content of each tag in the <tags> tag. I can do so with the following code:
public String[] getAvailableTags() throws Exception
{
String path = "/docusign-cfg/tagConfig/tags";
XPathFactory f = XPathFactory.newInstance();
XPath x = f.newXPath();
Object result = null;
try
{
XPathExpression expr = x.compile(path);
result = expr.evaluate(doc, XPathConstants.NODE);
}
catch (XPathExpressionException e)
{
throw new Exception("An error ocurred while trying to retrieve the tags");
}
Node node = (Node) result;
NodeList childNodes = node.getChildNodes();
String[] tags = new String[childNodes.getLength()];
System.out.println(tags.length);
for(int i = 0; i < tags.length; i++)
{
String content = childNodes.item(i).getNodeName().trim().replaceAll("\\s", "");
if(childNodes.item(i).getNodeType() == Node.ELEMENT_NODE &&
childNodes.item(i).getNodeName() != null)
{
tags[i] = content;
}
}
return tags;
}
After some searching I found that parsing it this way causes it to read whitespace between nodes / tags causes those whitespaces to be read as children. In this case the whitespaces are considered children of <tags> .
My output:
37
null
approve
null
checkbox
null
company
null
date
null
decline
null
email
null
emailAddress
null
envelopeID
null
firstName
null
lastName
null
number
null
ssn
null
zip
null
signHere
null
checkbox
null
initialHere
null
dateSigned
null
fullName
null
37 is the number of nodes it found in <tags>
Everything below 37 is the content of the tag array.
How are these null elements being added to the tag array despite my checking for null?
I think that is because of the indexing of tag. The if check also skips an index. So even though value is not being inserted it will result in null. Use separate index for tag array
int j = 0;
for(int i = 0; i < tags.length; i++)
{
String content = childNodes.item(i).getNodeName().trim().replaceAll("\\s", "");
if(childNodes.item(i).getNodeType() == Node.ELEMENT_NODE &&
childNodes.item(i).getNodeName() != null)
{
tags[j++] = content;
}
}
Since you are omitting some of the child nodes, creating an array of entire child nodes length may result in wastage of memory. You can use a List instead. If you are particular about String array you can later convert this to an array as well.
public String[] getAvailableTags() throws Exception
{
String path = "/docusign-cfg/tagConfig/tags";
XPathFactory f = XPathFactory.newInstance();
XPath x = f.newXPath();
Object result = null;
try
{
XPathExpression expr = x.compile(path);
result = expr.evaluate(doc, XPathConstants.NODE);
}
catch (XPathExpressionException e)
{
throw new Exception("An error ocurred while trying to retrieve the tags");
}
Node node = (Node) result;
NodeList childNodes = node.getChildNodes();
List<String> tags = new ArrayList<String>();
for(int i = 0; i < tags.length; i++)
{
String content = childNodes.item(i).getNodeName().trim().replaceAll("\\s", "");
if(childNodes.item(i).getNodeType() == Node.ELEMENT_NODE &&
childNodes.item(i).getNodeName() != null)
{
tags.add(content);
}
}
String[] tagsArray = tags.toArray(new String[tags.size()]);
return tagsArray;
}
The contents of tag array defaults to null.
So it is not a case of how does the element become null, it is the case of it being left as null.
To prove this to yourself, add the following else block like this:
if(childNodes.item(i).getNodeType() == Node.ELEMENT_NODE &&
childNodes.item(i).getNodeName() != null)
{
tags[i] = content;
} else {
tags[i] = "Foo Bar";
}
You should now see 'Foo Bar' instead of null.
A better solution here would be to use an ArrayList, and append the tags to it instead of using an array. Then you do not need to track the indexes and so less chance of this type of bug.
i'm trying to get folder structure of a rule using java TEAMSERVER API .
IlrSessionFactory factory = new IlrRemoteSessionFactory();
try {
factory.connect(login, password, serverUrl, datasource);
IlrSession session = factory.getSession();
IlrRuleProject ruleProject = (IlrRuleProject) IlrSessionHelper.getProjectNamed(session, project);
IlrBaseline currentBaseline = IlrSessionHelper.getCurrentBaseline(session, ruleProject);
session.setWorkingBaseline(currentBaseline);
String query = new String("Find all business rules such that the name of each business rule is \"R105_1_krl\"");
IlrDefaultSearchCriteria criteria = new IlrDefaultSearchCriteria( query.toString());
List summaries = session.findElements(criteria, IlrModelConstants.ELEMENT_SUMMARY);
for (int i = 0; i < summaries.size(); i++) {
IlrElementSummary ruleSummary = (IlrElementSummary) summaries.get(i);
String ruleName = ruleSummary.getName();
System.out.println("\t" + ruleName);
}
If there is as named R105_1_krl rule , I can reach using java and DECİSİON CENTER API. But i need location of this rule. Such as XYZ package / abc folder / def folder
In addition , when i wrote the following two line in loop , i can reach these properties ;
Expiration Date, Effective Date, Created By, Last Changed On ... But, i can not reach folder information of properties of a rule.
IlrActionRule rule = (IlrActionRule) elementDetails;
String lastChangedBy = String.valueOf(rule.getPropertyValue("lastChangedBy"));
Here is the solution.
public static String getHierarchyPath (IlrElementDetails element) {
try {
if (!(element instanceof IlrRule)) return element.getName();
IlrRule rule = (IlrRule)element;
StringBuffer sb = new StringBuffer ();
// Get the rule name
String name = rule.getName();
// Get the rule package
IlrRulePackage current = rule.getRulePackage();
Stack<String> stack = new Stack<String> ();
while (true) {
if (current==null) break;
// Push the package name onto the stack
stack.push("/" + current.getName());
// Next parent ...
current = current.getParent();
}
// Pop the stack and build the path
while (!stack.empty()) {
String folder = (String) stack.pop();
sb.append(folder);
}
// Append the rule name to the path
sb.append("/").append(name);
// Return the built path
return sb.toString();
} catch (Exception e) {
return element.getName();
}
}
I have xml file as below
<dashboard DASHBOARD_ID="1" DASHBOARD_IMAGE="" DASHBOARD_NAME="TestDashboard">
<linkedpages>
<pages page_id=1212 pagename=""/>
<report reportid=212 reportname=""/>
</linkedpages>
my need is that I should import these tag attribute velues int o respective table say page table, report table, dashborad table and so on.
I am get the elements and their attributes by
String attribute = child.getAttribute("report_id");
but I need to write n number of such line, and its not generic, i can have variable length of attributes.
So i need to be able to read all attributes of each tag.
How can this be done Please help, Any idea of doing this is appreciated.
Thank You
Try this method : getAttributes()
And an example:
List<String> attributNames = new ArrayList<String>();
if(child.getAttributes() != null){
for (int i = 0; i < child.getAttributes().getLength(); i++) {
attributNames.add(child.getAttributes().item(i).getNodeName());
}
}
String[] attributes = new String[child.getAttributes().getLength()];
for (int i = 0; i < attributes.length; i++) {
attributes[i] = child.getAttributes().item(i).getNodeValue();
}
i am using Java html parser(link text) to try to parse this line.
<td class=t01 align=right><div id="OBJ123" name=""></div></td>
But I am looking for the value like I see on my web browser, which is a number. Can you help me get the value?
Please let me know if you need more details.
Thanks
From the documentation, all you have to do is find all of the DIV elements that also have an id of OBJ123 and take the first result's value.
NodeList nl = parser.parse(null); // you can also filter here
NodeList divs = nl.extractAllNodesThatMatch(
new AndFilter(new TagNameFilter("DIV"),
new HasAttributeFilter("id", "OBJ123")));
if( divs.size() > 0 ) {
Tag div = divs.elementAt(0);
String text = div.getText(); // this is the text of the div
}
UPDATE: if you're looking at the ajax url, you can use similar code like:
// make some sort of constants for all the positions
const int OPEN_PRICE = 0;
const int HIGH_PRICE = 1;
const int LOW_PRICE = 2;
// ....
NodeList nl = parser.parse(null); // you can also filter here
NodeList values = nl.extractAllNodesThatMatch(
new AndFilter(new TagNameFilter("TD"),
new HasAttributeFilter("class", "t1")));
if( values.size() > 0 ) {
Tag openPrice = values.elementAt(OPEN_PRICE);
String openPriceValue = openPrice.getText(); // this is the text of the div
}
I have this XML file which I parse into an ArrayList
In this ArrayList I have countries and the alarmnumbers for the countries in it.
I want to search to a country and get it's police, ambulance or firedep. number.
Here is the code to help you out.
Parsing XML into ArrayList:
protected ArrayList<Alarmdiensten> getAlarmdiensten() {
ArrayList<Alarmdiensten> lijst = new ArrayList<Alarmdiensten>();
try {
DocumentBuilder builder =DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(getAssets().open("alarmdiensten.xml"));
NodeList nl = doc.getElementsByTagName("land");
for (int i=0;i<nl.getLength();i++) {
Node node = nl.item(i);
Alarmdiensten land = new Alarmdiensten();
land.land = Xml.innerHtml(Xml.getChildByTagName(node, "naam"));
land.landcode = Xml.innerHtml(Xml.getChildByTagName(node, "code"));
land.politie = Xml.innerHtml(Xml.getChildByTagName(node, "politie"));
land.ambulance = Xml.innerHtml(Xml.getChildByTagName(node, "ambulance"));
land.brandweer = Xml.innerHtml(Xml.getChildByTagName(node, "brandweer"));
land.telamba = Xml.innerHtml(Xml.getChildByTagName(node, "telamba"));
land.adresamba = Xml.innerHtml(Xml.getChildByTagName(node, "adresamba"));
lijst.add(land);
}
} catch (Exception e) {;
}
return lijst;
}
The method that will use the alarmnumbers:
public void AlarmMenu(){
String landcode;
ArrayList<Alarmdiensten> diensten = getAlarmdiensten();
if(fakelocation = true) {
landcode = sfakelocation;
}
else {
try {
landcode = getAddressForLocation(this, locationNow).getCountryCode();
} catch (IOException e) {
e.printStackTrace();
}
}
So I have the landcode, and I want to search in the ArrayList diensten for the numbers that belong with the landcode.
How can I do this?
Well at the moment you've got an ArrayList of Alarmdiensten objects. I would suggest you might want to change that to a Map so that you are storing a Map of land codes vs your Alarmdiensten objects.
That way you get the Alarmdiensten out of the Map using the landcode and then simply call the getPolitie() etc methods on your Alarmdiensten object.
I would make sure that you encapsulate your Alarmdiensten object BTW, accessing it's private members directly is a bit of a no-no :)
So something like:
protected Map<String, Alarmdiensten> getAlarmdiensten()
{
Map<String, Alarmdiensten> alarmNumbersForCountries
= new HashMap<String, Alarmdiensten>();
try
{
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(getAssets().open("alarmdiensten.xml"));
NodeList nl = doc.getElementsByTagName("land");
for (int i = 0; i < nl.getLength(); i++)
{
Node node = nl.item(i);
Alarmdiensten land = new Alarmdiensten();
land.setLand(Xml.innerHtml(Xml.getChildByTagName(node, "naam")));
land.setLandcode(Xml.innerHtml(Xml.getChildByTagName(node, "code")));
land.setPolitie(Xml.innerHtml(Xml.getChildByTagName(node, "politie")));
land.setAmbulance(Xml.innerHtml(Xml.getChildByTagName(node, "ambulance")));
land.setBrandweer(Xml.innerHtml(Xml.getChildByTagName(node, "brandweer")));
land.setTelamba(Xml.innerHtml(Xml.getChildByTagName(node, "telamba")));
land.setAdresamba(Xml.innerHtml(Xml.getChildByTagName(node, "adresamba")));
alarmNumbersForCountries.put(land.getLandCode(), land);
}
}
catch (Exception e)
{
// Handle Exception
}
return alarmNumbersForCountries;
}
To get the entry out of the Map
Alarmdiensten land = alarmNumbersForCountries.get(landcode);
Another YMMV point is that you might want to split out the part of your method that builds Alarmdiensten objects from the XML parsing. "Each method should do one thing and one thign well."
Use a for loop and search for it
for(Alarmdiensten land :diensten){
if(land.landcode.equals(landcode) ){
// yes i got it, The current land.
break;
}
}
Just iterate over the list:
String landcode = getLandCode();
for (Alarmdiensten dienst:diensten) {
if (dienst.landcode.equals(landcode)) {
// do what has to be done
}
}
Consider using a map instead of a list, if you have to lookup the values more frequently:
Map<String, List<Alarmdiensten>> servicesInCountry
= new HashMap<String, List<Alarmdiensten>>();
for (Alarmdiensten dienst:diensten) {
List<Alarmdiensten> list = servicesInCountry.get(dienst.landcode);
if (list == null) {
list = new ArrayList<Alarmdiensten>();
servicesInCountry.put(dienst.landcode, list);
}
list.add(dienst);
}
// ... and later on
List<Alarmdiensten> servicesInSweden = servicesInCountry.get("SWE");