Modifying XML file (namespace) using Java, JDom, XPath - java

I am newbie in XML parsing world. I found solution for reading xml file with 'namespaces' as given below. I refered this stackoverflow link Default XML namespace, JDOM, and XPath and reading Element works. But I am not able to modify the element. Here goes my problem statements.
My xml file looks like this.
<?xml version="1.0" encoding="UTF-8"?>
<ProofSpecification xmlns="http://www.zurich.ibm.com/security/idemix"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.zurich.ibm.com/security/idemix ProofSpecification.xsd">
<Declaration>
<AttributeId name="id1" proofMode="unrevealed" type="string" />
<AttributeId name="id2" proofMode="unrevealed" type="string" />
</Declaration>
<Specification>
<Credentials>
<Credential issuerPublicKey="file:/Users/ipk.xml"
credStruct="file:/Users/CredStruct_ResUAC.xml" name="SpecResUAC">
<Attribute name="FirstName">id1</Attribute>
<Attribute name="LastName">id2</Attribute>
</Credential>
</Credentials>
<Pseudonyms>
<Pseudonym name="pseudonym"></Pseudonym>
<DomainPseudonym>1331859289489</DomainPseudonym>
</Pseudonyms>
<Messages />
</Specification>
</ProofSpecification>
And my java code snippet looks like this.
public class ModifyXMLFileJDom {
public static void main(String[] args) throws JDOMException, IOException {
try {
SAXBuilder builder = new SAXBuilder();
File xmlFile = new File("/blabla/ProofSpecResUAC_old.xml");
Document doc = (Document) builder.build(xmlFile);
XPath xpath = XPath.newInstance("x:ProofSpecification/x:Specification/x:Pseudonyms/x:DomainPseudonym");
xpath.addNamespace("x", doc.getRootElement().getNamespaceURI());
System.out.println("domainPseudonym: "+xpath.valueOf(doc));
xpath.setVariable("555555", doc);
XMLOutputter xmlOutput = new XMLOutputter();
xmlOutput.setFormat(Format.getPrettyFormat());
xmlOutput.output(doc, new FileWriter("/blabla/proofSpecOut.xml"));
System.out.println("File updated!");
} catch (IOException io) {
io.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
}
}
}
This piece of code works fine and Reading Element "domainPseudonym" works.
But I want to modify this element from
<DomainPseudonym>1331859289489</DomainPseudonym> to
<DomainPseudonym>555555</DomainPseudonym>
I tried modifying using function xpath.setVariable("555555", doc) but not working and not giving out any error. The end result is that it copies the same content to the new xml file "proofSpecOut.xml".

XPath is not used for modifying the xml, only for finding parts of it. XPath.setVariable() is only for setting variables in your xpath expression. You want to use XPath.selectSingleNode() to retrieve one of the Elements of your document and then you want to modify that directly using Element.setText().

Related

Jdom2 parser with empty xml document

I am trying to parse a string representation of a xml document with jdom2. I expect the xml string
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
to be a valid xml document. But when I run this simple code snippet:
import java.io.IOException;
import java.io.StringReader;
import org.jdom2.*;
public class Main {
public static void main(String []args){
SAXBuilder parser = new SAXBuilder();
String data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
try {
Document doc = parser.build(new StringReader(data));
} catch (JDOMException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
I receive the error:
org.jdom2.input.JDOMParseException: Error on line 1: Premature end of file.
at org.jdom2.input.sax.SAXBuilderEngine.build(SAXBuilderEngine.java:232)
at org.jdom2.input.sax.SAXBuilderEngine.build(SAXBuilderEngine.java:303)
at org.jdom2.input.SAXBuilder.build(SAXBuilder.java:1196)
at testpack.Main.main(Main.java:32)
Does the xml specification not allow an xml payload without an root element?
If not, how should I check if the xml document is empty?
Edit: I also noticed that the documentation in Jdom2 for the Document() class states that
A document must have a root element, so this document will not be well-formed and accessor methods will throw an IllegalStateException if this document is accessed before a root element is added.
It might just be that Jdom2 doesn't support empty xml documents?
After further investigation I have noted that the specification for and xml document as defined by w3 specifies that a 'Well formed xml document' should adhere to
It contains one or more elements.
Meaning zero elements is not an option. The input xml String is a malformed xml string.

JAVA DOM XML Parse

I have a large XML file that I want to parse
XML - The XML file has over 300 cases and other tags
i'm only interested in the Cases. What I want todo is take all the cases and everything in the case tag and save it in a new DOM doc that only holds the cases, Once I have this New DOM I want to send it to another class that will take the information and format it into a word document( but i'll takle that once I get there)
an example of my XML is
<suite>
<cases>
<case>
<id/>
<title/>
<type/>
<priority/>
<estimate/>
<references/>
<custom>
<functional_area/>
<technology_dependence/>
<reviewed/>
<steps_completed>
</steps_completed>
<preconds> </preconds>
<steps_seperated>
<step>
<index/>
<content>
</content>
<expected>
</expected>
</step>
<step>
<index/>
<content>
</content>
<expected>
</expected>
</step>
<step>
</steps_seperated>
</custom>
</case>
</suite>
</cases>
There are about 400 of these case nodes
My java
setting up the initial
private void setXMLdoc(String path){
xmlDoc = getDocument(path) ;
}
getting the xml file
private Document getDocument(String path) {
try{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(path);
} catch (ParserConfigurationException ex) {
Logger.getLogger(ImportXML.class.getName()).log(Level.SEVERE, null, ex);
} catch (SAXException ex) {
Logger.getLogger(ImportXML.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(ImportXML.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
would this create a new doc that only contains the cases?
NodeList fList = xmlDoc.getElementsByTagName("case");
also how would I print out all elements todo with the case? / print out all the elements todo with all the cases
Thanks in advance - im still pretty new so sorry if this question doesn't make sense or seems a bit basic
The approximate code will be
DOMParser parser=new DOMParser();
InputSource source=new InputSource(<the XML file/network stream>);
parser.parse(source);
Element docElement=parser.getDocument().getDocumentElement();
XPath xPath=xPathFactory.newXPath();
XPathExpression expression_=xPath.compile("//case");
NodeList list_=(NodeList)expression_.evaluate(docElement,XPathConstants.NODESET);DocumentBuilder documentBuilder=DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document newDocument=documentBuilder.newDocument();
Element newElement=newDocument.createElement("SOME_NAME");
newDocument.appendChild(newElement);
for(int i=0;i<list_.getLength();i++){Node n=newDocument.importNode(list_.item(i),true);newElement.appendChild(n);}
then send the 'newDocument' to the other class

Trying to get my Java IRC Bot to parse and write to an preexisting XML file

I'm a total Java virgin, and I've been stumbling slowly but surely in developing an IRC bot for my friends. So far, I've gotten nearly all of the features in working order. But, I'm really wracking my brain over this problem here, my bot so far can reply with a link, but every week, I have to change the link in the java file manually and recompile the whole thing. So, I want it to be able to parse the pertinent values from an XML file in the same directory the bot's java files are in, and be able to update those same values through an IRC client.
import org.jibble.pircbot.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.xml.sax.*;
import org.w3c.dom.*;
public class ModBot extends PircBot {
static String inputFile = "./botdata.xml";
static String outputFile = "./botdata.xml";
public ModBot() {
setLogin("ModBot");
this.setName("ModBot");
setVersion(" ");
}
public void onMessage(String channel, String sender, String login, String hostname, String message) {
if (message.equalsIgnoreCase("!lolcat")) {
sendMessage(channel, sender + "http://i.imgur.com/4IX4cUL.jpg");
}
if (message.startsWith("!updatelolcat ")) {
if(login.equals("Mainmod"));
String changelolcat = message.substring(14);
}
}
}
And the XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE botdata [
<!ELEMENT botdata (lolcat,partytime,start,end)>
<!ELEMENT lolcat (#PCDATA)>
<!ELEMENT partytime (start,end)>
<!ELEMENT start (#PCDATA)>
<!ELEMENT end (#PCDATA)>
]>
<botdata>
<lolcat>http://i.imgur.com/4IX4cUL.jpg</lolcat>
<partytime>
<start>8:45:30</start>
<end>11:00:00</end>
</partytime>
</botdata>
What I want to do is take whatever "changelolcat" is, and overwrite the current link in the XML, and then a way to read from the same XML to send what's in "lolcat" to anyone replying "!lolcat". I've been going through xpath and jdom and stuff, and I just can't make sense of it. What I've read with methods using xpath looks promising, and I'd prefer to use it because it's prettier to read.
EDIT:
It worked, I put in
try {DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse("botdata.xml");
Node botdata = document.getElementsByTagName("botdata").item(0);
NodeList nodes = botdata.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node element = nodes.item(i);
if ("lolcat".equals(element.getNodeName())) {
element.setTextContent(changelolcat);
}
}
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
StreamResult streamResult = new StreamResult(new File("botdata.xml"));
transformer.transform(domSource, streamResult);
}catch (ParserConfigurationException pce) {
pce.printStackTrace();
} catch (TransformerException tfe) {
tfe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (SAXException sae) {
sae.printStackTrace();
}
after String changelolcat = message.substring(14);
EDIT: I figured out how to parse from my XML to send what's in a node as a message, does this look right? I feel like I'm not supposed to keep copying the doc builder over and over in different methods
if (message.equalsIgnoreCase("!lolcat")) {
try {
DocumentBuilderFactory documentBuilderFactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder =
documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(botxml);
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
String lolcat = xpath.evaluate("//lolcat", document);
sendMessage(channel, sender + lolcat);
} catch (Exception e) {
e.printStackTrace();
}
}
One way to do this would be to use the javax.xml.parsers.DocumentBuilder to parse your xml file into javax.swing.text.Document, which provides a great interface for getting and modifying individual elements and their values. You can then write the modified document back to the original file, overwriting it with the new version.
Here are some links to the relevant javadocs:
Document
DocumentBuilder
You might also want to look at the javadocs for the DocumentBuilderFactory object, which is definitely the best way to generate DocumentBuilders.

Write Junit test for applying XSLT on XML Using TransformerFactory

I am new to Junit Test, I have an xml file and xslt file as follow:
File.xml
<people>
<person>
<role>Parent</role>
<lastname>Smith</lastname>
<locations>
<location>
<city>Springfield</city>
<state>MA</state>
<unimportant-field>123</unimportant-field>
</location>
<location>
<city>Chicago</city>
<state>IL</state>
<unimportant-field>456</unimportant-field>
</location>
</locations>
</person>
<person>
<role>Child</role>
<lastname>Smith</lastname>
<locations>
<location>
<city>Springfield</city>
<state>IL</state>
<unimportant-field>789</unimportant-field>
</location>
<location>
<city>Chicago</city>
<state>IL</state>
<unimportant-field>000</unimportant-field>
</location>
<location>
<city>Washington</city>
<state>DC</state>
<unimportant-field>555</unimportant-field>
</location>
</locations>
</person>
The XSLT file is as follows:
<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:strip-space elements="*"/>
<xsl:key name="parent-location" match="person[role='Parent']/locations/location" use="concat(../../lastname, '|', city, '|', state)" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="person[role='Child']/locations/location[not(key('parent-location', concat(../../lastname, '|', city, '|', state)))]"/>
</xsl:stylesheet>
This is the java code that I am using to apply the above XSLT on XML.
public class ApplyXSLT {
/**
* #param args
* #throws TransformerException
* #throws FileNotFoundException
*/
public void process()throws FileNotFoundException, TransformerException
{
InputStream file1 = new FileInputStream("file.xml");
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new StreamSource("file.xslt"));
transformer.transform(new StreamSource(file1), new StreamResult(new FileOutputStream("output.xml")));
}
}
I am not sure how to write a Junit test for the above, if anyone can guide me. I would appreciate it. Thank you.
First of all, it is most recommended that you write Junit tests to test API utilities. I mean, you should first code a full-semantic abstraction: A class with properly named methods, parameters, and return values. The standard method main does not have any semantics: You cannot know what it does (its name has no specific meaning), nor how many parameters it requires, nor where it produces its results.
So, when you have a class like this:
public class MyTransformer
{
public void transform(InputStream in, OutputStream out)
throws IOException, TransformException
{...}
}
// If needed, You could also add a main method which delegates over the transform method.
... you may code your Junit tester like this:
public class MyTransformerTest
{
// Transforms an existing file producing the result in memory,
// and compares it with an existing, expected output file.
private void test(String inputFilename, String expectedOutputFilename)
throws IOException
{
try
{
ByteArrayOutputStream out=new ByteArrayOutputStream();
new MyTransformer().transform(new FileInputStream(inputFilename), out);
String expectedResult=readFileAsString(expectedOutputFilename);
assertEquals("File '" + inputFilename + "' was not transformed OK", expectedResult, out.toString("ISO-8859-1"));
}
catch (TransformerException e)
{
e.printStackTrace();
fail(e.toString());
}
}
#Test
public void testEmpty()
throws IOException
{
test("empty-input.xml", "empty-output.xml");
}
#Test
public void testOnePersons()
throws IOException
{
test("one-persons-input.xml", "one-persons-output.xml");
}
#Test
public void testTwoPersons()
throws IOException
{
test("two-persons-input.xml", "two-persons-output.xml");
}
}
Not that this tester is based on a general purpose test method which tests any file. So, you just have to write an input file and an expected output file for each case you are interested on.
This technique will ease for you the future maintainment of the tester: Rembember that every time you'll find a bug, first of all you'll have to add a method in the tester to reproduce it (which initially will fail, of corse). Then fix the code, and run the tester again until it doesn't fail.
One more note: I usually leave the IOExceptions in the throws clause, without catching them. This is because this exception is not your code's fault. So, it is not interesting to your tests. If an IOException arises while the tester is executing, it means that no input or output file could be instantiated: It occurred before your code was executed.

Load resource referencing ressources in Java Webapp

I got a xslt transformation done with something like this:
public static String transform(Source xml, String xsltPath) {
try {
InputStream is = MyClass.class.getResourceAsStream(xsltPath);
final Source xslt = new StreamSource(is);
final TransformerFactory transFact = TransformerFactory.newInstance();
final Transformer trans = transFact.newTransformer(xslt);
final OutputStream os = new ByteArrayOutputStream();
final StreamResult result = new StreamResult(os);
trans.transform(xml, new StreamResult(os));
final String theResult = result.getOutputStream().toString();
return theResult;
}
catch (TransformerException e) {
return null;
}
}
As you can see xslt is loaded from resources. The function together with the transformation files i need are bundled in a library and this works as long as the library is stand alone from a main method or so.
However if this library is bundled with a webapplication and deployed in Jetty/Tomcat it gets a bit complicated. As long as the transformation files in it self do not reference any other files from resources there is no problem but with files like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet>
<xsl:import href="import_file1.xsl" />
<xsl:import href="import_file2.xsl" />
<xsl:template name="aTtemplate">
<xsl:for-each select="document('import_file3.xml')">
...
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The imports cannot be resolved and the document from the for each loop cannot be found. In Tomcat a workaround is to put the files inside the $TOMCAT/bin directory but that is not a suitable solution for us. Is there any method to get this resources recursively out of the lib?

Categories