How to add a default name space with dom4j? - java

I would like to add a default name space to the root element of a XML document using dom4j, like in the following snippet. What is the correct way to do this with dom4j?
<?xml version="1.0" encoding="utf-8" ?>
<animals xmlns="http://zoo.domain.org/schema/animals" >
<animal id="1">
<elephant>
<name>Jumbo</name>
</elephant>
</animal>
</animals>
The dom4j API does provide the method Element#addNamespace but the javadoc tells that the prefix may not be blank.
The following code will result in the expected namespace for animals though:
Document document = DocumentHelper.createDocument();
Element animals = document.addElement("animals")
.addNamespace("", "http://zoo.domain.org/schema/animals");
Element animal = animals.addElement("animal")
.addAttribute("id", "1");
animal.addElement("elephant")
.addElement("name")
.addText("Jumbo");
// write document to file etc.
...
... but the child element animal gets an empty string as default namespace, which is not what I want:
<?xml version="1.0" encoding="UTF-8"?>
<animals xmlns="http://zoo.domain.org/schema/animals">
<animal xmlns="" id="1">
<elephant>
<name>Jumbo</name>
</elephant>
</animal>
</animals>

The method Document#addElement (but also Element#addElement) accepts a second parameter namespaceURI. That does the trick, adding the default namespace to the XML element.
The following code will result in the expected XML.
Document document = DocumentHelper.createDocument();
Element animals = document.addElement("animals", "http://zoo.domain.org/schema/animals");
Element animal = animals.addElement("animal")
.addAttribute("id", "1");
animal.addElement("elephant")
.addElement("name")
.addText("Jumbo");
// write document to file etc.
...
Also worth mentioning is that in case you would like to create an Element on its own DocumentFactory#createElement has an overloaded version that accepts a namespaceURI as well. DocumentHelper#createElement does not have such an overloaded method.

You are not creating the document (root) element.
Document document = DocumentHelper.createDocument();
Element root = document.addElement( "animals" );
// TODO the rest of your code
Eventually, you'll pass your document to an XML writer to write (save) the document. Also, from Javadoc:
Element addNamespace​(java.lang.String prefix, java.lang.String uri)
Adds a namespace to this element for use by its child content
Parameters:
prefix - is the prefix to use, which should not be null or blank
One thing you could try is create an instance of Namespace passing a empty String to the constructor and use the Element.add() method instead. The Javadoc of Namespace doesn't indicate that the prefix can't be blank.

Related

Two Xmls input, One output with XSL transform

I'm trying to write an XSL that basically need to take some values from one xml and other from another and output a XML. I've searched online for some solution and I found that I've to put this <xsl:variable name='file' select="'file:///C:/Users/file.xml'"> inside my input XML which is supposed to load another XML and store it into a variable but from this I dont know how to get the tags value of the document.
The file.xml is this one
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<silosMediaObject>
<canBeDeleted>-1</canBeDeleted>
<checkedOut>-1</checkedOut>
<checkedOutBy>-1</checkedOutBy>
<deleted>-1</deleted>
<description>Traccia audio migrata da ASCN</description>
<externalResourcePath>TEST/ASCN/lq/3763_2015-05-05.mp3</externalResourcePath>
<fileName>3763_2015-05-05.mp3</fileName>
<framesPerSecond>-1</framesPerSecond>
<hasScheduledIngestion>false</hasScheduledIngestion>
<isArchived>-1</isArchived>
<isArchiving>-1</isArchiving>
<isAvailable>-1</isAvailable>
<isEncoding>-1</isEncoding>
<isRestoring>-1</isRestoring>
<isVerified>-1</isVerified>
<mediaObjectId>-1</mediaObjectId>
<mediaTypeId>-1</mediaTypeId>
<mosId>4347</mosId>
<resourceIsExternal>-1</resourceIsExternal>
<sourceMediaObjectId>-1</sourceMediaObjectId>
<state>AVAILABLE</state>
<versionLinkId>-1</versionLinkId>
</silosMediaObject>
The Java class I'm using to transform the file is this one:
public class TestMain {
public static void main(String[] args) throws IOException, URISyntaxException, TransformerException {
TransformerFactory factory = TransformerFactory.newInstance();
Source xslt = new StreamSource(new File("C:\\Users\\xmltemplate_transformer.xsl"));
Transformer transformer = factory.newTransformer(xslt);
Source text = new StreamSource(new File("C:\\Users\\tobe_transformed.xml"));
transformer.transform(text, new StreamResult(new File("C:\\Users\\out.xml")));
}
}
I've searched online for some solution and I found that I've to put this <xsl:variable name='file' select="'file:///C:/Users/file.xml'"> inside my input XML which is supposed to load another XML and store it into a variable
I don't know where you got that idea, but you're confused. The select value is interpreted as an XPath expression. Yours is a string literal containing a URL with the file scheme. As far as XPath or XSLT is concerned, it is just a string. One might do something further to cause the file designated by that URL to be parsed, but what you've presented has no such effect.
In particular, you might have wanted to do this:
<xsl:variable name='file' select="document('file:///C:/Users/file.xml')"/>
The document() function is the secret sauce that actually causes the designated file to be read and parsed (if possible); when used as shown, its result is a node set containing the root node of the resulting document, or an empty node set if the designated document cannot be parsed and the processor elects not to signal an error.
Note: when you say you put the xsl:variable "inside my input XML", I presume you mean at an appropriate place inside your (XML-based) XSL stylesheet. If you actually mean that you have placed it in a different XML data file that you are processing, then it will have no direct effect there, other than to be included, as itself, in the input tree.
but from this I dont know how to get the tags value of the document.
Having successfully parsed the file, you can use the resulting node set anywhere that XSLT expects an expression that evaluates to a node set. In particular, within its scope, you can use a reference to the variable you've defined ($file) as an argument to XPath functions, or as a whole expression, such as the select expression of an xsl:apply-templates. Since you haven't said what, specifically, you want to do with the contents, I cannot be any more specific myself. See what you can do, and if you can't figure out the details then that could be a suitable topic for a new question.

Creating namespace prefixed XML nodes in Java DOM

I am creating several XML files via Java and up to this point everything worked fine, but now I've run into a problem when trying to create a file with namespace prefixed nodes, i.e, stuff like <tns:node> ... </tns:node> using a refactored version of my code that's already working for normal xml files without namespaces.
The error getting thrown is:
org.w3c.dom.DOMException: INVALID_CHARACTER_ERR: Ungültiges XML-Zeichen angegeben.
Sorry for the German in there, it says "invalid XML-sign specified".
The codeline where the error occurs:
Element mainRootElement = doc.createElement("tns:cmds xmlns:tns=\"http://abc.de/x/y/z\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://abc.de/x/y/z xyzschema.xsd\"");
To eliminate the possibility of the error resulting in escaping that rather long string or something among those lines I also tried just using Element mainRootElement = doc.createElement("tns:cmds");, however, this results in the same error.
That's why I figure it has something to do with the namespace declaration, i.e., the : used to do it, as that's the only "invalid" character I could think of in that string.
Can anyone confirm this is the source of the problem? If so, is there an easy solution to it? Can Java DOM use namespaced tags at all?
Edit: Whole method for reference
private void generateScriptXML()
{
DocumentBuilderFactory icFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder icBuilder;
try
{
icBuilder = icFactory.newDocumentBuilder();
Document doc = icBuilder.newDocument();
Element mainRootElement = doc.createElement("tns:cmds xmlns:tns=\"http://abc.de/x/y/z\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://abc.de/x/y/z xyzschema.xsd\"");
doc.appendChild(mainRootElement);
mainRootElement.appendChild(getAttributes(doc,"xxx", "yyy", "zzz"));
mainRootElement.appendChild(getAttributes(doc,"aaa", "bbb", "ccc"));
mainRootElement.appendChild(getAttributes(doc,"ddd", "eee", "fff"));
...
...
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(doc);
StreamResult streamResult = new StreamResult(new File(vfsPath));
transformer.transform(source, streamResult);
}
catch (Exception e)
{
e.printStackTrace();
}
}
Wrong method, try the *NS variants:
Element mainRootElement = doc.createElementNS(
"http://abc.de/x/y/z", // namespace
"tns:cmds" // node name including prefix
);
First argument is the namespace, second the node name including the prefix/alias. Namespace definitions will be added automatically for the namespace if needed. It works to set them as attributes, too.
The namespace in your original source is http://abc.de/x/y/z. With the attribute xmlns:tns="http://abc.de/x/y/z" the alias/prefix tns is defined for the namespace. The DOM api will implicitly add namespaces for nodes created with the *NS methods.
xmlns and xml are reserved/default namespace prefixes for specific namespaces. The namespace for xmlns (namespace definitions) is http://www.w3.org/2000/xmlns/.
To add an xmlns:* attribute with setAttributeNS() use the xmlns namespace:
mainRootElement.setAttributeNS(
"http://www.w3.org/2000/xmlns/", // namespace
"xmlns:xsi", // node name including prefix
"http://www.w3.org/2001/XMLSchema-instance" // value
);
But even that is not needed. Just like for elements, the namespace definition will be added implicitly if you add an attribute node using it.
mainRootElement.setAttributeNS(
"http://www.w3.org/2001/XMLSchema-instance", // namespace
"xsi:schemaLocation", // node name including prefix
"http://abc.de/x/y/z xyzschema.xsd" // value
);
Namespaces Prefixes
If you see a nodename like xsi:schemaLocation you can resolve by looking for the xmlns:xsi attribute. This attribute is the namepace definition. The value is the actual namespace. So if you have an attribute xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" the node name can be resolved to {http://www.w3.org/2001/XMLSchema-instance}schemaLocation (Clark notation).
If you want to create the node you need 3 values:
the namespace: http://www.w3.org/2001/XMLSchema-instance
the local node name: schemaLocation
the prefix: xsi
The prefix is optional for element nodes, but mandatory for attribute nodes. The following three XMLs resolve all to the element node name {http://abc.de/x/y/z}cmds:
<tns:cmds xmlns:tns="http://abc.de/x/y/z"/>
<cmds xmlns="http://abc.de/x/y/z"/>
<other:cmds xmlns:other="http://abc.de/x/y/z"/>

how to pass all parameters of input XML document into output XML document

general task is XML document processing. It'd be nice to have all input XML parameters usually given as <p:MyDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="urn:where/to/look/4/schema" schemaVersion="1.3">
passed to an output XML.
More specifically, how to get them from the input XML?
I can pass them (if I know them in advance) as
Element rootElement = document.createElementNS("urn:hard/coded", root);
rootElement.setAttribute("schemaVersion", "myWish");
You can get all the attribute details using document.getAttributes()

Parsing 'pseudo' XML (that is, not well formed) in java?

I have some xml that looks like this:
<xml><name>oscar</name><race>puppet</race><class>grouch</class></xml>
The tags change and are variable, so there won't always be a 'name' tag.
I've tried 3 or 4 parses and they all seem to choke on it. Any hints?
Just because it doesn't have a defined schema, doesn't mean it isn't "valid" XML - your sample XML is "well formed".
The dom4j library will do it for you. Once parsed (your XML will parse OK) you can iterate through child elements, no matter what their tag name, and work with your data.
Here's an example of how to use it:
import org.dom4j.*;
String text = "<xml><name>oscar</name><race>puppet</race><class>grouch</class></xml>";
Document document = DocumentHelper.parseText(text);
Element root = document.getRootElement();
for ( Iterator i = root.elementIterator(); i.hasNext(); ) {
Element element = (Element) i.next();
String tagName = element.getQName();
String contents = element.getText();
// do something
}
This is valid xml; try adding an XML Schema that allows for optional elements. If you can write an xml schema, you can use JAXB to parse it. XML allows for having optional elements; it isn't too "strict" about it.
Your XML sample is well-formed XML, and if anything "chokes" on it then it would be useful for us to know exactly what the symptoms of the "choking" are.

XML parsing problem

I'm having this strange XML parsing problem.
I have this XML string I'm trying to parse
<?xml version="1.0"?>
<response status="success">
<lot>32342</lot>
</response>
I'm using XPath with Java in order to do this. I'm using the Xpath expression "/response/#status" to find the text "success". However whenever I evaluate this expression I get an empty string.
However I am able to successfully parse this string using "/response/#type"
<?xml version="1.0"?>
<response type="success">
<lot>32342</lot>
</response>
So why would simply changing the name of the attribute change the return string to nothing?
is = new InputSource(new StringReader(testWOcreateStrGood));
xPathexpressionSuccess = xPath.compile("/response/#status");
responseStr = xPathexpressionSuccess.evaluate(is);
reponseStr is the string I posted earlier with the "status" attribute
Also I declared testWOcreateStrGood as below
private String testWOcreateStrGood = "<?xml version=\"1.0\"?>\n" +
"<response status=\"success\">\n" +
"<lot>32342</lot>\n" +
"</response>";
So why would simply changing the name of the attribute change the return string to nothing?
It shouldn't. You must be doing something else wrong, e.g. accessing the wrong XML document or not actually using the XPath expression you believe to be using.
To your code example:
Check the API documentation for InputSource. You cannot pass an XML document as a string directly to the constructor.

Categories

Resources