Split unsymmetrical XML into multiple XMLs - java

I have to split an unsymmetrical xml on 'Trade' element using java
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<BookingRequest>
<Id>1234</Id>
<App>Pan</App>
<Parties>
<Party>1</Party>
<Party>2</Party>
</Parties>
<Contract>
<Trades>
<Trade>
<Detail>100</Detail>
<Location>
<Num>10001</Num>
</Location>
</Trade>
<Trade>
<Detail>101</Detail>
<Location>
<Num>10002</Num>
</Location>
</Trade>
</Trades>
</Contract>
</BookingRequest>
My requirement is post split there should be 2 files:
FILE 1:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<BookingRequest>
<Id>1234</Id>
<App>Pan</App>
<Parties>
<Party>1</Party>
<Party>2</Party>
</Parties>
<Contract>
<Trades>
<Trade>
<Detail>100</Detail>
<Location>
<Num>10001</Num>
</Location>
</Trade>
</Trades>
</Contract>
</BookingRequest>
FILE 2:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<BookingRequest>
<Id>1234</Id>
<App>Pan</App>
<Parties>
<Party>1</Party>
<Party>2</Party>
</Parties>
<Contract>
<Trades>
<Trade>
<Detail>101</Detail>
<Location>
<Num>10002</Num>
</Location>
</Trade>
</Trades>
</Contract>
</BookingRequest>
Could someone help me in doing this kind of splitting (preferably using DOM/XPath and NOT XSLT)

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc = factory.newDocumentBuilder().parse("booking-request.xml");
Element bookingRequest = doc.getDocumentElement();
Element contract = (Element)bookingRequest.getElementsByTagName("Contract").item(0);
Element trades = (Element)contract.getElementsByTagName("Trades").item(0);
List<Element> tradeList = new ArrayList<Element>();
NodeList nodeList = trades.getElementsByTagName("Trade");
for(int i=0; i<nodeList.getLength(); i++)
tradeList.add((Element)nodeList.item(i));
Transformer transformer = TransformerFactory.newInstance().newTransformer();
int i = 0;
for(Element trade: tradeList){
// remove all children of <Trades>
while(trades.getFirstChild()!=null)
trades.removeChild(trades.getFirstChild());
trades.appendChild(doc.createTextNode("\n "));
trades.appendChild(trade);
trades.appendChild(doc.createTextNode("\n "));
++i;
transformer.transform(new DOMSource(doc), new StreamResult(new File("trade"+i+".xml")));
}

Related

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

Add new element to xml file with Java DOM

I have been working with a simple cd catalog XML file:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
</CATALOG>
With this method I want to create a new CD element:
private static void addCd(File xml) throws ParserConfigurationException, TransformerConfigurationException, TransformerException, SAXException, IOException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(xml);
Element cd = document.createElement("CD");
document.appendChild(cd);
for (int i = 1; i <= 3; i++) {
Element title = document.createElement("TITLE");
Element artist = document.createElement("ARTIST");
Element country = document.createElement("COUNTRY");
Element company = document.createElement("COMPANY");
Element price = document.createElement("PRICE");
Element year = document.createElement("YEAR");
title.appendChild(document.createTextNode("mike "));
artist.appendChild(document.createTextNode("oconnor "));
country.appendChild(document.createTextNode("ie "));
company.appendChild(document.createTextNode("dell "));
price.appendChild(document.createTextNode("14 "));
year.appendChild(document.createTextNode("2014 "));
cd.appendChild(title);
cd.appendChild(artist);
cd.appendChild(country);
cd.appendChild(company);
cd.appendChild(price);
cd.appendChild(year);
}
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
DOMSource domSource = new DOMSource(document);
StreamResult streamResult = new StreamResult(new File("cdCatalog.xml"));
transformer.transform(domSource, streamResult);
DOMSource source = new DOMSource(document);
}
I can create a new CD element, but the new element appears outside my root (<CATALOG>) element:
<?xml version="1.0" encoding="utf-8" standalone="no"?><CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
</CATALOG><CD><TITLE>mike </TITLE><ARTIST>oconnor </ARTIST><COUNTRY>ie </COUNTRY><COMPANY>dell </COMPANY><PRICE>14 </PRICE><YEAR>2014 </YEAR><TITLE>mike </TITLE><ARTIST>oconnor </ARTIST><COUNTRY>ie </COUNTRY><COMPANY>dell </COMPANY><PRICE>14 </PRICE><YEAR>2014 </YEAR><TITLE>mike </TITLE><ARTIST>oconnor </ARTIST><COUNTRY>ie </COUNTRY><COMPANY>dell </COMPANY><PRICE>14 </PRICE><YEAR>2014 </YEAR></CD>
I think I appended the wrong element, but have no idea how to append to CATALOG.
The problem is, you were append element by document, YOU needs to append CD by root element
Try like this:
...
Document document = db.parse(xml);
Element root = document.getDocumentElement();
Element cd = document.createElement("CD");
root.appendChild(cd);
.....
......
....
You should use the getElementsByTagName to get the first catalog tag and append your cd object to it, not to the document like you're doing.
You can use XML DOM editing features:
Java DOM

How to ignore namespaces when selecting XML nodes with XPath

I saw that there is an option to ignore namespace
xpathUtil.getObjectValue("//*[local-name() = 'object name']")
is it possible to add more objetcs names
for example
<Schema xmlns:m...... Namespace="app"
xmlns:d = ....
<complexType Name = "Comp>
<EntityType Name = "a">
<EntityType Name = "b">
</Schema>
<Schema xmlns:m...... Namespace="app2"
xmlns:d = ....
<complexType Name = "Comp2>
<EntityType Name = "a2">
<EntityType Name = "b2">
</Schema>
is it possible to get the data of the attribute EntityType only for Schema= app ? ( meaning to get the result of a and b and not a2 and b2 )
Assuming that a complete version of your input file might look like this
<?xml version="1.0" encoding="ISO-8859-1"?>
<Schemas>
<ns1:Schema xmlns:ns1="app1" >
<ns1:complexType Name = "Comp1">
<ns1:EntityType Name = "a1"/>
<ns1:EntityType Name = "b1"/>
</ns1:complexType>
</ns1:Schema>
<ns2:Schema xmlns:ns2="app2">
<ns2:complexType Name = "Comp2">
<ns2:EntityType Name = "a2"/>
<ns2:EntityType Name = "b2"/>
</ns2:complexType>
</ns2:Schema>
</Schemas>
the following XSLT shows how to filter the tags by the URI of the namespaceand the name of the namespace.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:variable name="ns_uri1" select="'app1'"/>
<xsl:variable name="ns_uri2" select="'app2'"/>
<xsl:variable name="ns_name1" select="'ns1'"/>
<xsl:variable name="ns_name2" select="'ns2'"/>
<xsl:template match="/Schemas">
<apps>
<app1_selected_by_namespace_uri>
<xsl:copy-of select="*[local-name(.) = 'Schema' and namespace-uri(.)=$ns_uri1]"/>
</app1_selected_by_namespace_uri>
<app2_selected_by_namespace_uri>
<xsl:copy-of select="*[local-name(.) = 'Schema' and namespace-uri(.)=$ns_uri2]"/>
</app2_selected_by_namespace_uri>
<app1_selected_by_namespace_name>
<xsl:copy-of select="*[name(.) = concat($ns_name1, ':Schema')]"/>
</app1_selected_by_namespace_name>
<app2_selected_by_namespace_name>
<xsl:copy-of select="*[name(.) = concat($ns_name2, ':Schema')]"/>
</app2_selected_by_namespace_name>
</apps>
</xsl:template>
</xsl:stylesheet>
yielding the following output document
<?xml version="1.0" encoding="UTF-8"?>
<apps>
<app1_selected_by_namespace_uri>
<ns1:Schema xmlns:ns1="app1">
<ns1:complexType Name="Comp1">
<ns1:EntityType Name="a1"/>
<ns1:EntityType Name="b1"/>
</ns1:complexType>
</ns1:Schema>
</app1_selected_by_namespace_uri>
<app2_selected_by_namespace_uri>
<ns2:Schema xmlns:ns2="app2">
<ns2:complexType Name="Comp2">
<ns2:EntityType Name="a2"/>
<ns2:EntityType Name="b2"/>
</ns2:complexType>
</ns2:Schema>
</app2_selected_by_namespace_uri>
<app1_selected_by_namespace_name>
<ns1:Schema xmlns:ns1="app1">
<ns1:complexType Name="Comp1">
<ns1:EntityType Name="a1"/>
<ns1:EntityType Name="b1"/>
</ns1:complexType>
</ns1:Schema>
</app1_selected_by_namespace_name>
<app2_selected_by_namespace_name>
<ns2:Schema xmlns:ns2="app2">
<ns2:complexType Name="Comp2">
<ns2:EntityType Name="a2"/>
<ns2:EntityType Name="b2"/>
</ns2:complexType>
</ns2:Schema>
</app2_selected_by_namespace_name>
</apps>
In both cases the names are not hardcoded but given by parameters. I guess that one of the versions will suit your needs. You will find the XPath expression in the select attributes of the copy-of tags.

Cutting a XML with JAXB

I have the following xml:
<Package>
<PackageHeader>
<name>External Vendor File</name>
<description>External vendor file for some purpose</description>
<version>3.141694baR3</version>
</PackageHeader>
<PackageBody>
<Characteristic id="1">
<Size>
<value>1.68</value>
<scale>Meters</scale>
<comment>Size can vary, depending on temperature</comment>
</Size>
<Weight>
<value>9</value>
<scale>M*Tons</scale>
<comment>His mama is so fat, we had to use another scale</comment>
</Weight>
<rating>
<ratingCompany>ISO</ratingCompany>
<rating:details xmlns:rating="http://www.w3schools.com/ratingDetails">
<rating:value companyDepartment="Finance">A</rating:value>
<rating:expirationDate update="1/12/2010">1/1/2014</rating:expirationDate>
<rating:comment userID="z94234">You're not Silvia.</rating:comment>
<rating:comment userID="r24942">You're one of the Kung-Fu Creatures On The Rampage</rating:comment>
<rating:comment userID="i77880">TWO!</rating:comment>
<rating:priority>3</rating:priority>
</rating:details>
</rating>
</Characteristic>
<Characteristic id="2">
<Size/>
<Weight/>
<rating/>
</Characteristic>
...
<Characteristic id="n"/>
</PackageBody>
</Package>
And the following Java code:
public class XMLTest {
public static void main(String[] args) throws Exception {
Package currentPackage = new Package();
Package sourcePackage = new Package();
int totalCharacteristics;
PackageBody currentPackageBody = new PackageBody();
Characteristic currentCharacteristic = new Characteristic();
rating currentRating = new rating();
FileInputStream fis = new FileInputStream("sourceFile.xml");
JAXBContext myCurrentContext = JAXBContext.newInstance(Package.class);
Marshaller m = myCurrentContext.createMarshaller();
Unmarshaller um = myCurrentContext.createUnmarshaller();
sourcePackage = (Package)um.unmarshal(fis);
currentPackage.setPackageHeader(sourcePackage.getPackageHeader());
totalCharacteristics = sourcePackage.getPackageBody().getCharacteristics().size();
for (int i = 0; i < totalCharacteristics; i++)
{
currentRating = sourcePackage.getPackageBody().getCharacteristics().get(i).getrating();
}
currentCharacteristic.setrating(currentRating);
currentPackageBody.getCharacteristics().add(currentCharacteristic);
currentPackage.setPackageBody(currentPackageBody);
m.marshal(currentPackage, new File("targetFile.xml"));
fis.close();
}
}
Which gives me the next XML:
<Package>
<PackageHeader>
<name>External Vendor File</name>
<description>External vendor file for some purpose</description>
<version>3.141694baR3</version>
</PackageHeader>
<PackageBody>
<Characteristic id="1">
<rating>
<ratingCompany>ISO</ratingCompany>
<rating:details xmlns:rating="http://www.w3schools.com/ratingDetails">
<rating:value companyDepartment="Finance">A</rating:value>
<rating:expirationDate update="1/12/2010">1/1/2014</rating:expirationDate>
<rating:comment userID="z94234">You're not Silvia.</rating:comment>
<rating:comment userID="r24942">You're one of the Kung-Fu Creatures On The Rampage</rating:comment>
<rating:comment userID="i77880">TWO!</rating:comment>
<rating:priority>3</rating:priority>
</rating:details>
</rating>
</Characteristic>
<Characteristic id="2">
<rating/>
</Characteristic>
...
<Characteristic id="n"/>
</PackageBody>
</Package>
And this is what I need:
<Package>
<PackageHeader>
<name>External Vendor File</name>
<description>External vendor file for some purpose</description>
<version>3.141694baR3</version>
</PackageHeader>
<PackageBody>
<Characteristic>
<rating id="1">
<ratingCompany>ISO</ratingCompany>
<rating:details xmlns:rating="http://www.w3schools.com/ratingDetails">
<rating:comment userID="z94234">You're not Silvia.</rating:comment>
<rating:comment userID="r24942">You're one of the Kung-Fu Creatures On The Rampage</rating:comment>
<rating:comment userID="i77880">TWO!</rating:comment>
<rating:priority>3</rating:priority>
</rating:details>
</rating>
</Characteristic>
<Characteristic>
<rating id="2"/>
</Characteristic>
...
<Characteristic/>
</PackageBody>
</Package>
But I have a few questions:
How could I implement a way to read a 4GBs file? (for example, reading it with StAX).
If I want to filter some tags from source to target(as in the last xml), would I have to assign them one by one to the targetFile? Is there any iterator that might allow me to go through all subnodes and assign them?
If the sourceFile changes, would I need to rerun the xjc and recompile the whole project?
Thanks.
For reading huge XML files, you definitely need a streaming parser like StAX. In addition, you can use a combination of JAXB to selectively map a given piece of xml to java object if you wish work with it. You need to regenerate your JAXB classes only if your schema changes. No need to regenerate if you application code changes.

Categories

Resources