Xpath for item with same attribute value as ancestor - java

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)]

Related

How to identify which element belongs to which node in XSL

<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.

How to get last 5 elements from an xml file using java

I have a XML file in the following format:
<CodeSnippet>
<Code tag="class eclipse">
<Snippet>sample</Snippet>
<TimeStamp>05/11/2014 13:50:36</TimeStamp>
</Code>
<Code tag="button java">
<Snippet>Sample code</Snippet>
<TimeStamp>05/11/2014 12:36:36</TimeStamp>
</Code>
.
.
.
</CodeSnippet>
I would like to know, how to retrieve the last 5 child nodes of the root node "CodeSnippet" and display the text inside the tag "Snippet".
Get a learning of using XPATH expressions in java here http://howtodoinjava.com/2012/11/07/how-to-work-with-xpaths-in-java-with-examples/
For your specific problem get all the snippet nodes using xpath expression //CodeSnippet/Snippet. Once done run a for loop from nodes.length to nodes.length-5.
Try something like:
XSLT 1.0
<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:template match="/">
<root>
<xsl:for-each select="CodeSnippet/Code[position() > last() - 5]">
<xsl:copy-of select="Snippet"/>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
You didn't say what the exact format of output should be. The above will result in an XML document in the form of:
<?xml version="1.0" encoding="utf-8"?>
<root>
<Snippet>V</Snippet>
<Snippet>W</Snippet>
<Snippet>X</Snippet>
<Snippet>Y</Snippet>
<Snippet>Z</Snippet>
</root>
This is a good tutorial of XML Parsing for Java using three different technologies. In your case I would recommend you the DOM option, even if I see SAX the easiest one. Just try to make the changes you need for your case.
http://www.javacodegeeks.com/2013/05/parsing-xml-using-dom-sax-and-stax-parser-in-java.html
Have a nice code!
If you can use an XPath 2.0 processor, you could use the following XPath:
subsequence(//Snippet,count(//Snippet)-5)

Loop logic for creating a formatted particular xml format

How do I construct the for loop for this below logic in Java. I have a list of items that i need to end that with the KT1 tag for every KT2 item. Please let me know what is the for loop logic to apply
This is the format that i have as
Input
<KT1>
<KT2>
String1
</KT2>
<KT2>
String2
</KT2>
.
.
.
</KT1>
Expected output
<KT1>
<KT2>
String1
</KT2>
</KT1>
<KT1>
<KT2>
String2
</KT2>
</KT1>
.
.
.
</KT1>
OK, first up, to use XSLT with Java, see the answer to this question:
java xslt tutorial
The XSLT you would need is as follows (disclaimer: I'm learning XSLT myself so, if anyone has any better solutions, please post them and comment here!)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Template 1 (see below) -->
<xsl:template match="/">
<xsl:element name="Root">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<!-- Template 2 (see below) -->
<xsl:template match="KT2">
<xsl:element name="KT1">
<xsl:copy>
<xsl:apply-templates select="text()" />
</xsl:copy>
</xsl:element>
</xsl:template>
<!-- Template 3 (see below) -->
<xsl:template match="text()">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>
The results of applying this XSLT to your input XML are as follows:
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<KT1>
<KT2>String1</KT2>
</KT1>
<KT1>
<KT2>String2</KT2>
</KT1>
.
.
.
</Root>
My understanding of the way XSLT works is that it will apply templates in order of specificity. A dry run through the XLST above would be as follows:
1) The root of the XML document matches exactly to Template 1 - this tells it to output a root node and then carry on processing other elements
2) The first KT1 element doesn't match to any templates, so is not output
3) The first KT2 child of the first KT1 element matches to Template 2 - this tell it output a KT1 node, and then copy the found KT2 node and then to continue processing
4) The text of the KT2 node matches to Template 3, which just copies it out
5) The second KT2 child of the first KT1 element matches to Template 2 - and so on
The XSLT above is quite specific, it can easily be made more general - google "XSLT Identity Transforms" for a good start.

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.

Match document root node with a prefix

I have a XML document which root node is sv:node and I am trying to define a template who match this root node.
When my root node has no prefix it usually works but in this case an exception is thrown.
<xsl:template name="/sv:node" />
I am using JAVA with the Apache XALAN XSLT processor which raises a RuntimeException
Caused by: java.lang.RuntimeException: Le préfixe doit se convertir en espace de noms : /sv
at org.apache.xml.utils.QName.<init>(QName.java:450)
at org.apache.xalan.processor.XSLTAttributeDef.processQNAME(XSLTAttributeDef.java:937)
I apologize about the localized message of the RuntimeException, basically it is complaining about the prefix with is not mapped to a namespace.
So the question is, how can I match this root node?
Edit : Here is my stylesheet tag
Here is my stylesheet tag :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:zip="http://apache.org/cocoon/zip-archive/1.0"
xmlns:sv="http://www.jcp.org/jcr/sv/1.0">
I suspect you really meant
<xsl:template match="/sv:node" />
(with match rather than name). Using <xsl:template name="..."> declares a named template which can be called by <xsl:call-template>, not a matching template for <xsl:apply-templates>, and the name of a named template must be a QName. The exception is telling you that it's trying to treat the three characters "forward-slash, s, v" as the prefix part of the QName and finding that this prefix is not mapped to a URI.
It was a silly mistake, I was using
<xsl:template name="/sv:node" />
instead of
<xsl:template match="/sv:node" />

Categories