parsing Xml with NodeList and DocumentBuilder - java

Having a bit of trouble parsing xml with dom and DocumentBuilder. I'm able to get it working, but I guess I get a bit confused with all the child nodes, etc.
Here's the XML I'm working with:
<?xml version="1.0" encoding="utf-8"?>
<LabTests>
<LabTest type="specialty" name="Anti-FXa activity" id="antiFXa" order="16">
<values unit="U/mL" default="N/A">
<value type="increased" val="0">
<conditions>
<condition>Heparin effect</condition>
</conditions>
</value>
<value type="normal" val="">
<conditions></conditions>
</value>
<value type="decreased" val="">
<conditions></conditions>
</value>
</values>
</LabTest>
<LabTest type="general" name="aPTT" id="aPTT" order="">
<values unit="secs" default="N/A">
<value type="increased" val="">
<conditions>
<condition>Acquired hemophilia</condition>
<condition>Acquired vWD</condition>
<condition>DIC</condition>
<condition>Dysfibrinogenemia</condition>
<condition>FI deficiency</condition>
<condition>FII deficiency</condition>
<condition>FII/IIa inhibitors</condition>
<condition>FIX deficiency</condition>
<condition>FIX inhibitors</condition>
<condition>FV deficiency</condition>
<condition>FV inhibitors</condition>
<condition>FVIII deficiency</condition>
<condition>FX deficiency</condition>
<condition>FX inhibitors</condition>
<condition>FXI deficiency</condition>
<condition>FXI inhibitors</condition>
<condition>FXII deficiency</condition>
<condition>FXII inhibitors</condition>
<condition>Heparin effect</condition>
<condition>Liver disease effect</condition>
<condition>Lupus anticoagulant</condition>
<condition>Monoclonal gammopathy</condition>
<condition>Vitamin K deficiency</condition>
<condition>vWD type 1</condition>
<condition>vWD type 2</condition>
<condition>vWD type 3</condition>
<condition>Warfarin effect</condition>
</conditions>
</value>
<value type="normal" val="">
<conditions>
<condition>DIC</condition>
<condition>Dysfibrinogenemia</condition>
<condition>FVII deficiency</condition>
<condition>FXIII deficiency</condition>
<condition>FVII inhibitors</condition>
<condition>Liver disease effect</condition>
<condition>Lupus anticoagulant</condition>
<condition>Monoclonal gammopathy</condition>
<condition>Vitamin K deficiency</condition>
<condition>vWD type 1</condition>
<condition>vWD type 2</condition>
<condition>vWD type 3</condition>
<condition>Warfarin effect</condition>
</conditions>
</value>
<value type="decreased" val="">
<conditions>
<condition>DIC</condition>
</conditions>
</value>
</values>
</LabTest>
</LabTests>
what I'm trying to do is grab hold of each LabTest element and, within each of those elements, grab hold of the value elements (and grab the value of type) and, within the value element, grab hold of all of the condition elements.
In the end, I want something like a Map<String, HashMap<String, ArrayList<String>>, where the String is the LabTest name and the HashMap uses the type (e.g. decreased, increased, etc) for the key and then fills up the ArrayList with the conditions for that value type.
Confusing enough?
Basically, I just need an example, I think, of how to loop through and grab each LabTest with its "value" elements, and each of the "condition" elements under those "value" elements.

That should work as you described:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("input.xml");
NodeList labTestList = doc.getElementsByTagName("LabTest");
for (int i = 0; i < labTestList.getLength(); ++i)
{
Element labTest = (Element) labTestList.item(i);
String labTestType = labTest.getAttribute("type");
NodeList valueList = labTest.getElementsByTagName("value");
for (int j = 0; j < valueList.getLength(); ++j)
{
Element value = (Element) valueList.item(j);
String valueType = value.getAttribute("type");
NodeList conditionList = value.getElementsByTagName("condition");
for (int k = 0; k < conditionList.getLength(); ++k)
{
Element condition = (Element) conditionList.item(k);
String conditionText = condition.getFirstChild().getNodeValue();
}
}
}

Related

If my xml has two nodes with the same name, how to know the node I'm into?

I have the following xml file that contains twice the element manufacturedMaterial.
I'm using NodeList nodes = dom.getElementsByTagName("manufacturedMaterial"); to access the elements of manufacturedMaterial and add some subelements. However, the subelements are added also to the second manufacturedMaterial.
The code I'm using is:
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
}
Is there a way to check if I'm in the first or second node of manufacturedMaterial?
<product>
<manufacturedProduct classCode="MANU">
<templateId root="1.3.6.1.4.1.12559.11.10.1.3.1.3.1"/>
<templateId root="2.16.840.1.113883.10.20.1.53"/>
<manufacturedMaterial classCode="MMAT" determinerCode="KIND">
<code code="10219000" codeSystem="0.4.0.127.0.16.1.1.2.1" codeSystemName="EDQM" codeSystemVersion="2022-01-18" displayName="Tablet"/>
<name>ALGOFEN TABLET 500MG</name>
<epsos:desc xmlns="">500 mg</epsos:desc>
<epsos:formCode xmlns="" code="658244" codeSystem="2.16.724.4.21.5.15.4" displayName="PARACETAMOL NORMON 500MG 20 COMPRIMIDOS EFG"/>
<epsos:asContent xmlns="urn:epsos-org:ep:medication" classCode="CONT">
<epsos:containerPackagedMedicine xmlns="" classCode="CONT" determinerCode="INSTANCE">
<epsos:name/>
<epsos:formCode code="116" codeSystem="1.3.6.1.4.1.12559.11.10.1.3.1.44.4" codeSystemName="eHDSIDisplayLabel" displayName=""/>
<epsos:capacityQuantity unit="1" value="20.0"/>
<epsos:capTypeCode nullFlavor="NA"/>
</epsos:containerPackagedMedicine>
</epsos:asContent>
<epsos:asSpecializedKind xmlns="urn:epsos-org:ep:medication" classCode="GEN">
<epsos:generalizedMedicineClass classCode="MMAT">
<code code="N02BE01" codeSystem="2.16.840.1.113883.6.73" codeSystemName="Anatomical Therapeutic Chemical" codeSystemVersion="2022-01" displayName="paracetamol"/>
</epsos:generalizedMedicineClass>
</epsos:asSpecializedKind>
</manufacturedMaterial>
</manufacturedProduct>
</product>
<entryRelationship typeCode="REFR">
<sequenceNumber value="1"/>
<substanceAdministration classCode="SBADM" moodCode="INT">
<id extension="JE000233461" root="2.16.724.4.9.30.40"/>
<text>
<reference value="#medicine.1"/>
</text>
<statusCode code="completed"/>
<effectiveTime nullFlavor="UNK" xsi:type="IVL_TS"/>
<doseQuantity nullFlavor="UNK"/>
<consumable typeCode="CSM">
<manufacturedProduct classCode="MANU">
<templateId root="1.3.6.1.4.1.12559.11.10.1.3.1.3.1"/>
<templateId root="2.16.840.1.113883.10.20.1.53"/>
<manufacturedMaterial classCode="MMAT" determinerCode="KIND">
<code code="10219000" codeSystem="0.4.0.127.0.16.1.1.2.1" codeSystemName="EDQM" codeSystemVersion="2022-01-18" displayName="Tablet"/>
<name>PARACETAMOL NORMON 500 mg COMPRIMIDOS EFGPARACETAMOL NORMON 500 mg COMPRIMIDOS EFG</name>
<epsos:formCode xmlns=""/>
<epsos:asContent xmlns="urn:epsos-org:ep:medication" classCode="CONT">
<epsos:containerPackagedMedicine xmlns="" classCode="CONT" determinerCode="INSTANCE">
<epsos:name/>
<epsos:formCode/>
<epsos:capacityQuantity/>
<epsos:capTypeCode nullFlavor="NA"/>
</epsos:containerPackagedMedicine>
</epsos:asContent>
<epsos:asSpecializedKind xmlns="urn:epsos-org:ep:medication" classCode="GEN">
<epsos:generalizedMedicineClass classCode="MMAT">
<code code="N02BE01" codeSystem="2.16.840.1.113883.6.73" codeSystemName="Anatomical Therapeutic Chemical" codeSystemVersion="2022-01" displayName="paracetamol"/>
</epsos:generalizedMedicineClass>
</epsos:asSpecializedKind>
</manufacturedMaterial>
</manufacturedProduct>
</consumable>
</substanceAdministration>
</entryRelationship>

Extract values for more than one attribute node values via XPath expression

How can I extract value of more than one attribute node via XPath expression?
A sample XML file is:
<tag1>
<tag2>
<tag3 id="1">
<tage4>
<tage4code code="1">
<tag5>
<tage4Value Day="14" Month="Oct" Year="2000" />
</tag5>
<tag5>
<tage4Value Month="Oct" Year="2001" />
</tag5>
<tag5>
<tage4Value Year="2002" />
</tag5>
<tag5>
<tage4Value Day="1" Month="Jan" Year="1999" />
</tag5>
<tag5>
<tage4Value Year="1940" />
</tag5>
</tage4code>
</tage4>
</tag3>
</tag2>
</tag1>
So far I have this XPath string:
XPathExpression expr = xpath.compile("concat((/tag1/tag2/tag3[#id=1]/tage4/tage4code[#code=1]/tag5/tage4Value/#Day, '/' , /tag1/tag2/tag3[#id=1]/tage4/tage4code[#code=1]/tag5/tage4Value/#Month, '/', /tag1/tag2/tag3[#id=1]/tage4/tage4code[#code=1]/tag5/tage4Value/#Year)");
NodeList combination1 = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
for (int a = 0; a <= combination1.getLength(); a++) {
System.out.println("date : " + combination.item(a).getNodeValue());
}
My Expected result
14/Oct/2000
Oct/2001
2002
1/Jan/1999
1940
Can someone help to correct my XPathExpression
XPath 2.0 solution :
tokenize(replace(replace(replace(substring-after(string-join(//tag5/*|//tag5//#*,","),","),",,","%"),","," ")," ","/"),"%")
Output :
String='14/Oct/2000'
String='Oct/2001'
String='2002'
String='1/Jan/1999'
String='1940'
XPath 1.0 solution :
concat(translate(normalize-space(concat((//tage4Value)[1]/#Day," ",(//tage4Value)[1]/#Month," ",(//tage4Value)[1]/#Year))," ","/"),"|",translate(normalize-space(concat((//tage4Value)[2]/#Day," ",(//tage4Value)[2]/#Month," ",(//tage4Value)[2]/#Year))," ","/"),"|",translate(normalize-space(concat((//tage4Value)[3]/#Day," ",(//tage4Value)[3]/#Month," ",(//tage4Value)[3]/#Year))," ","/"),"|",translate(normalize-space(concat((//tage4Value)[4]/#Day," ",(//tage4Value)[4]/#Month," ",(//tage4Value)[4]/#Year))," ","/"),"|",translate(normalize-space(concat((//tage4Value)[5]/#Day," ",(//tage4Value)[5]/#Month," ",(//tage4Value)[5]/#Year))," ","/"))
Output :
String='14/Oct/2000|Oct/2001|2002|1/Jan/1999|1940'
Or with new line separator :
concat(translate(normalize-space(concat((//tage4Value)[1]/#Day," ",(//tage4Value)[1]/#Month," ",(//tage4Value)[1]/#Year))," ","/"),codepoints-to-string(10),translate(normalize-space(concat((//tage4Value)[2]/#Day," ",(//tage4Value)[2]/#Month," ",(//tage4Value)[2]/#Year))," ","/"),codepoints-to-string(10),translate(normalize-space(concat((//tage4Value)[3]/#Day," ",(//tage4Value)[3]/#Month," ",(//tage4Value)[3]/#Year))," ","/"),codepoints-to-string(10),translate(normalize-space(concat((//tage4Value)[4]/#Day," ",(//tage4Value)[4]/#Month," ",(//tage4Value)[4]/#Year))," ","/"),codepoints-to-string(10),translate(normalize-space(concat((//tage4Value)[5]/#Day," ",(//tage4Value)[5]/#Month," ",(//tage4Value)[5]/#Year))," ","/"))
Output :
String='14/Oct/2000
Oct/2001
2002
1/Jan/1999
1940'
This xpath expression
//tag3[#id="1"]//tage4code[#code=1]//tag5/tage4Value/concat(#Day,'/',#Month,'/',#Year)
should output
14/Oct/2000
/Oct/2001
//2002
1/Jan/1999
//1940
This way we can create a dynamic way of reading the child.
xpathExpression = "count(//tag1/tag2/tag3[#id=1]/tage4/tage4code[#code=1]/tag5/tage4Value)";
double nodeList1 = (double) xpath.compile(xpathExpression).evaluate(doc, XPathConstants.NUMBER);
int s = (int) (nodeList1);
for (int z = 1; z <= s; z++) {
xpathExpression = "normalize-space(concat((//tag1/tag2/tag3[#id=1]/tage4/tage4code[#code=1]/tag5/tage4Value)["
+ z
+ "]/#Day,\" \",(//tag1/tag2/tag3[#id=1]/tage4/tage4code[#code=1]/tag5/tage4Value)["
+ z
+ "]/#Month,\" \",(//tag1/tag2/tag3[#id=1]/tage4/tage4code[#code=1]/tag5/tage4Value)["
+ z + "]/#Year))";
String year = (String) xpath.evaluate(xpathExpression, doc, XPathConstants.STRING);
System.out.println(year);
}

Unable to run the XSLT through JAVA and Empty result observed in output

I just want to fetch a piece of tags from XML file and I'm using it XSLT.
XSLT:
<xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/><xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:strip-space elements="*"/>
<xsl:template match="/testng-results">
<xsl:copy-of select="class/test-method[#status='PASS']"/>
</xsl:template></xsl:stylesheet>
Input.XML
<?xml version="1.0" encoding="UTF-8"?>
<testng-results skipped="0" failed="0" total="10" passed="10">
<class name="com.transfermoney.Transfer">
<test-method status="PASS" name="setParameter" is-config="true" duration-ms="4"
started-at="2018-08-16T21:43:38Z" finished-at="2018-08-16T21:43:38Z">
<params>
<param index="0">
<value>
<![CDATA[org.testng.TestRunner#31c2affc]]>
</value>
</param>
</params>
<reporter-output>
</reporter-output>
</test-method> <!-- setParameter -->
</class>
<class name="com.transfermoney.Transfer">
<test-method status="FAIL" name="setSettlementFlag" is-config="true" duration-ms="5"
started-at="2018-08-16T21:44:55Z" finished-at="2018-08-16T21:44:55Z">
<reporter-output>
<line>
<![CDATA[runSettlement Value Set :false]]>
</line>
</reporter-output>
</test-method> setSettlementFlag
</class>
</testng-results>
JAVA Code:
public static void main(String[] args) throws Exception {
String XML = fetchDataFrmXML(".//Test//testng-results_2.xml");
Transformer t = TransformerFactory.newInstance().newTransformer(new StreamSource(new File(".//Test//Cut.xslt")));
t.transform(new StreamSource(new StringReader(XML)), new StreamResult(new File(".//Test//Sample1.xml")));
}
Expected Output:
<test-method status="PASS" name="setParameter" is-config="true" duration-ms="4" started-at="2018-08-16T21:43:38Z" finished-at="2018-08-16T21:43:38Z">
<params>
<param index="0">
<value>
<![CDATA[runSettlement Value Set :false]]>
</value>
</param>
</params>
<reporter-output/>
</test-method>
FetchXML:
public static String fetchDataFrmXML(String fileLocation) throws Exception
{
file = new File(fileLocation);
fr = new FileReader(file);
br = new BufferedReader(fr);
String temp;
String result = "";
while ((temp = br.readLine()) != null) {
result += temp;
}
br.close();
return result;
}
I'm getting the empty sample1.xml file after I ran the JAVA class file. But if the same XSLT script I just run it through online editor it's giving an expected result.
Is there any issue in my java file to execute the XSLT? please help me on this.
Your code works for me. The only things I changed were:
declaring the variables used in your fetchDataFromXML() method
Adding the missing space after version="1.0" in your stylesheet
Changing the file names.
I added the line
System.err.println(t.getClass().getName());
to identify the XSLT engine used; the output was
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl
You might like to do the same.
Looking more carefully at the output, it displays the record with status="PASS", which is what the code is selecting, though you said you wanted the one that has status="FAIL".
A note about your fetchDataFrmXML() method: it's incredibly inefficient to build up the content of a string by repeated string concatenation this way. Use a StringBuilder instead.
(I once earned myself $10K in consultancy fees by pointing this mistake out to a client, who probably saved themselves $1m in hardware costs as a result).

How to parse unconventional XML files

I have the need to take what I consider an "unconventional" XML file that I get in and convert it into what I consider "conventional". The below XML examples have been "scrubbed" of proprietary names/values.
Here is what the "unconventional" XML file looks like:
<?xml version="1.0" encoding="UTF-8"?>
<dataView name="document/aDocument" xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dataItems>
<dataItem name="item1" location="item1" dataItemType="Long">
<value xsi:type="xs:string">1</value>
</dataItem>
<dataItem name="item2" location="item.seconditem(some item).ref" dataItemType="String">
<value xsi:type="xs:string">ABC2DEF3</value>
</dataItem>
</dataItems>
<dataRepeaters>
<dataRepeater name="SomeLevel1" runtimeInfomration="SomeLevel11984371030">
<dataGroups>
<dataGroup name="SomeLevel2" location="SomeLevel:(a level).thelevel" keyValue="SomeLevel119843710300" runtimeInformation="19843710300">
<dataItems>
<dataItem name="someItem1" location="someLevel.aLevel(another item)" dataItemType="String">
<value xsi:type="xs:string">12345</value>
</dataItem>
<dataItem name="someItem2" location="someLevel.aLevel(another item2)" dataItemType="Integer"/>
<dataItem name="someItem3" location="someLevel.aLevel(another item3)" dataItemType="ObjectReference">
<value xsi:type="xs:string">11:20012:3536</value>
</dataItem>
<dataItem name="someItem4" location="someLevel.aLevel(another item4)" dataItemType="String"/>
<dataItem name="someItem5" location="someLevel.aLevel(another item5)" dataItemType="String">
<value xsi:type="xs:string">someItem5 Data</value>
</dataItem>
<dataItem name="someItem6" location="someLevel.aLevel(another item6)" dataItemType="CurrencyAmount">
<value xsi:type="xs:string">123456:USD</value>
</dataItem>
</dataItems>
</dataGroup>
<dataGroup name="SomeLevel3" keyValue="SomeLevel31984371030" runtimeInformation="1984371030">
<dataRepeaters>
<dataRepeater name="SomeLevel4" runtimeInfomration="SomeLevel4">
<dataGroups>
<dataGroup name="SomeLevel5" location="anotherLevel.level5(SomeLevel5):someLevel5" keyValue="SomeLevel51984371030-11521863690" runtimeInformation="1984371030-11521863690">
<dataItems>
<dataItem name="someItem6" location="someLevel.aLevel(another item6)" dataItemType="BigDecimal">
<value xsi:type="xs:string">303.149</value>
</dataItem>
<dataItem name="someItem7" location="someLevel.aLevel(another item7)" dataItemType="CurrencyAmount">
<value xsi:type="xs:string">117:USD</value>
</dataItem>
<dataItem name="someItem8" location="someLevel.aLevel(another item8)" dataItemType="String">
<value xsi:type="xs:string">117.000000:USD</value>
</dataItem>
<dataItem name="someItem9" location="someLevel.aLevel(another item9)" dataItemType="CurrencyAmount">
<value xsi:type="xs:string">117:USD</value>
</dataItem>
</dataItems>
<dataRepeaters>
<dataRepeater name="SomeLevel6" runtimeInfomration="someLevel6">
<dataGroups/>
</dataRepeater>
</dataRepeaters>
</dataGroup>
<dataGroup name="SomeLevel7" location="anotherLevel.level5(SomeLevel7):someLevel7" keyValue="SomeLevel71984371030-11521863690" runtimeInformation="1984371030-11521863690">
<dataItems>
<dataItem name="someItem10" location="someLevel.aLevel(another item10)" dataItemType="BigDecimal">
<value xsi:type="xs:string">292.741</value>
</dataItem>
<dataItem name="someItem11" location="someLevel.aLevel(another item11)" dataItemType="CurrencyAmount">
<value xsi:type="xs:string">165:USD</value>
</dataItem>
<dataItem name="someItem12" location="someLevel.aLevel(another item12)" dataItemType="String">
<value xsi:type="xs:string">165.000000:USD</value>
</dataItem>
<dataItem name="someItem13" location="someLevel.aLevel(another item13)" dataItemType="CurrencyAmount">
<value xsi:type="xs:string">165:USD</value>
</dataItem>
</dataItems>
<dataRepeaters/>
</dataGroup>
<dataGroup name="SomeLevel8" location="anotherLevel.level5(SomeLevel8):someLevel8" keyValue="SomeLevel81984371030-11521863690" runtimeInformation="1984371030-11521863690">
<dataItems>
<dataItem name="someItem14" location="someLevel.aLevel(another item14)" dataItemType="BigDecimal">
<value xsi:type="xs:string">369.075</value>
</dataItem>
<dataItem name="someItem15" location="someLevel.aLevel(another item15)" dataItemType="CurrencyAmount">
<value xsi:type="xs:string">598:USD</value>
</dataItem>
<dataItem name="someItem16" location="someLevel.aLevel(another item16)" dataItemType="String">
<value xsi:type="xs:string">598.000000:USD</value>
</dataItem>
</dataItems>
<dataRepeaters>
<dataRepeater name="SomeLevel9" runtimeInfomration="someLevel9">
<dataGroups>
<dataGroup name="SomeLevel10" location="ownedAuto.AgreementActual(Liability).ConstantRole(Policy Form):policyForm" keyValue="aomeLevel101984371030-11521863690-115218636900" runtimeInformation="1984371030-11521863690-115218636900">
<dataItems>
<dataItem name="someItem17" location="someLevel.aLevel(another item17)" dataItemType="String">
<value xsi:type="xs:string">Some Data | ABC 123</value>
</dataItem>
<dataItem name="someItem18" location="someLevel.aLevel(another item18)" dataItemType="Integer">
<value xsi:type="xs:string">2</value>
</dataItem>
</dataItems>
</dataGroup>
</dataGroups>
</dataRepeater>
<dataRepeater name="SomeLevel10" runtimeInfomration="someLevel11">
<dataGroups/>
</dataRepeater>
</dataRepeaters>
</dataGroup>
</dataGroups>
</dataRepeater>
</dataRepeaters>
</dataGroup>
</dataGroups>
</dataRepeater>
</dataRepeaters>
</dataView>
I am trying to convert it to something like this:
<?xml version="1.0" encoding="UTF-8"?>
<dataView name="document/aDocument" xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item1>1</item1>
<item2>ABC2DEF3</item2>
<SomeLevel1>
<SomeLevel2>
<someItem1>12345f</someItem1>
<someItem2></someItem2>
<someItem3>11:20012:3536</someItem3>
<someItem4>12345f</someItem4>
<someItem5>someItem5 Data</someItem5>
<someItem6>123456:USD</someItem6>
</SomeLevel2>
<SomeLevel3>
<SomeLevel4>
<SomeLevel5>
<someItem7>303.149</someItem7>
<someItem8>117:USD</someItem8>
<someItem9>117.000000:USD</someItem9>
<someItem10>117:USD</someItem10>
<SomeLevel6></SomeLevel6>
</SomeLevel5>
<SomeLevel7>
<someItem11>292.741</someItem11>
<someItem12>165:USD</someItem12>
<someItem13>165.000000:USD</someItem13>
<someItem14>165:USD</someItem14>
</SomeLevel7>
<SomeLevel8>
<someItem15>369.075</someItem15>
<someItem16>598:USD</someItem16>
<someItem17>598.000000:USD</someItem17>
<SomeLevel9>
<SomeLevel10>
<someItem18>Some Data | ABC 123</someItem18>
<someItem19>2</someItem19>
</SomeLevel10>
</SomeLevel9>
<SomeLevel11></SomeLevel11>
</SomeLevel8>
</SomeLevel4>
</SomeLevel3>
</SomeLevel1>
</dataView>
So basically I am trying to take the "name" attribute and make it the XML node/tag and the value between the <value> node/tag and use it as the "value" for the XML node/tag.
I have tried several different parsing/writing "routines" and the below two are the two that have gotten me the closest. So I'm leaving out the other ones as they would be just "noise".
Here is what I have tried so far:
private Document xmlDocument;
public void convertXML() {
xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader("DSExample.xml")));
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Node dsNode = xmlDocument.getDocumentElement();
xmlDocument = docBuilder.newDocument();
// Attempt 1:
doSomethingDS(dsNode); // This will convert the XML but with NO nesting - creates example output 1
// Attempt 2:
traverseDS(dsNode); // This will create an XML with ONLY the root dataItems - Don't know why - creates example output 2
}
public void doSomethingDS(Node node) {
System.out.println(node.getNodeName());
Element xmlElement = null;
// This will be the "root" element/node "dataView":
if(node.getNodeName() != null && node.getNodeName().equalsIgnoreCase("dataView")) {
// Debugging: Print the node
printNode(node);
String nodeName = node.getNodeName().trim();
rootElement = xmlDocument.createElement(nodeName);
mPreviousElement = rootElement;
addAllAttributesToRootElement((Element) node, rootElement);
xmlDocument.appendChild(rootElement);
}
else {
// Debugging: Print the node
printNode(node);
// If has "name" property - create element/node
if(node.getAttributes() != null && node.getAttributes().getNamedItem("name") != null) {
xmlElement = createElement(xmlDocument, node);
if(xmlElement !=null && xmlDocument.getFirstChild() != null) {
xmlDocument.getFirstChild().appendChild(xmlElement);
}
}
}
NodeList nodeList = node.getChildNodes();
for(int i = 0; i < nodeList.getLength(); i++) {
Node currentNode = nodeList.item(i);
if(currentNode.getNodeType() == Node.ELEMENT_NODE) {
// recursively call this method for all the children which are of type Element
doSomethingDS(currentNode);
}
}
} // End of doSomethingDS()
public void traverseDS(Node parentNode) {
// This will create an XML with ONLY the root dataItems - Don't know why.
Element xmlElement = null;
//****************************************************************
// This will be the "root" element/node "dataView":
if(parentNode.getNodeName() != null && parentNode.getNodeName().equalsIgnoreCase("dataView")) {
// Debugging: Print the node
printNode(parentNode);
String nodeName = parentNode.getNodeName().trim();
rootElement = xmlDocument.createElement(nodeName);
mPreviousElement = rootElement;
if(!isRootNodeSet) {
mRootNode = parentNode;
isRootNodeSet = true;
}
addAllAttributesToRootElement((Element) parentNode, rootElement);
xmlDocument.appendChild(rootElement);
// traverse children
Node theNode = parentNode.getFirstChild();
if(theNode != null) {
theNode = theNode.getNextSibling();
}
else if (isRootNodeSet) {
theNode = mRootNode.getNextSibling();
}
traverseDS(theNode);
}
else {
// traverse all nodes that belong to the parent
for(Node theNode = parentNode.getFirstChild(); theNode != null; theNode = theNode.getNextSibling()) {
// Debugging: Print the node
printNode(theNode);
// If has "name" property - create element/node
if(theNode.getAttributes() != null && theNode.getAttributes().getNamedItem("name") != null) {
// Create new Element/Node
xmlElement = createElement(xmlDocument, theNode);
if(xmlElement !=null && xmlDocument.getFirstChild() != null) {
xmlDocument.getFirstChild().appendChild(xmlElement);
}
else {
System.out.println(" not a node we wanted?");
}
}
// traverse children
traverseDS(theNode);
}
}
} // End of traverseDS()
private Element createElement(Document aDoc, Node aNode) {
Element xmlElement = null;
String elementName = "";
NamedNodeMap dataNodeMap = aNode.getAttributes();
if(dataNodeMap != null && dataNodeMap.getNamedItem("name") != null) {
elementName = dataNodeMap.getNamedItem("name").getTextContent();
xmlElement = aDoc.createElement(elementName);
// if node = "dataItem" - walk node to get <value> node
// Note: some "dataItem" nodes also have a <previousValue> node - what to do with these????
if(aNode.getNodeName() != null && aNode.getNodeName().equalsIgnoreCase("dataItem")) {
// Walk Node to get <value> node
NodeList childNodes = aNode.getChildNodes();
int childNodesLength = childNodes.getLength();
for(int x = 0; x < childNodesLength; x++) {
Node childNode = childNodes.item(x);
if(childNode.getNodeName() != null && childNode.getNodeName().equalsIgnoreCase("value")) {
xmlElement.setTextContent(childNode.getTextContent());
break;
}
}
}
}
return xmlElement;
} // End of createElement()
Example Output 1 (created by doSomethingDS()):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<dataView xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="document/aDocument">
<item1>1</item1>
<item2>ABC2DEF3</item2>
<SomeLevel1/>
<SomeLevel2/>
<someItem1>12345</someItem1>
<someItem2/>
<someItem3>11:20012:3536</someItem3>
<someItem4/>
<someItem5>someItem5 Data</someItem5>
<someItem6>123456:USD</someItem6>
<SomeLevel3/>
<SomeLevel4/>
<SomeLevel5/>
<someItem6>303.149</someItem6>
<someItem7>117:USD</someItem7>
<someItem8>117.000000:USD</someItem8>
<someItem9>117:USD</someItem9>
<SomeLevel6/>
<SomeLevel7/>
<someItem10>292.741</someItem10>
<someItem11>165:USD</someItem11>
<someItem12>165.000000:USD</someItem12>
<someItem13>165:USD</someItem13>
<SomeLevel8/>
<someItem14>369.075</someItem14>
<someItem15>598:USD</someItem15>
<someItem16>598.000000:USD</someItem16>
<SomeLevel9/>
<SomeLevel10/>
<someItem17>Some Data | ABC 123</someItem17>
<someItem18>2</someItem18>
<SomeLevel11/>
</dataView>
Example Output 2 (Created by traverseDS()):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<dataView xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="document/aDocument">
<item1>1</item1>
<item2>ABC2DEF3</item2>
</dataView>
Now here is that catch.... What I get is dynamic I could get a Mac truck one time and a Toyota the next or a mixture of the two. And the size of the XML I get could be small or large. So it's hard to just use XPath or directly go after specific elements as I don't know what I'll get. This is why I'm basically walking/traversing the DOM a node at a time to extract out just what is useable.
So my question is:
What am I doing wrong in my parsing/writing of the XML file to not get it to look like what I'm trying to get it to look like?
This is (almost) trivially easy with XSLT:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ds="http://component.mycompany.com/entity/ds"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://component.mycompany.com/entity/ds"
>
<xsl:output indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="ds:dataView" priority="1">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="ds:*[#name]">
<xsl:element name="{#name}">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space()" />
</xsl:template>
</xsl:stylesheet>
applied do your input, gives you:
<dataView xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item1>1</item1>
<item2>ABC2DEF3</item2>
<SomeLevel1>
<SomeLevel2>
<someItem1>12345</someItem1>
<someItem2 />
<someItem3>11:20012:3536</someItem3>
<someItem4 />
<someItem5>someItem5 Data</someItem5>
<someItem6>123456:USD</someItem6>
</SomeLevel2>
<SomeLevel3>
<SomeLevel4>
<SomeLevel5>
<someItem6>303.149</someItem6>
<someItem7>117:USD</someItem7>
<someItem8>117.000000:USD</someItem8>
<someItem9>117:USD</someItem9>
<SomeLevel6 />
</SomeLevel5>
<SomeLevel7>
<someItem10>292.741</someItem10>
<someItem11>165:USD</someItem11>
<someItem12>165.000000:USD</someItem12>
<someItem13>165:USD</someItem13>
</SomeLevel7>
<SomeLevel8>
<someItem14>369.075</someItem14>
<someItem15>598:USD</someItem15>
<someItem16>598.000000:USD</someItem16>
<SomeLevel9>
<SomeLevel10>
<someItem17>Some Data | ABC 123</someItem17>
<someItem18>2</someItem18>
</SomeLevel10>
</SomeLevel9>
<SomeLevel10 />
</SomeLevel8>
</SomeLevel4>
</SomeLevel3>
</SomeLevel1>
</dataView>
Notes:
Having numbered element names (someItem1 thru someItem6) will come back and bite you, eventually. XML elements have a natural order (or you can have a "number" attribute), putting a counter into the name is bad and wrong. Take my advice and don't do that (*).
To understand the solution here's a quick breakdown:
XSLT processors run XML nodes through templates by finding the best match for the XML node at hand.
The process starts with the root node.
Since there is no template explicitly matching the root node (<xsl:template match="/">), a default rule kicks in: Output nothing, but find templates for the child nodes.
The first (and only) child node is the document element (<ds:dataView>).
The template #1 matches that node, tells the processor to copy it and process its children (<xsl:apply-templates />).
Every element that has a #name is matched by template #2, which outputs an element with that name and again processes its children.
Every other element (like those <value> nodes) is - in the absence of any matching template - processed according to the mentioned default rule.
Text nodes are matched by template #3, which outputs their trimmed value.
Additional notes:
The expression in curly braces is called an attribute value template.
The template priority="1" makes sure that this template is selected for the <ds:dataView> element, because the other one would match as well.
Your result document still is in the "http://component.mycompany.com/entity/ds". This might or might not be correct. I suspect it's not correct, technically speaking, but you decide.
(*) <xsl:element name="{translate(#name, '0123456789', '')}" would be one variant to lose the counters.
XSLT has been created exactly for a task like that. As long as your Source-XML (unconventional xml) is well-formed (correct xml-syntax) so that it can be read by a standard xml-parser like DOM, you can use that.
It works like so: You create a xsl-template file that contains a declerative description how the source xml should be transformed into another format. The transformation itself is done by a XSL-Transformer.
The xsl is also an xml-format, so it should be quit easy readable, as long as the transformation is not too complex.
Here is explained how that can be implemented:
http://docs.oracle.com/javase/tutorial/jaxp/xslt/transformingXML.html

How to read xml file in java

How to read XML file in java.
Below is my XML file:
<?xml version="1.0" encoding="utf-8"?>
<LivescoreData>
<Sport SportId="1">
<Name language="en">Soccer</Name>
<Name language="se">Fotboll</Name>
<Category CategoryId="34">
<Name language="en">Australia</Name>
<Name language="se">Australien</Name>
<Tournament TournamentId="144">
<Name language="en">Hyundai A-League</Name>
<Name language="se">Hyundai A-League</Name>
<Match MatchId="4616735">
<MatchDate>2011-01-05T07:30:00</MatchDate>
<Team1 TeamId="1029369">
<Name language="en">Wellington Phoenix FC</Name>
<Name language="se">Wellington</Name>
</Team1>
<Team2 TeamId="529088">
<Name language="en">Melbourne Victory</Name>
<Name language="se">Melbourne Victory</Name>
</Team2>
<Status Code="100">
<Name language="en">Ended</Name>
<Name language="se">Avslutad</Name>
</Status>
<Winner>1</Winner>
<Scores>
<Score type="Current">
<Team1>2</Team1>
<Team2>0</Team2>
</Score>
</Scores>
<Goals></Goals>
<Cards></Cards>
<Substitutions></Substitutions>
<Lineups></Lineups>
</Match>
</Tournament>
</Category>
<Category CategoryId="1">
<Name language="en">England</Name>
<Name language="se">England</Name>
<Tournament TournamentId="1">
<Name language="en">Premier League</Name>
<Name language="se">Premier League</Name>
<Match MatchId="4601857">
<MatchDate>2011-01-04T21:00:00</MatchDate>
<Team1 TeamId="5431228">
<Name language="en">Blackpool FC</Name>
<Name language="se">Blackpool FC</Name>
</Team1>
<Team2 TeamId="23960">
<Name language="en">Birmingham City</Name>
<Name language="se">Birmingham City</Name>
</Team2>
<Status Code="100">
<Name language="en">Ended</Name>
<Name language="se">Avslutad</Name>
</Status>
<Winner>1</Winner>
<Scores>
<Score type="Current">
<Team1>5</Team1>
<Team2>1</Team2>
</Score>
</Scores>
<Goals></Goals>
<Cards></Cards>
<Substitutions></Substitutions>
<Lineups></Lineups>
</Match>
<Match MatchId="4601859">
<MatchDate>2011-01-04T21:00:00</MatchDate>
<Team1 TeamId="26511">
<Name language="en">Fulham FC</Name>
<Name language="se">Fulham FC</Name>
</Team1>
<Team2 TeamId="94356">
<Name language="en">West Bromwich Albion</Name>
<Name language="se">West Bromwich Albion</Name>
</Team2>
<Status Code="100">
<Name language="en">Ended</Name>
<Name language="se">Avslutad</Name>
</Status>
<Winner>1</Winner>
<Scores>
<Score type="Current">
<Team1>4</Team1>
<Team2>1</Team2>
</Score>
</Scores>
<Goals></Goals>
<Cards></Cards>
<Substitutions></Substitutions>
<Lineups></Lineups>
</Match>
</Tournament>
</Category>
</Sport>
</LivescoreData>
Below is the code:IT print the first value of XML Soccer and not able to print the next one.
nodeLst = doc.getElementsByTagName("Sport");
for (int i = 0; i < nodeLst.getLength(); i++) {
Node myNode = nodeLst.item(i);
if (myNode.getNodeType() == Node.ELEMENT_NODE) {
Element Sport = (Element) myNode;
NodeList Name= Sport
.getElementsByTagName("Name");
Element NameElement = (Element) Name.item(0);
NodeList Namevalue = NameElement.getChildNodes();
System.out.println("Name : "
+ ((Node) Namevalue.item(0)).getNodeValue()+"|");
//This gives me null value
NodeList Category = Sport
.getElementsByTagName("Category");
Element CategoryName= (Element) Category
.item(0);
NodeList Categoryvalue = CategoryName
.getChildNodes();
System.out.println("Category: "
+ ((Node) Categoryvalue.item(0))
.getNodeValue());
}
}
Am able to read only the first data Soccer.The second one just give null value.
I need my result as:
Soccer | Australia | Hyundai A-League | Wellington Phoenix FC - Melbourne Victory : 2 - 0
Soccer | England | Premier League | Blackpool FC - Birmingham City : 5 - 1
Soccer | England | Premier League | Fulham FC - West Bromwich Albion : 4 - 1
You could use JAXP for parsing the XML
Since you haven't really said what you want to do with said XML file, the best I can do is direct you to this guide:
http://tutorials.jenkov.com/java-xml/dom.html
It's much better/easier NOT to do this in Java, but to do it in XSLT or XQuery code which you can invoke from your Java application.

Categories

Resources