XSLT - Checking for Element [duplicate] - c#

Using a XPath query how do you find if a node (tag) exists at all?
For example if I needed to make sure a website page has the correct basic structure like /html/body and /html/head/title.

<xsl:if test="xpath-expression">...</xsl:if>
so for example
<xsl:if test="/html/body">body node exists</xsl:if>
<xsl:if test="not(/html/body)">body node missing</xsl:if>

Try the following expression: boolean(path-to-node)

Patrick is correct, both in the use of the xsl:if, and in the syntax for checking for the existence of a node. However, as Patrick's response implies, there is no xsl equivalent to if-then-else, so if you are looking for something more like an if-then-else, you're normally better off using xsl:choose and xsl:otherwise. So, Patrick's example syntax will work, but this is an alternative:
<xsl:choose>
<xsl:when test="/html/body">body node exists</xsl:when>
<xsl:otherwise>body node missing</xsl:otherwise>
</xsl:choose>

Might be better to use a choice, don't have to type (or possibly mistype) your expressions more than once, and allows you to follow additional different behaviors.
I very often use count(/html/body) = 0, as the specific number of nodes is more interesting than the set. For example... when there is unexpectedly more than 1 node that matches your expression.
<xsl:choose>
<xsl:when test="/html/body">
<!-- Found the node(s) -->
</xsl:when>
<!-- more xsl:when here, if needed -->
<xsl:otherwise>
<!-- No node exists -->
</xsl:otherwise>
</xsl:choose>

I work in Ruby and using Nokogiri I fetch the element and look to see if the result is nil.
require 'nokogiri'
url = "http://somthing.com/resource"
resp = Nokogiri::XML(open(url))
first_name = resp.xpath("/movies/actors/actor[1]/first-name")
puts "first-name not found" if first_name.nil?

A variation when using xpath in Java using count():
int numberofbodies = Integer.parseInt((String) xPath.evaluate("count(/html/body)", doc));
if( numberofbodies==0) {
// body node missing
}

Related

c# How to get attribute name from xslt

I have following part of xslt in string format :-
<xsl:if test="TestValue3 and TestValue3 != ''">
<xsl:attribute name = "TestDate" >
<xsl:value-of select = "TestValue3" />
</xsl:attribute>
</xsl:if>
I just want to fetch its attribute name from c# code.
Attribuute Name= TestDate
How can I achieve this?
Use your favourite XML API to load the XSLT and iterate/query the item in question (in this case you would need to look for the owning xsl:if and the condition itself). e.g. you could load it into an XmlDocument or XDocument.
You can use XPath to find the element for XmlDocuments or if you use XDocument you can use LINQ.
Do not attempt to use technologies that are not equipped for structured data.
i.e.
don't use flat string search
don't use regex
Actually I am adding above block into existing xslt ,, but before adding I need to check if attribute name =TestDate already exists ... For that I need to know attribute name , becase attribute name can vary as per block,, it is not fix each time
Again, use the above recommendations. Both XmlDocument and XDocument allow for load/edit/save.
If I understand you correctly this should do the job, otherwise please add more context.
<xsl:if test="TestValue3 and TestValue3 != ''">
<xsl:if test="not(#TestDate)">
<xsl:attribute name = "TestDate" >
<xsl:value-of select = "TestValue3" />
</xsl:attribute>
</xsl:if>
</xsl:if>

Bind value to xslt variable with choose-when-otherwise

I am using xslt to transform xml to xml with c#. Below is an extract from the xslt where variables assignment is being shown.
<xsl:variable name="testvar">
<xsl:choose>
<xsl:when test="$condition">
<xsl:value-of select="myUtils:Method1($var1,$var2)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="myUtils:Method2($var1,$var2)" /> <!--Method1 and Method 2 are written in c# code.-->
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
The above assignment is different from the below assignment:
<xsl:variable name="testvar" select="myUtils:Method1($var1,$var2)"/>
Another variable depends on the above variable as below:
<xsl:variable name="testvar2" select="$testvar/node()[1]/node()[1]/node()[1]/node()[1]"/>
This variable is assigned the expected value when $testvar is assigned in the second way.
The return type of both Method1 and Method2 is XmlDocument. I think there is something wrong with <xsl:value-of>. What is the correct way of assigning this variable?
UPDATE
I have solved the issue by the following code:
<xsl:variable name="testvar"><xsl:copy-of select="myUtils:Method1($var1,$var2)"/></variable>
For the second variable I have used the below code:
<xsl:variable name="testvar2" select="msxsl:node-set($testvar)/node()[1]/node()[1]/node()[1]/node()[1]"/>
Please refer to this link for more details.
I am guessing your Method1 functions returns node-set, as opposed to simple text/number value. In this this case the issue is probably because xsl:value-of gets the "value" of a node, not the actual node itself.
Try using xsl:copy-of instead
<xsl:copy-of select="myUtils:Method1($var1,$var2)" />

Creating a space ( ) in XSL

Im trying to create automatic spacing in a XSL document in the following manner.
<td><xsl:value-of select="Name/First"/> <xsl:text disable-output-escaping="yes"><![CDATA[ ]]></xsl:text><xsl:value-of select="Name/Last"/> </td>
However, the rendered HTML is of the following form
<td>John&nbsp;Grisham</td>
Any idea on how I could fix this?
Your immediate problem is that while unicode 160 (hex 0xA0) is an HTML entity, it is not an XML entity.
Use   or   for non-breaking-space instead.
However for your larger problem, how to handle white space in XSL, the answer is simply this: Use <xsl:text>.
Every time you include ANY plain text, enclose it in <xsl:text> the text goes here </xsl:text> tags. If you don't you will be in for a world of pain the next time a clever text editor reformats your document.
You are already in for at least a Continent, or possibly if you are lucky a Country of pain, for expecting XML/XSL to conserve whitespace. Even geniuses who understand XSL to the nth degree still get County or at least Borough level pain from whitespace handling. (Borough level pain is encoded in the XML spec, "2.11 End-of-Line Handling" by its insane design decision to refuse to distinguish between LF and CRLF - so nobody can avoid that).
Just so you know what to expect: It isn't easy - you can get away without the <xsl:text> tags for a surprisingly long time, but if you just accept it, and put them in from the get-go, it will be easier in the long run.
Example WRONG:
<xsl:element name="MyElem">
<xsl:attribute name="fullPath">c:\base\Path\here\<xsl:value-of select="../parent/#relPath"/>\<xsl:value-of select="#fileName">
</xsl:attribute>
</xsl:element>
Example RIGHT:
<xsl:element name="MyElem">
<xsl:attribute name="fullPath">
<xsl:text>c:\base\Path\here\</xsl:text>
<xsl:value-of select="../parent/#relPath"/>
<xsl:text>\</xsl:text>
<xsl:value-of select="#fileName">
</xsl:attribute>
</xsl:element>
The thing is, they both produce exactly the same output.
But one of them will be messed up at some point in the future, yes, possibly by someone as yet unborn, the other will not.
The short-ish explanation is this: Nodes consisting ONLY OF WHITESPACE are ignored by default (unless you tweak the options). So that is anything consisting only of CR, LF, TAB and SPACE between > and <. Nodes consisting of non-whitespace text, with leading and trailing whitespace, may have whitespace "folded" - I.e. effed up.
So the difference between the Example RIGHT and this:
<xsl:element name="MyElem">
<xsl:attribute name="fullPath">
c:\base\Path\here\
<xsl:value-of select="../parent/#relPath"/>
\
<xsl:value-of select="#fileName">
</xsl:attribute>
</xsl:element>
is that one generates <MyElem fullPath="c:\base\Path\here\relative\path\filename.txt"/> and the other, depending on the DOM options in force, generates one of these:
<MyElem fullPath="c:\base\Path\here\relative\path\filename.txt"/>
<MyElem fullPath="c:\base\Path\here\ relative\path \ filename.txt"/>
<MyElem fullPath="c:\base\Path\here\
relative\path
\&10;filename.txt"/>
<MyElem fullPath="c:\base\Path\here\
relative\path
\ &10; filename.txt"/>
Only one of which was what you wanted... but any of which might be correct depending on the options in force...
Used this <xsl:text disable-output-escaping="yes">&</xsl:text>nbsp; and it worked!

XPath / XmlCompiledTransform: Test parent with XPath function

I am not sure if I'm really doing a mistake or if this is just not working properly:
<xsl:if test="position() = 1 or parent::position() = 1">
<!-- do something -->
</xsl:if>
If the current node is the first child, or it's parent is the first child, then do something special.
The problem is "parent::position() = 1" .. with .net's XmlCompiledTransform I get
Expected end of the expression, found '('. ...sition() = 1 or parent ::position -->(<-- = 1.
Now, apparently it doesn't like that position() function in the second part, but.. how do I get the parent's position? How do I, generally, combine XPath functions with XPaths in tests?
Good question, +1.
You need to define what is meant by position of the parent.
parent::node()[position() = 1]
is always true() when the parent node exists (is false() only if the context node is the document node / as this node is the top node in the tree and doesn't have a parent), because any node in a tree (well-formed XML document) by definition can have at most one parent node.
Most likely, you want to test that the parent element is the first in document order among its siblings. One way to test for this is:
not(parent::node()/preceding-sibling::*)
So, the complete code becomes:
<xsl:if test="position() = 1 or not(parent::node()/preceding-sibling::*)">
<!-- do something -->
</xsl:if>

XPath/HtmlAgilityPack: How to find an element (a) with a specific value for an attribute (href) and find adjacent table columns?

I'm pretty desperate because I can't figure out how to achieve what I stated in the question. I've already read countless of similar examples but didn't find one which works in exact situation. So, let's say I have the following code:
<table><tr>
<td>text A</td><td><a>id A</a></td><td><a>img A</a></td>
<td>text B</td><td><a>id B</a></td><td><a>img B</a></td>
<td>text C</td><td><a>id C</a></td><td><a>img C</a></td>
</tr></table>
Now, what I already have is a part of url-a. I basically want to know how I can get id A and img A. I'm trying to "find" the line with XPath but I can't work out a way to make it work. Also, it might be possible that the information is not present at all. This is my latest try (seriously, I've tinkered with this for more than 3 hours now trying numerous different ways):
if (htmlDoc.DocumentNode.SelectSingleNode(#"/a[contains(#href, 'part-url-a')]") != null)
string ida = htmlDoc.DocumentNode.SelectSingleNode(#"/a[contains(#href, 'part-url-a')]/following-sibling::a").InnerText;
Well, it's apparently wrong as hell so I'd be very pleased if someone could help me out here. Also I'd appreciate it if someone could point me to some Website which explains XPath and the notations/Syntax in detail with examples like this one. Books also welcome.
PS: I know I could achieve my goal without XPath at all too with Regex or just a simple StreamReader in C# and checking if each line contains what I need but a) it's too fragile for my needs because the code might have abrupt line-breaks and b) I really want to stay consistend with sticking completely to XPath for anything I'm doing in this project.
Thanks in advance for your help!
Use the following XPath expressions:
/*/tr/td[a[#href='url-a']]
/following-sibling::td[1]
/a/text()
When evaluated against the provided (malformed but corrected) XML document:
<table><tr>
<td>text A</td><td><a>id A</a></td><td><a>img A</a></td>
<td>text B</td><td><a>id B</a></td><td><a>img B</a></td>
<td>text C</td><td><a>id C</a></td><td><a>img C</a></td>
</tr></table>
the wanted text node is selected:
id A
Similarly, this XPath expression:
/*/tr/td[a[#href='url-a']]
/following-sibling::td[2]
/a/text()
when evaluated against the same XML document (above), selects the other wanted text node:
img A
XSLT-based verification:
When this transformation is applied on the XML document (above):
<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=
"/*/tr/td[a[#href='url-a']]
/following-sibling::td[1]
/a/text()"/>
<xsl:text>
</xsl:text>
<xsl:copy-of select=
"/*/tr/td[a[#href='url-a']]
/following-sibling::td[2]
/a/text()"/>
</xsl:template>
</xsl:stylesheet>
the wanted results are produced:
id A
img A
You have a seriously broken HTML with unmatching closing td tags. Fix them please. It's just an ugly picture this markup.
This being said hopefully Html Agility Pack can handle any crap that you throw at it, so here's how to proceed and parse the junk you have and find the id and img values given the href:
class Program
{
static void Main()
{
var doc = new HtmlDocument();
doc.Load("test.html");
var anchor = doc.DocumentNode.SelectSingleNode("//a[contains(#href, 'url-a')]");
if (anchor != null)
{
var id = anchor.ParentNode.SelectSingleNode("following-sibling::td/a");
if (id != null)
{
Console.WriteLine(id.InnerHtml);
var img = id.ParentNode.SelectSingleNode("following-sibling::td/a");
if (img != null)
{
Console.WriteLine(img.InnerHtml);
}
}
}
}
}

Categories

Resources