The first time I run this program I need to create the xml, I first create the file and create a Document object and then convert it to an Element object.
xmlDoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
xmlDoc +="<head>";
xmlDoc += "</head>";
Document xmlFile = XmlParser.parseXmlString(xmlDoc);
Element element = xmlFile.getDocumentElement();
I have already verified this with its NodeType code, but when I create the parent node it gives me the Element_Node == 1. I attach this node to the element object.
Element newElement = xmlFile.createElement("parent");
newElement.setAttribute("id", i);
element.appendChild(newElement);
I will put the child in a parent if it isn't already a child of the parent element, I check for this, if it isn't a child yet I will create a new Node class and give it text content.
Node newChild = xmlFile.createElement("child");
newChild.setTextContent(text);
newElement.appendChild(newChild);
Then I will save this file with a transformer.
Transformer transformer = null;
try {
transformer = TransformerFactory.newInstance().newTransformer();
} catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(xmlFile);
StreamResult console = new StreamResult(System.out);
try {
transformer.transform(source, new StreamResult(new FileOutputStream(file.getPath())));
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Now the second time I run the program I will parse straight from this file. The XML file that was created has the structure below
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<head>
<parent id="1">
<child>text1</child>
<child>text2</child>
<child>text3</child>
</parent>
<parent id="2">
<child>text1</child>
<child>text2</child>
</parent>
</head>
now that the file is created, the file will be read then parsed to create the element instead of the hard coded string.
xmlDoc = this.readFile(file, Charset.forName("UTF-8"));
Document xmlFile = XmlParser.parseXmlString(xmlDoc);
Element element = xmlFile.getDocumentElement();
...
String readFile(File file, Charset charset) throws IOException {
return new String(Files.readAllBytes(file.toPath()), charset);
The problem is now the parent element cannot be casted as a Element and has the Text_Node type value == 3. The following object cannot be casted.
Element nextSib = (Element) element.getFirstChild();
The idea is that now I can append a relevant child to a parent by going through each parent node which is why I need to obtain it in Element form so I can use the id attribute. But I cannot do this since the parent node is converted to a text node for some reason.
As you use the indentation when writing out a tree there will be white space between element nodes so a child node can be a text node with white space. If you are looking for the first element child node either use XPath *[1] or simply the name of the element foo[1] or if you want to do it with childNodes make sure you check the nodeType until you have an element node.
Related
I want to create new Xml file from the select nodes only i am using dom4j to parse and create new xml file. Example lets assume Nodes Customer name = Joseph is the child of root element TRX i want to show the whole elements that contain joseph and create a new file
enter code here
File inputFile = new File("C:\\Users\\db2admin\\Desktop\\S4decs\\tlog01_004.xml");
SAXReader reader = new SAXReader(true);
reader.setValidation(false);
reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
Document document = reader.read(inputFile);
document.getRootElement();
document.selectNodes("//TRX[#type]='16'").size();
document.selectNodes("/CUSTOMER").size();
// Pretty print the document to System.out
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer;
writer = new XMLWriter( System.out, format );
writer.write( document );
} catch (DocumentException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE TRANSACTIONS SYSTEM "tlog.dtd">
<TRANSACTIONS storeid="4" sbs="780030" location="1">
<TRX type="16" term="742" trxnum="143895" saleperson="0" supervisor_id="152332149" storeid="4" sbs="780030" opcode="153135959" date="20160915" endtime="111000">
</TRX>
<TRX type="31" term="742" trxnum="143896" starttime="095720" supervisor_id="152332149" storeid="4" sbs="780030" opcode="153135959" date="20160915" endtime="111001">
<CASHOPER managerid="153135959">
<PAYMENT id="1" amount="3000.00" descr="CASH" tndnumb="1" exchangetndid="0">
</PAYMENT>
</CASHOPER>
<LINKTRX linktype="8" prevstoreid="4" prevxactdate="2016-09-14" prevxacttime="22:58:29" prevtermid="0" prevxactid="7620" prevoperid="0"></LINKTRX>
</TRX>
<TRX type="16" term="743" trxnum="65729" saleperson="0" supervisor_id="153136068" storeid="4" sbs="780030" opcode="152332262" date="20160915" endtime="111219">
</TRX>
You should select the nodes you need, then append them as children of another element and put this element as root of a new document. Note that first XPath expression seems incorrect (maybe it should be //TRX[#type=16])
private Document daMethod(Document document, String xpath) throws Exception{
List<Node> nodes = document.selectNodes(xpath);
Element newRoot = DocumentHelper.createElement("TRXS");
for (Node node : nodes) {
newRoot.add((Node)node.clone());
}
return DocumentHelper.createDocument(newRoot);
}
Hope it helps.
Well, going straight to the point. i'm using java DOM to merge two xml documents in one. To do this i first created a new node to put both documents inside, making the docs, child of the created node. The father has two attributes and one of them is the same as one of my xml document.
The attribute is xmlns="http://www.portalfiscal.inf.br/nfe".
I don't know if there is some xml rule but the child attribute got hide and just the father show the attribute "xmlns".
Here is a stretch of the code i got:
<?xml version="1.0" encoding="UTF-8"?>
-<nfeProc versao="3.10" xmlns="http://www.portalfiscal.inf.br/nfe">
-<NFe>
Here is what i want:
<?xml version="1.0" encoding="UTF-8"?>
-<nfeProc xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
-<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
And here is my code:
public void juntarXML () {
File nota = new File("C:\\NotaFiscalEletronica.xml");
File protocolo = new File("C:\\Protocolo.xml");
//File xml = new File("C:\\xml.xml");
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = null;
Document docNota = null;
Document docProtocolo = null;
Document docXML = null;
try {
docBuilder = docFactory.newDocumentBuilder();
docNota = docBuilder.parse(nota);
docProtocolo = docBuilder.parse(protocolo);
docXML = docBuilder.newDocument();
docXML.setXmlVersion("1.0");
// Criando nó pai que conterá os dois documentos
Element nfeProc = docXML.createElement("nfeProc");
nfeProc.setAttribute("xmlns", "http://www.portalfiscal.inf.br/nfe");
nfeProc.setAttribute("versao", "3.10");
docXML.appendChild(nfeProc);
// Buscando e importando os nós dos documentos xml
NodeList list = docXML.getElementsByTagName("nfeProc");
Element listNode = (Element)list.item(0);
String chave = "chave";
for (int i=0; i<2; i++) {
NodeList list2;
Element list2Node;
if (i==0) {
list2 = docNota.getElementsByTagName("NFe"); // Nota Fiscal
list2Node = (Element)list2.item(0);
}else {
list2 = docProtocolo.getElementsByTagName("protNFe"); // Protocolo
list2Node = (Element)list2.item(0);
chave = list2Node.getChildNodes().item(0).getChildNodes().item(2).getTextContent(); // Recuperando a chave
}
Node importedNode = docXML.importNode(list2Node, true);
listNode.appendChild(importedNode);
}
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer trans = transFactory.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(docXML);
StreamResult result = new StreamResult(new StringWriter());
trans.transform(source, result);
Writer output = new BufferedWriter(new FileWriter("C:\\" + chave + ".xml"));
String xmlOutput = result.getWriter().toString();
//System.out.println(xmlOutput);
output.write(xmlOutput);
output.close();
}catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I had try to re-create the attribute of the node "NFe" and there is no results.
I already checked if the attribute is still there and it is. It just disappear when i put it into a xml document.
So, there is a way to show it??
Taking the opportunity, in the case of "nfeProc", can i set something to not order the attributes alphabetically, to stay the order i add them??
Since now i thank you for the attention.
The two XMLs are the same for a DOM. Descendants inherit the namespace definitions until they are redefined.
So the XML serializer should optimize the the XML and keep only the needed namespace definitions. That means ...
<?xml version="1.0" encoding="UTF-8"?>
<nfeProc versao="3.10" xmlns="http://www.portalfiscal.inf.br/nfe">
<NFe>
...
... is the expected XML document. Here is no reason to have an identical namespace definition on a child element.
Additionally, use createElementNS() - the namespace aware variant of createElement(). This should add the xmlns-attributes automatically as needed.
this is my xml:
Example:
<?xml version="1.0" encoding="UTF_8" standalone="yes"?>
<StoreMessage xmlns="http://www.xxx.com/feed">
<billingDetail>
<billingDetailId>987</billingDetailId>
<contextId>0</contextId>
<userId>
<pan>F0F8DJH348DJ</pan>
<contractSerialNumber>46446</contractSerialNumber>
</userId>
<declaredVehicleClass>A</declaredVehicleClass>
</billingDetail>
<billingDetail>
<billingDetailId>543</billingDetailId>
<contextId>0</contextId>
<userId>
<pan>F0F854534534348DJ</pan>
<contractSerialNumber>4666546446</contractSerialNumber>
</userId>
<declaredVehicleClass>C</declaredVehicleClass>
</billingDetail>
</StoreMessage>
With JDOM parser i want to get all <billingDetail> xml nodes from it.
my code:
SAXBuilder builder = new SAXBuilder();
try {
Reader in = new StringReader(xmlAsString);
Document document = (Document)builder.build(in);
Element rootNode = document.getRootElement();
List<?> list = rootNode.getChildren("billingDetail");
XMLOutputter outp = new XMLOutputter();
outp.setFormat(Format.getCompactFormat());
for (int i = 0; i < list.size(); i++) {
Element node = (Element)list.get(i);
StringWriter sw = new StringWriter();
outp.output(node.getContent(), sw);
StringBuffer sb = sw.getBuffer();
String text = sb.toString();
xmlRecords.add(sb.toString());
}
} catch (IOException io) {
io.printStackTrace();
} catch (JDOMException jdomex) {
jdomex.printStackTrace();
}
but i never get as output xml node as string like:
<billingDetail>
<billingDetailId>987</billingDetailId>
<contextId>0</contextId>
<userId>
<pan>F0F8DJH348DJ</pan>
<contractSerialNumber>46446</contractSerialNumber>
</userId>
<declaredVehicleClass>A</declaredVehicleClass>
</billingDetail>
what i am doing wrong? How can i get this output with JDOM parser?
EDIT
And why if XML start with
<StoreMessage> instead like <StoreMessage xmlns="http://www.xxx.com/MediationFeed">
then works? How is this possible?
The problem is that there are two versions of the getChildren method:
java.util.List getChildren(java.lang.String name)
This returns a List of all the child elements nested directly (one level deep) within this element with the given local name and belonging to no namespace, returned as Element objects.
and
java.util.List getChildren(java.lang.String name, Namespace ns)
This returns a List of all the child elements nested directly (one level deep) within this element with the given local name and belonging to the given Namespace, returned as Element objects.
The first one doesn't find your node if it belongs to a namespace, you should use the second one.
I am creating a W3C Document object using a String value. Once I created the Document object, I want to add a namespace to the root element of this document. Here's my current code:
Document document = builder.parse(new InputSource(new StringReader(xmlString)));
document.getDocumentElement().setAttributeNS("http://com", "xmlns:ns2", "Test");
document.setPrefix("ns2");
TransformerFactory tranFactory = TransformerFactory.newInstance();
Transformer aTransformer = tranFactory.newTransformer();
Source src = new DOMSource(document);
Result dest = new StreamResult(new File("c:\\xmlFileName.xml"));
aTransformer.transform(src, dest);
What I use as input:
<product>
<arg0>DDDDDD</arg0>
<arg1>DDDD</arg1>
</product>
What the output should look like:
<ns2:product xmlns:ns2="http://com">
<arg0>DDDDDD</arg0>
<arg1>DDDD</arg1>
</ns2:product>
I need to add the prefix value and namespace also to the input xml string. If I try the above code I am getting this exception:
NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
Appreciate your help!
Since there is not an easy way to rename the root element, we'll have to replace it with an element that has the correct namespace and attribute, and then copy all the original children into it. Forcing the namespace declaration is not needed because by giving the element the correct namespace (URI) and setting the prefix, the declaration will be automatic.
Replace the setAttribute and setPrefix with this (line 2,3)
String namespace = "http://com";
String prefix = "ns2";
// Upgrade the DOM level 1 to level 2 with the correct namespace
Element originalDocumentElement = document.getDocumentElement();
Element newDocumentElement = document.createElementNS(namespace, originalDocumentElement.getNodeName());
// Set the desired namespace and prefix
newDocumentElement.setPrefix(prefix);
// Copy all children
NodeList list = originalDocumentElement.getChildNodes();
while(list.getLength()!=0) {
newDocumentElement.appendChild(list.item(0));
}
// Replace the original element
document.replaceChild(newDocumentElement, originalDocumentElement);
In the original code the author tried to declare an element namespace like this:
.setAttributeNS("http://com", "xmlns:ns2", "Test");
The first parameter is the namespace of the attribute, and since it's a namespace attribute it need to have the http://www.w3.org/2000/xmlns/ URI. The declared namespace should come into the 3rd parameter
.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ns2", "http://com");
Bellow approach also works for me, but probably should not use in performance critical case.
Add name space to document root element as attribute.
Transform the document to XML string. The purpose of this step is to make the child element in the XML string inherit parent element namespace.
Now the xml string have name space.
You can use the XML string to build a document again or used for JAXB unmarshal, etc.
private static String addNamespaceToXml(InputStream in)
throws ParserConfigurationException, SAXException, IOException,
TransformerException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
/*
* Must not namespace aware, otherwise the generated XML string will
* have wrong namespace
*/
// dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(in);
Element documentElement = document.getDocumentElement();
// Add name space to root element as attribute
documentElement.setAttribute("xmlns", "http://you_name_space");
String xml = transformXmlNodeToXmlString(documentElement);
return xml;
}
private static String transformXmlNodeToXmlString(Node node)
throws TransformerException {
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
StringWriter buffer = new StringWriter();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new DOMSource(node), new StreamResult(buffer));
String xml = buffer.toString();
return xml;
}
Partially gleaned from here, and also from a comment above, I was able to get it to work (transforming an arbitrary DOM Node and adding a prefix to it and all its children) thus:
private String addNamespacePrefix(Document doc, Node node) throws TransformerException {
Element mainRootElement = doc.createElementNS(
"http://abc.de/x/y/z", // namespace
"my-prefix:fake-header-element" // prefix to "register" it with the DOM so we don't get exceptions later...
);
List<Element> descendants = nodeListToArrayRecurse(node.getChildNodes()); // for some reason we have to grab all these before doing the first "renameNode" ... no idea why ...
mainRootElement.appendChild(node);
doc.renameNode(node, "http://abc.de/x/y/z", "my-prefix:" + node.getNodeName());
descendants.stream().forEach(c -> doc.renameNode(c, "http://abc.de/x/y/z", "my-prefix:" + c.getNodeName()));
}
private List<Element> nodeListToArrayRecurse(NodeList entryNodes) {
List<Element> allEntries = new ArrayList<>();
for (int i = 0; i < entryNodes.getLength(); i++) {
Node child = entryNodes.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
allEntries.add((Element) child);
allEntries.addAll(nodeListToArray(child.getChildNodes())); // recurse
} // ignore other [i.e. text] nodes https://stackoverflow.com/questions/14566596/loop-through-all-elements-in-xml-using-nodelist
}
return allEntries;
}
If it helps anybody. I then convert it to string, then manually remove the extra header and closing lines. What a pain, I must be doing something wrong...
This seems to be working for me, and it's much simpler than those answers provided:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse(new File(filename));
document.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:yourNamespace", "http://whatever/else");
I was trying to parse following strings to form a xml document and then trying to extract all child nodes of and add to a different document object which is already available to me.
<dhruba><test>this</test>that<test2>wang chu</test2> something.... </dhruba>
<dhruba>this is text node <test>this</test>that<test2>wang chu</test2> anything..</dhruba>
while I am trying to read the child nodes, it is returning null child for TEXT_NODE for 1st string and null for ELEMENT_NODE for 2nd String, this is wrong, is it API problem ??
I am using following code ... it compile , I am using java 6.
Node n = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
dom = db.newDocument();
Element rootEle = dom.createElement("resources");
// adding the root element to the document
dom.appendChild(rootEle);
Element element = dom.createElement("string");
element.setAttribute("name", "some_name");
try {
n = db.parse(new InputSource(new StringReader("<dhruba><test>this</test>that<test2>node value</test2> some text</dhruba>"))).getDocumentElement();
n = dom.importNode(n, true);
NodeList nodeList = n.getChildNodes();
int length = nodeList.getLength();
System.out.println("Total no of childs : "+length);
for(int count = 0 ; count < length ; count++ ){
Node node = nodeList.item(count);
if(node != null ){
element.appendChild(node);
}
}
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rootEle.appendChild(element);
INPUT :: as string
<dhruba><string name="some_name">
that
<test>this</test>
<test2>node value</test2>
some text
</string>
</dhruba>
EXPECTED OUTPUT :: as document
<string>
<string name="some_name">
<test>this</test>
<test2>node value</test2>
</string>
</string>
if I try to parse
<test>this</test>that<test2>wang chu</test2> something....
then output comes as "thiswang chu"
Why is this happening? what needs to be done if I want to add following node under another document element, i.e. <string>.
<test>this</test>
that
<test2>node value</test2>
some text
[notice that it does not have <dhruba>] inside parent node of another
document.
Hope I am clear. Above code compiles in Java 6
I will assume that this is Java.
First, I'm surprised that you don't get an exception with your importNode() call, since you're importing the Document, which shouldn't be allowed (per the JavaDoc).
Now to the question that you asked: if you only want to attach specific node types, you need to make a test using the node's type. A switch statement is the easiest (note: this has not been compiled, may contain syntax errors):
switch (n.getNodeType())
{
case ELEMENT_NODE :
// append the node to the other tree
break;
default :
// do nothing
}
Probably you want Node.cloneNode() method:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document dom = db.newDocument();
Element element = dom.createElement("string");
element.setAttribute("name", "some_name");
String inputXMLString =
"<dhruba><test>this</test>that<test2>node value</test2> some text</dhruba>";
Node n = db.parse(new InputSource(new StringReader(inputXMLString))).getDocumentElement();
n = dom.importNode(n, true);
NodeList nodeList = n.getChildNodes();
for (int i = 0; i < nodeList.getLength(); ++i)
{
Node node = nodeList.item(i);
element.appendChild(node.cloneNode(true));
}
dom.appendChild(element);
To get dom into stdout or file you could write:
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
DOMSource source = new DOMSource(dom);
StreamResult result = new StreamResult(System.out);
transformer.transform(source, result);
Result:
<string name="some_name">
<test>this</test>that<test2>node value</test2> some text</string>