<cars>
<car>
<name v="speedy"/>
<type v="sport"/>
<engine>
<hp>300</hp>
</engine>
<car>
<car>
<name v="biggo"/>
<type v="truck"/>
<engine>
<hp>190</hp>
</engine>
<car>
</cars>
I have a problem in building a xpath-term that gives my biggos horsepower.
I am not sure how to filter and get the value of something that is not in the filtered element.
My current XPath returns ancestor-or-self nodes matching criteria.
//*[contains(., '"& v_search &"')]/ancestor-or-self::*/*[local-name()='name' and #locale='en']
Now I want to implement addition feature to return following-sibling from the matching nodes. I have changed my XPath as below. But it doesn't return ancestor-or-self plus sibling in result.
//*[contains(., '"& v_search &"')]/ancestor-or-self::*/following-sibling::*/*[local-name()='name' and #locale='en']
here v_search is a variable that can be replaced by any string for testing.
My xml is like :-
<root xmlns="https://jlkjsdlfjl/">
<name>Accounts</name>
<property name="included" type="hidden">true</property>
<locales>
<locale>en</locale>
<locale>de</locale>
</locales>
<defaultLocale>en</defaultLocale>
<searchspace>
<name locale="en">Accounts</name>
<name locale="de">Accounts</name>
<lastChanged>2014-03-05T18:47:30</lastChanged>
<lastChangedBy>userx</lastChangedBy>
<property name="included" type="hidden">true</property>
<searchspace>
<name locale="en">Database L</name>
<name locale="zw">Database L</name>
<searchSubject status="valid">
<name locale="en">SName1</name>
<name locale="zw">qskxyz</name>
<searchItem>
<name locale="en">IName1</name>
<name locale="zw">qixyz</name>
<hello>v_search</hello>
</searchItem>
<searchItem>
<name locale="en">IName2</name>
<name locale="zw">abc</name>
v_search
</searchItem>
<searchItem>
<name locale="en">IName3</name>
<name locale="zw">def</name>
<hello>something else</hello>
</searchItem>
</searchSubject>
</searchspace>
</searchspace>
<searchspace>
<name locale="en">Names</name>
<lastChanged>2016-01-12T12:42:46</lastChanged>
<searchspace>
<name locale="en">Database Layer</name>
<name locale="zw">Database Layer</name>
<searchSubject status="valid">
<name locale="en">SName2</name>
<searchItem>
<name locale="en">IName4</name>
<hello>...Hi there..</hello>
</searchItem>
</searchSubject>
</searchspace>
</searchspace>
</root>
v_search is sample keyword, I want to return sibling of lowest matching nodes.
i.e. sibling of self from ancestor-or-self.
The easiest way might be to introduce 2nd expression that will return 'following-sibling' of the matched element. You can combine the 2nd expression with the existing one using union (|) operator as follow (wrapped for readability) :
//*[contains(., 'v_search')]
/ancestor-or-self::*
/*[local-name()='name' and #locale='en']
|
//*[text()[contains(.,'v_search')]]
/following-sibling::*
/*[local-name()='name' and #locale='en']
Brief test : http://www.xpathtester.com/xpath/294a1ef49d30eb6abecf2f024cfcd318
My requirement is to search "using System.Resources;" in SonarQube. I can use the following XPath to search the keyword "Resources".
//*[#tokenValue='Resources']
But if I change to this pattern, it doesn't work. I think the reason is the dot, but I have no idea how to search it in XPath. Is there an escape character for it? I use "\", but not work.
//*[#tokenValue='System.Resources']
Update: I think the reason is 'System.Resources' are separated into 2 levels in XML. But still not know how to search the pattern by XPath.
<NAMESPACE_NAME tokenValue="System" tokenLine="3" tokenColumn="6">
<NAMESPACE_OR_TYPE_NAME tokenValue="System" tokenLine="3" tokenColumn="6">
<IDENTIFIER tokenValue="System" tokenLine="3" tokenColumn="6"/>
<DOT tokenValue="." tokenLine="3" tokenColumn="12"/>
<IDENTIFIER tokenValue="Resources" tokenLine="3" tokenColumn="13"/>
</NAMESPACE_OR_TYPE_NAME>
</NAMESPACE_NAME>
The string System.Resources is spread across three different elements in your input XML. That is why an expression like
//*[#tokenValue='Resources']
can never find it, because there is no single IDENTIFIER element that has both "System" and "Resources" as attribute values. So, you have to look for something else. Why not a NAMESPACE_OR_TYPE_NAME element? Use for example:
//NAMESPACE_OR_TYPE_NAME[IDENTIFIER/#tokenValue = 'System' and IDENTIFIER/#tokenValue = 'Resources']
I am not sure whether there is any significance to the dot .in between "System" and "Resources", but this expression just ignores it.
To illustrate all this, there is an XSLT stylesheet below.
XML Input
<?xml version="1.0" encoding="UTF-8"?>
<NAMESPACE_NAME tokenValue="System" tokenLine="3" tokenColumn="6">
<NAMESPACE_OR_TYPE_NAME tokenValue="System" tokenLine="3" tokenColumn="6">
<IDENTIFIER tokenValue="System" tokenLine="3" tokenColumn="6"/>
<DOT tokenValue="." tokenLine="3" tokenColumn="12"/>
<IDENTIFIER tokenValue="Resources" tokenLine="3" tokenColumn="13"/>
</NAMESPACE_OR_TYPE_NAME>
</NAMESPACE_NAME>
Stylesheet
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="UTF-8"/>
<xsl:template match="NAMESPACE_OR_TYPE_NAME[IDENTIFIER/#tokenValue = 'System' and IDENTIFIER/#tokenValue = 'Resources']">
<bingo/>
</xsl:template>
</xsl:transform>
Output
<bingo/>
Update: I appreciate all the help. I was able to get the Process elem tags to work with the help from you guys but my boss has a new requirement. I have to move elements to a new place (instead of removing them) and properly put elem numbers. Thanks again, you guys are the best!
Previous Problem:
I am new to xslt so please bear with me. So here is the problem. I
have this xml:
<Process>
<elem0>
<pcode>xx<pcode>
</elem0>
<elem1>
<pcode>xy<pcode>
</elem1>
<elem2>
<pcode>ab<pcode>
</elem2>
<elem3>
<pcode>AD<pcode>
</elem3>
</Process>
And I have to erase elements with pcode value='xy', which I am doing
it successfully with xslt. However, after doing that you will see
there is a gap in element (elem0 elem2 elem3) names.
<Process>
<elem0>
<pcode>xx<pcode>
</elem0>
<elem2>
<pcode>ab<pcode>
</elem2>
<elem3>
<pcode>AD<pcode>
</elem3>
</Process>
I would like for it to be
<Process>
<elem0>
<pcode>xx<pcode>
</elem0>
<elem1>
<pcode>ab<pcode>
</elem1>
<elem2>
<pcode>AD<pcode>
</elem2>
</Process> so it shows up properly in front end, but I am stuck. Tried sorting but didn't work. elem identifications are
changing so it is harder for me to use a template of some sort. Thanks
in advice!
UPDATED: with new requirement to move some elements to a new place, instead of removing them. I apologize for the inconvenience and appreciate all of your help in advance.
I have this xml:
<Process>
<elem0>
<pcode>xx<pcode>
</elem0>
<elem1>
<pcode>xy<pcode>
</elem1>
<elem2>
<pcode>ab<pcode>
</elem2>
<elem3>
<pcode>AD<pcode>
</elem3>
</Process>
And I have to MOVE(erase-old requirement) elements with pcode value='xy'to EdProcess, which I am doing it successfully with xslt. However, after doing that you will see there is a gap in element (elem0 elem2 elem3) names. Also, EdProcess needs to start at elem0, and any new elements that gets moved should be in order, i.e elem0, elem1, elem2, etc.
<Process>
<elem0>
<pcode>xx<pcode>
</elem0>
<elem2>
<pcode>ab<pcode>
</elem2>
<elem3>
<pcode>AD<pcode>
</elem3>
</Process>
<EdProcess>
<elem1>
<pcode>xy<pcode>
</elem1>
</EdProcess>
I would like for it to be
<Process>
<elem0>
<pcode>xx<pcode>
</elem0>
<elem1>
<pcode>ab<pcode>
</elem1>
<elem2>
<pcode>AD<pcode>
</elem2>
</Process>
<EdProcess>
<elem0>
<pcode>xy<pcode>
</elem0>
</EdProcess>
so it shows up properly in front end, but I am stuck. Tried sorting but didn't work. elem identifications are changing so it is harder for me to use a template of some sort. Thanks in advice!
Assuming your source format doesn't have other elements intermingled with the elems, there's a very simple and straightforward way to do this that doesn't require preceding/following-sibling or a double-pass:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="*[pcode != 'xy']" mode="elems" />
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="elems">
<xsl:element name="elem{position() - 1}">
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When applied to this input:
<Process>
<elem0>
<pcode>xx</pcode>
</elem0>
<elem1>
<pcode>xy</pcode>
</elem1>
<elem2>
<pcode>ab</pcode>
</elem2>
<elem3>
<pcode>xy</pcode>
</elem3>
<elem4>
<pcode>AD</pcode>
</elem4>
</Process>
Produces:
<Process>
<elem0>
<pcode>xx</pcode>
</elem0>
<elem1>
<pcode>ab</pcode>
</elem1>
<elem2>
<pcode>AD</pcode>
</elem2>
</Process>
Since XSLT doesn't do updateable variables the most straightforward approach I can see to this is a recursive template that deals with one sibling at a time
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<Process>
<!-- start with the first child -->
<xsl:apply-templates select="Process/*[pcode != 'xy'][1]" />
</Process>
</xsl:template>
<xsl:template match="*">
<xsl:param name="nextIndex" select="0" />
<xsl:element name="elem{$nextIndex}">
<xsl:copy-of select="node()" />
</xsl:element>
<!-- process the next applicable sibling, if any, incrementing the index -->
<xsl:apply-templates select="following-sibling::*[pcode != 'xy'][1]">
<xsl:with-param name="nextIndex" select="$nextIndex + 1" />
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
You may need to use a two-pass XSLT pattern, and rename all the elements in the second pass:
<xsl:template match="/">
<xsl:variable name="pass1">
<xsl:apply-templates select="Process"/>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($pass1)" mode="pass2" />
<xsl:template>
<xsl:template match="Process" mode="pass2">
<xsl:copy>
<xsl:for-each select="*">
<xsl:element name="{ concat('elem', position()) }">
<xsl:apply-templates />
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
Note that for XSLT 1, you need to use the node-set() function, which is included in most XSLT 1 processors, but you will need to include the namespace declaration at the top of your stylesheet. For ESLT-aware processors (Saxon, xsltproc, Xalan-J, jd.xslt, and 4XSLT) use xmlns:exsl="http://exslt.org/common" and exsl:node-set(). For Xalan-C use xmlns:xalan="http://xml.apache.org/xalan" and xalan:nodeset(). For MSXSL use xmlns:msxsl="urn:schemas-microsoft-com:xslt" and msxsl:node-set().
You can use the preceding-sibling axis to count the number of elements that appear before your current elem* element. Then subtract the number that would have been deleted, and you have your current number:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Process/*">
<!-- only copy if not "xy" -->
<xsl:if test="pcode!='xy'">
<!-- number of preceding elements which are also not "xy" -->
<xsl:element name="elem{count(preceding-sibling::*[pcode!='xy'])}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
That said, this is a terrible XML format and you should change this format if you can so it doesn't have unknown element names like elem0. If you need to know an element's position in a list, you can easily get it with position(), count(preceding-sibling::*), etc. If you really need an explicit number, put it in an attribute, not in the element name, e.g. <elem num="1">.
Here is a one-pass solution that correctly processes elemN elements that can be at any place in the tree -- not only siblings. None of the other answers processes such XML document correctly.
It is fairly efficient -- in the case when the tree is with limited depth its efficiency is close to linear (O(N)). It is linear if all such elements are siblings:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:param name="pCount" select="0"/>
<xsl:copy>
<xsl:apply-templates select="node()[1]|#*">
<xsl:with-param name="pCount" select="$pCount"/>
</xsl:apply-templates>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pCount" select=
"$pCount + count(.//*[starts-with(name(), 'elem') and not(pcode='xy')])"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'elem') and not(pcode='xy')]">
<xsl:param name="pCount" select="0"/>
<xsl:element name="elem{$pCount}">
<xsl:apply-templates select="node()[1]|#*">
<xsl:with-param name="pCount" select="$pCount+1"/>
</xsl:apply-templates>
</xsl:element>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pCount" select=
"$pCount+1+ count(.//*[starts-with(name(), 'elem') and not(pcode='xy')])"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'elem') and pcode='xy']">
<xsl:param name="pCount" select="0"/>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pCount" select="$pCount"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<Process>
<a/>
<elem0>
<pcode>xx</pcode>
<elem1>
<pcode>xy</pcode>
</elem1>
<elem2>
<pcode>xz</pcode>
</elem2>
</elem0>
<b/>
<elem3>
<pcode>xy</pcode>
</elem3>
<c/>
<elem4>
<pcode>ab</pcode>
</elem4>
<d/>
<elem5>
<pcode>xy</pcode>
</elem5>
<e/>
<elem6>
<pcode>AD</pcode>
</elem6>
<f/>
</Process>
the wanted, correct result is produced:
<Process>
<a/>
<elem0>
<pcode>xx</pcode>
<elem1>
<pcode>xz</pcode>
</elem1>
</elem0>
<b/>
<c/>
<elem2>
<pcode>ab</pcode>
</elem2>
<d/>
<e/>
<elem3>
<pcode>AD</pcode>
</elem3>
<f/>
</Process>
i need help with the xpath query to extract the title text and thumbnail url of each entry in the following rss results
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/'>
<entry>
<title>LiveFit Augusta - "Gym Time"</title>
<media:group>
<media:thumbnail url='http://i.ytimg.com/vi/Ig7CcLCR2n4/default.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/Ig7CcLCR2n4/mqdefault.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/Ig7CcLCR2n4/hqdefault.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/Ig7CcLCR2n4/1.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/Ig7CcLCR2n4/2.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/Ig7CcLCR2n4/3.jpg'/>
</media:group>
</entry>
<entry>
<title>LiveFit Augusta - "Everyday Joes & Janes"</title>
<media:group>
<media:thumbnail url='http://i.ytimg.com/vi/REhn9jjjV7Q/default.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/REhn9jjjV7Q/mqdefault.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/REhn9jjjV7Q/hqdefault.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/REhn9jjjV7Q/1.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/REhn9jjjV7Q/2.jpg'/>
<media:thumbnail url='http://i.ytimg.com/vi/REhn9jjjV7Q/3.jpg'/>
</media:group>
</entry>
</feed>
the title
< title type='text'>Evolution of Dance< /title>
or
< media:title type='plain'>Evolution of Dance< /media:title>
thumbnail
<media:thumbnail
url='http://img.youtube.com/vi/dMH0bHeiRNg/1.jpg' height='97' width='130'
time='00:01:30'/>
EDIT
here is the code am using
Dim xmlDoc As MSXML2.DOMDocument30
Dim xmlNode As MSXML2.IXMLDOMNode
Dim xmlEntryNodes As IXMLDOMNodeList
Dim ns As String
Set xmlDoc = New DOMDocument30
ns = "xmlns:x='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/'"
xmlDoc.setProperty "SelectionLanguage", "XPath"
xmlDoc.setProperty "SelectionNamespaces", ns
If xmlDoc.loadXML(xmlText) = False Then
Exit Function
End If
Set xmlEntryNodes = xmlDoc.documentElement.selectNodes("/feed/entry")
debug.print xmlEntryNodes.Length returns 0
Use:
ns = "xmlns:x='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/'"
doc.SetProperty("SelectionNamespaces", ns)
And then use an XPath expression like this:
/*/x:entry/x:title[. = 'Evolution of Dance']
and this:
/*/x:entry/media:group/media:thumbnail[#url='http://img.youtube.com/vi/dMH0bHeiRNg/1.jpg']
Learn more about the SetProperty() function here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms756048(v=vs.85).aspx
Alternatively, if you don't want to register namespaces, you may use less-readable expressions like these:
/*/*[name()='entry']/*[name()='title'][. = 'Evolution of Dance']
and this:
/*/*[name()='entry']
/*[name()='media:group']
/*[name()='media:thumbnail']
[#url='http://img.youtube.com/vi/dMH0bHeiRNg/1.jpg']
Is this what you are looking for?
title
/feed/entry/title
media:title
/feed/entry/media:group/media:title
thumbnail
/feed/entry/media:group/media:thumbnail