How to identify which element belongs to which node in XSL - java

<main>
<root>
<SubRoot>
<type>R</type>
<mand>N</mand>
<Section>B</Section>
</SubRoot>
<SubRoot>
<type>P</type>
<mand>Y</mand>
<Section>A</Section>
</SubRoot>
</root>
</main>
I have above XML file on this file How i can identify that type R or any other element belongs to which node,here it's in first SubRoot node.
In a xsl file i am calling a template if section have value as A and this section is in second SubRoot node as below,
Now i want to access the some other value from second SubRoot node inside the when condition.How can i do that?
<xsl:choose>
<xsl:when test="(/main/root/SubRoot[Section = 'A'])">
//Call some template
</xsl:when>
<xsl:otherwise>
//some template
</xsl:otherwise>
</xsl:choose>
I need to verify it dynamically on XSL.Because i am not aware at run time how many nodes will get generated for the XML.
If Section element having value as A then its inside second SubRoot node.On this basis how can i access the value of other element from second SubRoot Node.Please also help to tell me the performance issue with the suggested approach.
Any Idea Suggestion must be appreciated.

In your example you are not testing whether something is in a SubRoot with Section A, but you are testing whether a SubRoot with Section A exists, that's a huge difference.
I guess what you need is something like parent::SubRoot[Section/text() = 'A'] which will test whether the direct parent tag is a SubRoot that has a Section with text A.
Or maybe something like ancestor::SubRoot[Section/text() = 'A'] which will test whether any parent, great-parent, and-so-on tag is a SubRoot that has a Section with text A.
Or maybe something like ancestor-or-self::SubRoot[Section/text() = 'A'] which will test whether the node itself or any parent, great-parent, and-so-on tag is a SubRoot that has a Section with text A.

Related

keep only wanted elements in xml with the given list of xpath

I have some complex xml structure. Some times i want to keep only the elements match my runtime list of xpath entries.
Sample xml
<Employee>
<Address>
<addressLine1>Dummy Line 1</addressLine1>
<zip>535270</zip>
</Address>
<Department>
<id>102</id>
<name>development</name>
</Department
</Employee>
Sample xpath entries can be some time like
//Employee/Address
//Employee/Department/
//Employee/Department/name
In the above xpath if you observe we have Department and Name inside department then in that case I can ignore Department.Also above xpath entries can be as below too
//Employee/Address
//Employee/Department/name
Resultant xml i want as below
<Employee>
<Address>
<addressLine1>Dummy Line 1</addressLine1>
<zip>535270</zip>
</Address>
<Department>
<name>development</name>
</Department
</Employee>
I realized I can achieve this through xslt. So I want the xslt for this kind of generic requirement. Also my current code is in java. Is there any better alternative in java??
I have to admit that I do not fully understand your requirements, but what I do see is that:
You have a set of XPath
You seem to want to get the union of these XPath statements if applied to your input document
Duplicates removed (just like XPath union expression)
Some magic about certain elements that may appear even if they are not in the XPath statement list.
My original reflex was: use xsl:evaluate, but considering you are rooting all your XPath expressions, this may not give you the results you want. Also, it requires an XSLT 3.0 processor.
With XSLT 2.0, you could do something like this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:strip-space elements="*" />
<xsl:output indent="yes" />
<xsl:variable name="patterns" as="xs:string*">
<xsl:sequence select="(
'foo/bar',
'foo/test',
'foo/bar/zed')" />
</xsl:variable>
<xsl:template match="node()[true() = (
for $p in $patterns
return ends-with(
string-join(current()/ancestor-or-self::*/name(),
'/'), $p))]">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
It is just meant to get you started, it is not meant as a full-fledged solution. It matches XPath of the production //QName/QName, like the one in your example. I removed the trailing //, and simply match when the current path matches any of the paths (taking into account an implicit descendent-or-self, as in your example).
You probably want to wrap the for-expression in a function and call the function to map the concatenation of the current path to any of your paths in your list.
In its current form, you will also need to supply the paths leading up to a deeper path, or you will have to implement an fn:snapshot-like function to copy the ancestor nodes as well.
Anyway, I think this is a simple enough approach to mimic not necessarily xsl:evaluate, but mimic pattern matching based on a path, as your question seems to imply.

Xalan and the document() function

Since I have spent almost a full day now on debugging this, I hope to get some valuable insight on SO on following problem:
I am running an XSL Transformation on an input document, my stylesheet loads an external XML-Document which contains lookup values I need to do some comparisons.
I am loading the external document like this:
<xsl:variable name="dictionary"
select="document('myDict.xml', document(''))/path/to/LookupElement" />
LookupElement is an element which contains the complete XML-Fragment I need to access.
Throughout the stylesheet various comparison expressions are accessing $dictionary.
Now, what happens is, that the transformation with this document() function call in place takes about 12 (!) minutes using Xalan (2.7.?, latest version, downloaded from the Apache website, not the one contained in the JRE).
The same stylesheet without the document() call (and without my comparisons accessing data in $dictionary) completes in seconds.
The same stylesheet using Saxon-B 9.1.0.8 completes in seconds as well.
Information: The external document has 25MB(!) and there is no possibility for me to reduce its size.
I am running the transformations using the xslt-Task of ant under JRE 6.
I am not sure if this has anything to do with above mentioned problem: Throughout my stylesheet I have expressions that test for existence of certain attributes in the external XML-Document. These expressions always evaluate to true, regardless of whether the attributes exist or not:
<xsl:variable name="myAttExists" select="boolean($dictionary/path/to/#myAttribute)"/>
I am at the end of my wits. I know that Xalan correctly reads the document, all references go to $dictionary, so I am not calling document() multiple times.
Anybody any idea?
Edit:
I have removed the reference to the XML-Schema from the external XML-Document to prevent Schema-Lookups of Xalan or the underlying (Xerces) Parser.
Edit:
I have verified that myAttExists will always be true, even if specifiying an attribute name that for sure does not exist in the entire external XML-Document.
I have even changed the above expression to:
<xsl:variable name="myAttExists" select="count($dictionary/path/to/#unknownAttribute) != 0"/>
which still yields true.
Edit:
I have removed the call to the document() function and all references to $dictionary for testing purposes. This reduces transformation runtime with Xalan to 16 seconds.
Edit:
Interesting detail: The Xalan version shipped with Oxygen 12.1 completes within seconds loading the external XML-Document. However, it also evaluates the existence of attributes incorrectly...
Edit:
I have the following variable declaration which always yields true:
<xsl:variable name="expectedDefaultValueExists">
<xsl:choose>
<xsl:when test="#index">
<xsl:value-of select="boolean($dictionary/epl:Object[#index = $index]/#defaultValue)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="boolean($dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Is this possible in XSLT/XPath 1.0? $index and $subIndex are calculated from the #index and #subIndex attributes of the context node. I want to load the defaultValue attribute from the external XML-Document which has an equal index and/or subIndex.
Is it possible to use variables in predicates in XPath 1.0? This works in XPath 2.0.
Regarding the incorrect variable assignment, I don't believe in a parser (Xalan) issue anymore, since PHPs XSLTProcessor does the same. It must be an issue in the variable declaration...
This only answers the last part of the question, but it's getting too unwieldy for comments...
I have the following variable declaration which always yields true when used as the test of an xsl:if or xsl:when:
<xsl:variable name="expectedDefaultValueExists">
<xsl:choose>
<xsl:when test="#index">
<xsl:value-of select="boolean($dictionary/epl:Object[#index = $index]/#defaultValue)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="boolean($dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
In XSLT 1.0 a variable with a body rather than a select always becomes a "result tree fragment", in this case with a single text node child that will contain the string "true" or "false" as appropriate. Any non-empty RTF is considered true when converted to boolean.
In XSLT 2.0 it's a similar story - 2.0 doesn't distinguish between node sets and result tree fragments, but still the variable will be a "temporary tree" with a single text node child whose value is the string "true" or "false", and both these trees are true when converted to boolean. If you want to get an actual boolean value out of the variable then you need to change two things - add as="xs:boolean" to the variable declaration and use xsl:sequence instead of xsl:value-of:
<xsl:variable name="expectedDefaultValueExists" as="xs:boolean">
<xsl:choose>
<xsl:when test="#index">
<xsl:sequence select="boolean($dictionary/epl:Object[#index = $index]/#defaultValue)"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="boolean($dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
The xsl:value-of instruction converts the result of its select into a string and constructs a text node containing that string. The xsl:sequence instruction simply returns the value from the select directly as whatever type it happens to be.
But there are simpler ways to achieve the same thing. In XPath 2.0 you can do if/then/else constructs directly in the XPath
<xsl:variable name="expectedDefaultValueExists"
select="if (#index)
then $dictionary/epl:Object[#index = $index]/#defaultValue
else $dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue" />
In 1.0 you need to be slightly more creative
<xsl:variable name="expectedDefaultValueExists"
select="(#index and $dictionary/epl:Object[#index = $index]/#defaultValue)
or (not(#index) and $dictionary/epl:Object[#index = $index]/epl:SubObject[#subIndex = $subIndex]/#defaultValue)" />

Xpath for item with same attribute value as ancestor

How would I write an xpath query to match the <subitem> element in the XML snippet below?
Specifically I want to match any element that has a "name" attribute that matches the value of the "name" attribute of the root. There may be an arbitrary number of ancestors between <root> and <subitem>.
<root name="xyz">
<anything>
<subitem name="xyz" />
</anything>
</root>
My goal is to write a PMD rule for finding cases of Logger.getLogger() that use a different class than the class the statement is in.
Use this XPath expression:
//*[#name = /*/#name]
This selects any element (including the top element named root), whose name attribute has the same string value as that of the name attribute of the top element. When this XPath expression is evaluated against the provided XML document, two elements -- root and subitem are selected.
I think that only the second element of these two is wanted. For this, use the following XPath expression:
//*/*[#name = /*/#name]
XSLT - based verification:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select="//*/*[#name = /*/#name]"/>
========
<xsl:copy-of select="//*[#name = /*/#name]"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<root name="xyz">
<anything>
<subitem name="xyz" />
</anything>
</root>
the two XPath expressions are evaluated and the selected nodes are copied to the output:
<subitem name="xyz"/>
========
<root name="xyz">
<anything>
<subitem name="xyz"/>
</anything>
</root>
<subitem name="xyz"/>
//*[#name=(ancestor::*/#name)]
This should do the trick. I would read it as "Find all nodes that have an ancestor with the same name."
EDIT (plus a second one, it still selected too much)
After the edit of the question:
//*[#name=(/#name)]
This reads as "Find all nodes that have the same name as the root node." Note that the root node might not be what you think it to be (and you want to match against the topmost node named root, not root), and you might need to implement it as
//*[#name=(/root/#name)] or //*[#name=(/*/#name)]
This selects two elements, your subitem and root which is not wanted, you only want to select nodes that are not topmost (are descendants of root). Credits to Dimitre Novatchev for this.
//*/*[#name=(/*/#name)]

java xpath parsing

Is there a way to retrieve from a XML file all the nodes that are not empty using XPath? The XML looks like this:
<workspace>
<light>
<activeFlag>true</activeFlag>
<ambientLight>0.0:0.0:0.0:0.0</ambientLight>
<diffuseLight>1.0:1.0;1.0:1.0</diffuseLight>
<specularLight>2.0:2.0:2.0:2.0</specularLight>
<position>0.1:0.1:0.1:0.1</position>
<spotDirection>0.2:0.2:0.2:0.2</spotDirection>
<spotExponent>1.0</spotExponent>
<spotCutoff>2.0</spotCutoff>
<constantAttenuation>3.0</constantAttenuation>
<linearAtenuation>4.0</linearAtenuation>
<quadricAttenuation>5.0</quadricAttenuation>
</light>
<camera>
<activeFlag>true</activeFlag>
<position>2:2:2</position>
<normal>1:1:1</normal>
<direction>0:0:0</direction>
</camera>
<object>
<material>lemn</material>
<Lu>1</Lu>
<Lv>2</Lv>
<unit>metric</unit>
<tip>tip</tip>
<origin>1:1:1</origin>
<normal>2:2:2</normal>
<parent>
<object>null</object>
</parent>
<leafs>
<object>null</object>
</leafs>
</object>
After each tag the parser "sees" another empty node that i don't need.
I guess what you want is all element nodes that have an immediate text node child that does not consist solely of white space:
//*[string-length(normalize-space(text())) > 0]
If you're using XSLT, use <xsl:strip-space elements="*"/>. If you're not, it depends what technology you are using (you haven't told us), eg. DOM, JDOM, etc.
You want:
//*[normalize-space()]
The expression:
//*[string-length(normalize-space(text())) > 0]
is a wrong answer. It selects all elements in the document whose first text node child's text isn't whitespace-only.
Therefore, this wouldn't select:
<p><b>Hello </b><i>World!</i></p>
although this paragraph contains quite a lot of text...

Getting The XML Data Inside Custom XPath function

Is there a way to get the current xml data when we make our own custom XPath function (see here).
I know you have access to an XPathContext but is this enough?
Example:
Our XML:
<foo>
<bar>smang</bar>
<fizz>buzz</fizz>
</foo>
Our XSL:
<xsl:template match="/">
<xsl:value-of select="ourFunction()" />
</xsl:template>
How do we get the entire XML tree?
Edit: To clarify: I'm creating a custom function that ends up executing static Java code (it's a Saxon feature). So, in this Java code, I wish to be able to get elements from the XML tree, such as bar and fizz, and their CDATA, such as smang and buzz.
Try changing your XSL so you call 'ourFunction(/)'. That should pass the root node to the function. You could also try . or ..
You'll presumably need to change the signature of the implementing function, I'll let someone else help with that.
What about select the current node selecting the relevant data from the current node into an XSL parameter, and passing that parameter to the function? Like:
<xsl:value-of select="ourFunction($data)" />

Categories