I have an issue when I append a new node to xml dom. The following code is the dom saving code in xmlFactory.java.
import java.io.File;
import java.io.StringWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import com.ism.msgmgt.domain.Post;
import com.ism.msgmgt.domain.User;
public class XmlFactory {
private static Document userdom;
private static Document postdom;
private static File userfile = new File(User.class.getResource("User.xml").getPath());
static {
try{
DocumentBuilderFactory dbuf = DocumentBuilderFactory.newInstance();
DocumentBuilder dbu = dbuf.newDocumentBuilder();
userdom = dbu.parse(userfile);
DocumentBuilderFactory dbpf = DocumentBuilderFactory.newInstance();
DocumentBuilder dbp = dbpf.newDocumentBuilder();
postdom = dbp.parse(postfile);
} catch (Exception e){
e.printStackTrace();
}
}
public static Document getUserDom(){
return userdom;
}
public static void saveUserDom(){
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
System.out.println(userdom.getFirstChild()+"|"+userdom.getFirstChild().getFirstChild());
DOMSource ds = new DOMSource(userdom);
StreamResult sr = new StreamResult(userfile);
trans.transform(ds, sr);
} catch (Exception e){
throw new RuntimeException(e.getMessage(),e);
}
}
}
Note that the printed output is [users: null]|[#text: ]. Next, it is the code in the UserDao.java.
public class UserDao {
public static Element currentUser = null;
public static Document userdom = XmlFactory.getUserDom();
public static void reg(User u){
clrLogin();
u.setUserid(UidUtil.getUid());
u.setPassword(PwdUtil.encode(u.getPassword()));
Element e = userdom.createElement("user");
e.setAttribute("id", u.getUserid());
e.setAttribute("username", u.getUsername());
e.setAttribute("password", u.getPassword());
e.setAttribute("login", u.getLogin());
userdom.getFirstChild().appendChild(e);
System.out.println(e);
System.out.println(userdom.getFirstChild()+"|"+userdom.getFirstChild().getFirstChild());
XmlFactory.saveUserDom();
currentUser = e;
}
}
Note that in this code, I printed the node I want to add, which is [user: null]. Next, I printed the expecting added node. However, I got the result [users: null]|[#text: ], which is the same as the output printed from above code. So it looks like appendChild() didn't add e to the dom's first node. Please help. Thanks.
XML File
<?xml version="1.0" encoding="UTF-8"?>
<users>
</users>
There's too much context missing from your question, for one, we don't know how the userdom is actually created...
Let me demonstrate, with the following code...
import java.io.ByteArrayOutputStream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class Test1 {
public static Document userdom;
public static void main(String[] args) {
try {
userdom = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = userdom.createElement("users");
Node adoptNode = userdom.adoptNode(root);
userdom.appendChild(adoptNode);
reg();
} catch (ParserConfigurationException | DOMException exp) {
exp.printStackTrace();
}
}
public static void reg() {
Element e = userdom.createElement("user");
e.setAttribute("id", "blah");
e.setAttribute("username", "kermit");
e.setAttribute("password", "bunnies in the air");
e.setAttribute("login", "kermmi");
userdom.getFirstChild().appendChild(e);
System.out.println(e);
System.out.println(userdom.getFirstChild() + "|" + userdom.getFirstChild().getFirstChild());
saveUserDom();
}
public static void saveUserDom() {
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
System.out.println(userdom.getFirstChild() + "|" + userdom.getFirstChild().getFirstChild());
DOMSource ds = new DOMSource(userdom);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
StreamResult sr = new StreamResult(baos);
trans.transform(ds, sr);
System.out.println(new String(baos.toByteArray()));
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
I get the following output...
[user: null]
[users: null]|[user: null]
[users: null]|[user: null]
<?xml version="1.0" encoding="UTF-8" standalone="no"?><users><user id="blah" login="kermmi" password="bunnies in the air" username="kermit"/></users>
Which clearly demonstrates that the appendChild method is working fine.
An immediate concern is the relationship between...
public static Document userdom = XmlFactory.getUserDom();
and
DOMSource ds = new DOMSource(userdom);
One has to ask, are they the same reference (of userdom)?
Updated
This...
private static File userfile = new File(User.class.getResource("User.xml").getPath());
coupled with this...
StreamResult sr = new StreamResult(userfile);
Are your problems...
First, you are loading an internal resource and you should NEVER wrap these in File entry, internal/embedded resources are not accessible from a File context and need to be loaded in a different way, instead, maitain the URL refernce that getResource results...
private static URL userfile = User.class.getResource("User.xml");
The next line, (StreamResult sr = new StreamResult(userfile);) is trying to write the resulting DOM back to an embedded resource...embedded resources can not be written to in this fashion, hence the reason it would fail in a normal running environment (it might work in your IDE, but that's a different issue).
Basically, you can't maintain this information as an embedded resource, you need to externalise the file onto the disk.
FYI:
If you did use the URL of the resource properly, you would need to change the way you are loading the XML document to something like...
try (InputStream is = userfile.openStream()) {
userdom = dbu.parse(is);
}
Related
I have a folder("Container") inside this i have some .txt files which actually contains XML. All files have a common element <Number> which i want to increment by 1 for all the XML files.
any help.
i have done the change for a single .txt file, which is okay:-
package ParsingXML;
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
public class ReadAndModifyXMLFile {
public static final String xmlFilePath = "C:\\Users\\PSINGH17\\Desktop\\testFile.txt";
public static void main(String argv[]) {
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(xmlFilePath);
// Get employee by tag name
//use item(0) to get the first node with tag name "employee"
Node employee = document.getElementsByTagName("employee").item(0);
// update employee , increment the number
NamedNodeMap attribute = employee.getAttributes();
Node nodeAttr = attribute.getNamedItem("number");
String str= nodeAttr.getNodeValue();
System.out.println(str);
int val= Integer.parseInt(str);
System.out.println(val);
val=val+1;
System.out.println(val);
String final_value= String.valueOf(val);
System.out.println(final_value);
nodeAttr.setTextContent(final_value);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
StreamResult streamResult = new StreamResult(new File(xmlFilePath));
transformer.transform(domSource, streamResult);
} catch (ParserConfigurationException pce) {
pce.printStackTrace();
} catch (TransformerException tfe) {
tfe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (SAXException sae) {
sae.printStackTrace();
}
}
}
xml file:-
<?xml version="1.0" encoding="UTF-8" standalone="no"?><company>
<employee number="14">
<firstname>Pranav</firstname>
<lastname>Singh</lastname>
<email>pranav#example.org</email>
<salary>1000</salary>
</employee>
</company>
So this code uses the initial value digits after hash to start the counter.
Then it iterates through all the files in the folder.
The first file will get the initial value, the rest will get incremented values.
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
public class TestFile {
public static final String xmlFileFolder = "C:\\Rahul\\test";
private static final String initialValue = "450-000-1212";
public static void main(String argv[]) {
int baseValue = Integer.parseInt(getValueAfterLastDash(initialValue));
System.out.println("startValue " + baseValue);
File folder = new File(xmlFileFolder);
for (File file : folder.listFiles()) {
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(file);
Node employee = document.getElementsByTagName("employee").item(0);
NamedNodeMap attribute = employee.getAttributes();
Node nodeAttr = attribute.getNamedItem("number");
System.out.println(getValueBeforeLastDash(initialValue) + baseValue);
nodeAttr.setTextContent(getValueBeforeLastDash(initialValue) + baseValue);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
StreamResult streamResult = new StreamResult(file);
transformer.transform(domSource, streamResult);
baseValue++;
} catch (Exception ignored) {
}
}
}
private static String getValueAfterLastDash(String initialValue) {
return initialValue.substring(initialValue.lastIndexOf('-') + 1, initialValue.length());
}
private static String getValueBeforeLastDash(String initialValue) {
return initialValue.substring(0, initialValue.lastIndexOf('-') + 1);
}
}
It's my first intention to create XML file for android using TransformerFactory and I have these errors:
W/﹕ Unable to open '/system/framework/WfdCommon.jar': No such file or directory
W/art﹕ Failed to open zip archive '/system/framework/WfdCommon.jar': I/O Error
I'm using this class:
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
public class CreateXMLFile {
public static void save(String fName) {
try {
DocumentBuilderFactory dbFactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder =
dbFactory.newDocumentBuilder();
Document document = dBuilder.newDocument();
// adding root element to document
Element rootElement = document.createElement("rootelement");
document.appendChild(rootElement);
// write the content into xml file
TransformerFactory transformerFactory =
TransformerFactory.newInstance();
Transformer transformer =
transformerFactory.newTransformer();
DOMSource source = new DOMSource(document);
File file = new File(fName);
// Output to file
StreamResult result = new StreamResult(file);
transformer.transform(source, result);
// Output to console for testing:
// StreamResult consoleResult = new StreamResult(System.out);
// transformer.transform(source, consoleResult);
} catch (Exception e) {
e.printStackTrace();
}
}
}
I call .save method like this:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
.......
CreateXMLFile.save("test.xml");
}
....
}
Testing to consol works well, but when I'm trying to write the content to a file, it throws Exeption.
I was trying to get information about WfdCommon.jar, but could not find any. I have no idea where to dig. Can anyone help me, please?
I am new to JAVA programming, now I in need of JAVA program to read a big XML file that containing .. tags. Sample input as follows.
Input.xml
<row>
<Name>Filename1</Name>
</row>
<row>
<Name>Filename2</Name>
</row>
<row>
<Name>Filename3</Name>
</row>
<row>
<Name>Filename4</Name>
</row>
<row>
<Name>Filename5</Name>
</row>
<row>
<Name>Filename6</Name>
</row>
.
.
I need output as first <row> </row> as a single .xml file with filename as filename1.xml
and second <row>..</row> as filename2.xml and so.
Can anyone tell the steps how to do it in simple way with Java, it will be very useful if you give any sample codes ?
You could do the following with StAX because you said your xml is large
Code for Your Use Case
The following code uses StAX APIs to break up the document as outlined in your question:
import java.io.*;
import java.util.*;
import javax.xml.namespace.QName;
import javax.xml.stream.*;
import javax.xml.stream.events.*;
public class Demo {
public static void main(String[] args) throws Exception {
Demo demo = new Demo();
demo.split("src/forum7408938/input.xml", "nickname");
//demo.split("src/forum7408938/input.xml", null);
}
private void split(String xmlResource, String condition) throws Exception {
XMLEventFactory xef = XMLEventFactory.newFactory();
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLEventReader xer = xif.createXMLEventReader(new FileReader(xmlResource));
StartElement rootStartElement = xer.nextTag().asStartElement(); // Advance to statements element
StartDocument startDocument = xef.createStartDocument();
EndDocument endDocument = xef.createEndDocument();
XMLOutputFactory xof = XMLOutputFactory.newFactory();
while(xer.hasNext() && !xer.peek().isEndDocument()) {
boolean metCondition;
XMLEvent xmlEvent = xer.nextTag();
if(!xmlEvent.isStartElement()) {
break;
}
// Be able to split XML file into n parts with x split elements(from
// the dummy XML example staff is the split element).
StartElement breakStartElement = xmlEvent.asStartElement();
List<XMLEvent> cachedXMLEvents = new ArrayList<XMLEvent>();
// BOUNTY CRITERIA
// I'd like to be able to specify condition that must be in the
// split element i.e. I want only staff which have nickname, I want
// to discard those without nicknames. But be able to also split
// without conditions while running split without conditions.
if(null == condition) {
cachedXMLEvents.add(breakStartElement);
metCondition = true;
} else {
cachedXMLEvents.add(breakStartElement);
xmlEvent = xer.nextEvent();
metCondition = false;
while(!(xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().equals(breakStartElement.getName()))) {
cachedXMLEvents.add(xmlEvent);
if(xmlEvent.isStartElement() && xmlEvent.asStartElement().getName().getLocalPart().equals(condition)) {
metCondition = true;
break;
}
xmlEvent = xer.nextEvent();
}
}
if(metCondition) {
// Create a file for the fragment, the name is derived from the value of the id attribute
FileWriter fileWriter = null;
fileWriter = new FileWriter("src/forum7408938/" + breakStartElement.getAttributeByName(new QName("id")).getValue() + ".xml");
// A StAX XMLEventWriter will be used to write the XML fragment
XMLEventWriter xew = xof.createXMLEventWriter(fileWriter);
xew.add(startDocument);
// BOUNTY CRITERIA
// The content of the spitted files should be wrapped in the
// root element from the original file(like in the dummy example
// company)
xew.add(rootStartElement);
// Write the XMLEvents that were cached while when we were
// checking the fragment to see if it matched our criteria.
for(XMLEvent cachedEvent : cachedXMLEvents) {
xew.add(cachedEvent);
}
// Write the XMLEvents that we still need to parse from this
// fragment
xmlEvent = xer.nextEvent();
while(xer.hasNext() && !(xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().equals(breakStartElement.getName()))) {
xew.add(xmlEvent);
xmlEvent = xer.nextEvent();
}
xew.add(xmlEvent);
// Close everything we opened
xew.add(xef.createEndElement(rootStartElement.getName(), null));
xew.add(endDocument);
fileWriter.close();
}
}
}
}
I can suggest using SAXParser and extending the DefaultHandler class' methods.
You can use a few booleans to keep a track of which tag you are in.
DefaultHandler will let you know when you are in a particular tag by the startElement() method. Then, you will be given the contents of the tag by the characters() method and finally you will be notified of the end of a tag by the endElement() method.
As soon as you are notified of the end of a <row>, you can get the contents of the tag you just saved and create a file out of it.
Looking at your example, you just need a couple of boolean values -- boolean inRow and boolean inName so this should not be a hard task =)
Example from Mykong (I am leaving out the actual code, you must do it on your own. It is fairly trivial):
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class ReadXMLFile {
public static void main(String argv[]) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
DefaultHandler handler = new DefaultHandler() {
boolean bfname = false;
boolean blname = false;
boolean bnname = false;
boolean bsalary = false;
public void startElement(String uri, String localName,String qName,
Attributes attributes) throws SAXException {
System.out.println("Start Element :" + qName);
if (qName.equalsIgnoreCase("FIRSTNAME")) {
bfname = true;
}
if (qName.equalsIgnoreCase("LASTNAME")) {
blname = true;
}
if (qName.equalsIgnoreCase("NICKNAME")) {
bnname = true;
}
if (qName.equalsIgnoreCase("SALARY")) {
bsalary = true;
}
}
public void endElement(String uri, String localName,
String qName) throws SAXException {
System.out.println("End Element :" + qName);
}
public void characters(char ch[], int start, int length) throws SAXException {
if (bfname) {
System.out.println("First Name : " + new String(ch, start, length));
bfname = false;
}
if (blname) {
System.out.println("Last Name : " + new String(ch, start, length));
blname = false;
}
if (bnname) {
System.out.println("Nick Name : " + new String(ch, start, length));
bnname = false;
}
if (bsalary) {
System.out.println("Salary : " + new String(ch, start, length));
bsalary = false;
}
}
};
saxParser.parse("c:\\file.xml", handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The best approach is JAXB MArshal and unmarshaller to read and create xml fils.
Here is example
Assuming that your file have element that contains those rows:
<root>
<row><Name>Filename1</Name></row>
<row><Name>Filename2</Name></row>
<row><Name>Filename3</Name></row>
<row><Name>Filename4</Name></row>
<row><Name>Filename5</Name></row>
<row><Name>Filename6</Name></row>
</root>
This code will do the trick:
package com.example;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class Main {
public static String readXmlFromFile(String fileName) throws Exception {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
String line = null;
StringBuilder stringBuilder = new StringBuilder();
String lineSeparator = System.getProperty("line.separator");
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
stringBuilder.append(lineSeparator);
}
return stringBuilder.toString();
}
public static List<String> divideXmlByTag(String xml, String tag) throws Exception {
List<String> list = new ArrayList<String>();
Document document = loadXmlDocument(xml);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
NodeList rowList = document.getElementsByTagName(tag);
for(int i=0; i<rowList.getLength(); i++) {
Node rowNode = rowList.item(i);
if (rowNode.getNodeType() == Node.ELEMENT_NODE) {
DOMSource source = new DOMSource(rowNode);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
StreamResult streamResult = new StreamResult(baos);
transformer.transform(source, streamResult);
list.add(baos.toString());
}
}
return list;
}
private static Document loadXmlDocument(String xml) throws SAXException, IOException, ParserConfigurationException {
return loadXmlDocument(new ByteArrayInputStream(xml.getBytes()));
}
private static Document loadXmlDocument(InputStream inputStream) throws SAXException, IOException, ParserConfigurationException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = null;
documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(inputStream);
inputStream.close();
return document;
}
public static void main(String[] args) throws Exception {
String xmlString = readXmlFromFile("d:/test.xml");
System.out.println("original xml:\n" + xmlString + "\n");
System.out.println("divided xml:\n");
List<String> dividedXmls = divideXmlByTag(xmlString, "row");
for (String xmlPart : dividedXmls) {
System.out.println(xmlPart + "\n");
}
}
}
You only need to write this xml parts to separates files.
Since the user requested one more solution posting other way.
use a StAX parser for this situation. It will prevent the entire document from being read into memory at one time.
Advance the XMLStreamReader to the local root element of the sub-fragment.
You can then use the javax.xml.transform APIs to produce a new document from this XML fragment. This will advance the XMLStreamReader to the end of that fragment.
Repeat step 1 for the next fragment.
Code Example
For the following XML, output each "statement" section into a file named after the "account attributes value":
<statements>
<statement account="123">
...stuff...
</statement>
<statement account="456">
...stuff...
</statement>
import java.io.File;
import java.io.FileReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamResult;
public class Demo {
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml"));
xsr.nextTag(); // Advance to statements element
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
while(xsr.nextTag() == XMLStreamConstants.START_ELEMENT) {
File file = new File("out/" + xsr.getAttributeValue(null, "account") + ".xml");
t.transform(new StAXSource(xsr), new StreamResult(file));
}
}
}
If you're new to Java then the people recommending SAX and StAX parsing are throwing you in at the deep end! This is pretty low-level stuff, highly efficient, but not designed for beginners. You said the file is "big" and they've all assumed that to mean "very big", but in my experience an unquantified "big" can mean anything from 1Mb to 20Gb, so designing a solution based on that description is somewhat premature.
It's much easier to do this with XSLT 2.0 than with Java. All it takes is a stylesheet like this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="row">
<xsl:result-document href="{FileName}">
<xsl:copy-of select="."/>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>
And if it has to be within a Java application, you can easily invoke the transformation from Java using an API.
Try out this,
import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
public class Test{
static public void main(String[] arg) throws Exception{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("foo.xml");
TransformerFactory tranFactory = TransformerFactory.newInstance();
Transformer aTransformer = tranFactory.newTransformer();
NodeList list = doc.getFirstChild().getChildNodes();
for (int i=0; i<list.getLength(); i++){
Node element = list.item(i).cloneNode(true);
if(element.hasChildNodes()){
Source src = new DOMSource(element);
FileOutputStream fs=new FileOutputStream("k" + i + ".xml");
Result dest = new StreamResult(fs);
aTransformer.transform(src, dest);
fs.close();
}
}
}
}
Source: Related Answer
This is the JAVA XML i'm parsing..
<objects>
<object>...<class>A /<class>...</object>
<object>...<class>B</class>....</object>
<object>...<class>A /<class>...</object>
</objects>
Now i split the XML into 3 XML's based on object tag with the below code.
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse("xml");
doc.getDocumentElement().normalize();
TransformerFactory tranFactory = TransformerFactory.newInstance();
Transformer aTransformer = tranFactory.newTransformer();
NodeList list =(NodeList) doc.getElementsByTagName("object");
System.out.println("XML SPLITED");
for (int i=0; i<list.getLength(); i++){
Node element = list.item(i).cloneNode(true);
if(element.hasChildNodes()){
Source src = new DOMSource(element);
FileOutputStream fs=new FileOutputStream("XML" + i + ".xml");
Result dest = new StreamResult(fs);
aTransformer.transform(src, dest);
fs.close();
}
My requirement is to get only the files with class tag A.So my output will be only 2 XML's.Please post your answers.
Using your example, you can do that this way:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class XmlSplitting {
private static final Logger logger = Logger.getLogger(XmlSplitting.class.getName());
private static final String FILE_PATH = "./";
private DocumentBuilder builder;
private Transformer transformer;
public XmlSplitting() throws ParserConfigurationException, TransformerConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
this.builder = factory.newDocumentBuilder();
TransformerFactory transfromerFactory = TransformerFactory.newInstance();
this.transformer = transfromerFactory.newTransformer();
}
public static void main(String[] args) {
try {
XmlSplitting s = new XmlSplitting();
s.run();
} catch (ParserConfigurationException | SAXException | IOException | TransformerException x) {
logger.log(Level.SEVERE, "Error", x);
}
}
private void run() throws ParserConfigurationException, SAXException, IOException, TransformerException {
File file = new File(FILE_PATH + "xml.xml");
if (file.exists()) {
Document document = this.builder.parse(file);
document.normalize();
NodeList list = document.getElementsByTagName("object");
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (Node.ELEMENT_NODE == node.getNodeType()) {
Element object = (Element)node;
NodeList classes = object.getElementsByTagName("class");
if (1 == classes.getLength()) {
Node clazz = classes.item(0);
if (Node.ELEMENT_NODE == clazz.getNodeType()) {
this.copyNodeToNewFile(clazz.getTextContent(), node, i);
}
} else {
logger.log(Level.SEVERE, "Number of class nodes in node object is different than expected. Number of class nodes found: {0}.", classes.getLength());
}
}
}
} else {
logger.log(Level.SEVERE, "You provided wrong path for xml file.");
}
}
private void copyNodeToNewFile(String content, Node node, int i) throws IOException, TransformerException {
boolean copy = this.nodeShouldBeCopied(content);
logger.log(Level.INFO, "Node with content {0} will {1}be moved to separete file.", new Object[]{content, true == copy ? "" : "not "});
if (copy) {
String fileName = String.format("%sxml%d.xml", FILE_PATH, i);
try (FileOutputStream fos = new FileOutputStream(fileName)) {
Source source = new DOMSource(node);
Result destination = new StreamResult(fos);
this.transformer.transform(source, destination);
}
}
}
// here you can change condition to copy given node to file
private boolean nodeShouldBeCopied(String content) {
return content.contains("A");
}
}
How about using a Brazilian framework to parse your XML? it will make your code more exotic and you can tell your friends about it:
http://jcoltrane.sourceforge.net/userguide/extending_parsing_process.html
I created a simple class to create an XML document.
However, if I call the method more than once while creating a document of the same name the file does not overwrite.
How could I make the class automatically overwrite existing files of the same name?
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class XMLCreater {
public static void CreateXMLDoc(String name, String root, String[] elements, String[] children) throws TransformerConfigurationException {
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement(root);
doc.appendChild(rootElement);
for (int i = 0; i < elements.length; i ++) {
Element element = doc.createElement(elements[i]);
element.appendChild(doc.createTextNode(children[i]));
rootElement.appendChild(element);
}
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
File dir = new File(System.getProperty("user.dir"));
StreamResult result = new StreamResult(new File(dir + "\\XML\\" + name + ".xml"));
transformer.transform(source, result);
}
catch(ParserConfigurationException pce){
pce.printStackTrace();
} catch(TransformerException tfe) {
tfe.printStackTrace();
}
}
}
I executed your code with the following statements:
public static void main (String[] args)
{
XMLCreater x = new XMLCreater();
String[] s = {"A","B","C"};
try
{
x.CreateXMLDoc("k","root",s,s);
x.CreateXMLDoc("k","root",s,s);
x.CreateXMLDoc("fakih","root",s,s);
}
catch (TransformerConfigurationException exception)
{ exception.printStackTrace(); }
}
And it nicely overwrites the existing files, no problems about overwriting, check it yourself.
I'll be honest here... I'm not able to replicate your problem. It works fine for me when I run this program multiple times in a for loop. Are you sure you didn't accidentally open the result file, thus locking it, before running your program?
If you are concerned of having multiple threads running your program at the same time, perhaps you can apply a synchronized block to prevent two threads trying to write the same file, like this:-
...
synchronized (XMLCreater.class) {
StreamResult result = new StreamResult(new File(dir + "\\XML\\" + name + ".xml"));
transformer.transform(source, result);
}