tags:

views:

502

answers:

3

I have an XML file:

<?xml version="1.0" standalone="yes"?>
<Questionnaire>
  <Temp_GridTypeTable_6>
    <Column2>Select Yes/No</Column2>
  </Temp_GridTypeTable_6>
  <Temp_GridTypeTable_1>
    <Column2>Rank 1,2,3</Column2>
  </Temp_GridTypeTable_1>
  <Temp_GridTypeTable_1>
    <Column1>I needed the income</Column1>
    <Column2>Why did you take a job on this project?</Column2>
  </Temp_GridTypeTable_1>
  <Temp_GridTypeTable_1>
    <Column1>Other</Column1>
    <Column2></Column2>
  </Temp_GridTypeTable_1>
  <Temp_GridTypeTable_2>
    <Column2>Select "Yes/No"</Column2>
  </Temp_GridTypeTable_2>
  <Temp_GridTypeTable_2>
    <Column1>No jobs</Column1>
    <Column2>344</Column2>
  </Temp_GridTypeTable_2>
  <Temp_GridTypeTable_3>
    <Column2>Input</Column2>
  </Temp_GridTypeTable_3>
  <Temp_GridTypeTable_3>
    <Column1>Unit</Column1>
    <Column2>123</Column2>
  </Temp_GridTypeTable_3>
</Questionnaire>

I want to access

<xsl:for-each select="Questionnaire/concat('Temp_GridTypeTablenode_',"1"))>

but this statement is not working.

+12  A: 

This is quite an example of how not to use XML. "Temp_GridTypeTable" and "Column" numbers are data, not structure, they should not be contained in the element names. So why aren't you using something less painful, say:

<Questionnaire>
  <Temp_GridTypeTable type="6">
    <Column num="2">Select Yes/No</Column>
  </Temp_GridTypeTable>
  <Temp_GridTypeTable type="1">
    <!-- ... -->
  <Temp_GridTypeTable>
  <!-- ... -->
</Questionnaire>

That being said, for your current situation, this is needed:

<xsl:for-each select="Questionnaire/*[
  local-name()
  =
  concat('Temp_GridTypeTable_', '1')
]">

For the "less painful" version of the input, this would have been required:

<xsl:for-each select="Questionnaire/Temp_GridTypeTable[@type = 1]">

Despite the fact that the second expression is a lot simpler and more straightforward, it will also perform much better. If you can help it, I recommend to change the input XML.


EDIT: Following up the argument that unfolded itself in the comments, I try to emphasize the difference between the local-name() and name() XPath functions, and where the difference matters:

                            | XML has namespaces  |  XML has no namespaces
----------------------------+---------------------+-----------------------
I care about namespaces     | use `name()`        |  use either function
                            |                     |
don't care about namespaces | use `local-name()`  |  use either function

Generally: If you fall into the "don't care about namespaces" group (most XML novices or casual XML users do), it's okay (sometimes even beneficial) to just always use local-name(). However, be prepared to learn about XML namespaces when the results you get and the results you expect start to diverge. At this point you don't belong to the said group anymore.

If you fall into the "I care about namespaces" group, you don't need this advice anyway. ;-)

Tomalak
In the past I have raised the issue that using local-name() is not a good practice and generally may produce unwanted and unexpected results. Using the name() function is almost always the better answer.
Dimitre Novatchev
And as always I find that view domain-centric and at odds with practical reality. It's akin to picking a side in strongly vs weakly typed: both can be correct depending on the task. Couldn't agree more about the state of the XML.
annakata
@both: From an academic standpoint (which is the one Dimitre Novatchev usually takes), the use of local-name() will potentially yield false positives and is therefore incorrect. From a practical standpoint (which I am inclined to take looking at the actual XML in the question), I believe that local-name() is the better alternative here, since *if* XML namespaces are involved somehow (improbable as it is), this is almost certainly by accident.
Tomalak
To clarify, my view is that whether you wish to look within a specific namespace or against any namespace is entirely dependant on the problem at hand. If you want to pick up from any namespace, name() yields false negatives. The pragmatic view (where we concur) is that namespaces are generally an obstruction, not a feature. That's just a consequence of how developers are using XML these days.
annakata
@annakata: Agreed. I've definitely seen more "How can I ignore namespaces?/Why does this XPath not match anything?" questions from people with no concept of namespaces, than questions that actively embrace namespaces.
Tomalak
The table in the Edit is a good step forward. However the value in row 1, col. 2: "Use either function" if "I care about namespaces" and "XML has no namespaces" is really incorrect. First of all, there is no such choice as I care or I don't care about namespaces. If anyone doesn't care about namespaces, sooner or later they are going to pay the price for this. Even if the document has no namespaces, this may change in the future and the expression will then start selecting unwanted nodes.
Dimitre Novatchev
To finish: local-name() should be recommended only if the OP has explicitly stated that they do not care about *prefixes* in the name. The correct question is not "Do yoy care about namespaces" but "Do you care about namespace prefixes". Even if a name belongs to some namespace, it still may have no prefix (when this namespace is the default namespace).
Dimitre Novatchev
@annakata "namespaces are generally an obstruction, not a feature." is a completely wrong statement. SO is to give people knowledge and to mark incorrect statements as such.
Dimitre Novatchev
@Dimitre Novatchev: The statement was about the pragmatic view and the way XML is being used today, not so much about namespaces. While it is true that namespaces are a (much needed) feature, they are just not used as a feature by the wider public, and often are regarded a hindrance by people with no use for them (which in fact is the majority).
Tomalak
@Dimitre Novatchev: Regarding the "namespace prefixes" comment: You are absolutely correct. I used "namespaces" and "namespace prefixes" interchangeably to keep it simple. As I noted: Those who do care about namespaces (and hence, about prefixes) don't need that little table. On the other comment: If the document ever starts to contain namespaces and you did not plan for it, you are very likely screwed anyway. Failure potential exists no matter if you used the one or the other function.
Tomalak
Failure potential exists, therefore it is best to try to minimize it. This is exactly why name() should be used if the OP does not explicitly state that prefixes are not an issue and can be ignored.
Dimitre Novatchev
@Tomalak "The majority is correct" principle is a dangerous and obnoxious one. I have lived the first half of my life in a socialist country and know this from experience. Even nowadays in some societies the majority decides that someone should be stoned to death...
Dimitre Novatchev
@Dimitre Novatchev: Strictly speaking: The "the majority is correct" principle is called democracy. ;-) In socialist counties they just *claimed* to care for the majority. But I digress... My (and annakata's) point is not to base correctness upon majority, but to take a realistic/heuristc approach based on experience. Since using name() will yield false negatives when namespaces suddenly appear (think: "name() = 'foo'"), it is potentially just as wrong as local-name(), only the other way around.
Tomalak
@Tomalak Fortunately, there is something more precise than your "realistic/heuristic approach". The same about the definition of democracy.
Dimitre Novatchev
thanks but problem is i can't the input xml file structure... but i done it by different way.
pankaj
Hm. I thought I showed a way to do it even with your original XML in my answer.
Tomalak
+1  A: 

You cannot evaluate strings as XPath expressions at run-time with pure XSLT.

You need an extension function that can evaluate an xpath expression at runtime. See for instance the EXSLT project.

On my system, using xsltproc, I can accomplish what you want with:

<!-- load the saxon extensions -->
<xsl:stylesheet version="1.0" xmlns:xx="http://icl.com/saxon" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
...
...
<xsl:for-each 
    select="xx:evaluate(concat('Questionnaire/Temp_GridTypeTable_', '1'))">

Here, I use the evaluate function from the saxon extension library. EXSLT's evaluate function should work the same way.

Must XSLT processors/libraries have some sort of evaluate function built in. See your library's documentation.

codeape
It can be done without extensions (see my answer). It's just not nice, and it performs poorly.
Tomalak
Of course, saying that "this cannot be done in pure XSLT without extensions" is definitely wrong!
Dimitre Novatchev
I will be downvoting this answer as soon as I have more time available.
Dimitre Novatchev
Yes, you can get the result the question asks for using your approach. But the general issue of evaluating XPath in XSLT at runtime, is only doable with extensions to XSLT. In this particular case, it was possible to create an equivalent XPath expression. That may not always be the case.
codeape
An answer to the question that does not address the "particular case" has no place among the correct answers to the question. It is simply misleading. Please, consider deleting.
Dimitre Novatchev
@Dimitre: That is not what I am saying. I write: "It cannot be done LIKE THAT in pure XSLT ...". I feel that my answer and Tomalak's are complimentary. My answer shows how to do it without rewriting the XPath expression, but with extensions. Tomalak's answer shows how to do it in pure XSLT, but requiring that the XPath expression is rewritten.
codeape
I changed the wording of my answer. From 'cannot be done' to 'cannot evaluate XPath at runtime'. @Dimitre, I agree that my answer was misleading before the edit. I will not delete, however, as my answer also solves the problem.
codeape
@codepage, recommending the use of extension functions for this problem is wrong, definitely a bad advice. Please, consider deleting this answer.
Dimitre Novatchev
@Dimitre: it might be bad advice, but it's still a solution. Let SO's voting system sort it out. At the moment, Tomalak's answer has the most votes, and therefore is at the top. Mine has zero, and is at the bottom. I even upvoted your answer, so your answer will be above mine :-)
codeape
"You cannot evaluate XPath expressions at runtime in pure XSLT" -- what a statement. All XPath expressions *are* evaluated by the XSLT processor at run time.
Dimitre Novatchev
@Dimitre Novatchev: I'd say that's nitpicking. ;-) Though of course "you cannot evaluate strings as XPath expressions at run-time with pure XSLT" is technically more correct.
Tomalak
I changed the wording of the first sentence.
codeape
@codepage You needn't upvote my answer if you aren't convinced it is correct. I see that someone downvoted your answer: I am not that person (didn't have time, just arrived at work). What we are doing here is to establish *the best answer*, regardless the points being given or taken. That's why I voiced objections to a (small) part of Tomalak's otherwise correct answer. As for the "majority decides correctness" principle, yuk, sometimies majority decides that someone should be stoned to death...
Dimitre Novatchev
@Dimitre: I am quite convinced that your answer is correct - I wouldn't have upvoted it otherwise. My answer is also "correct" - however offensive it is to the XSLT purist.
codeape
+2  A: 

Use:

        Questionnaire/*[name() = concat('', $vSuffix)]

where the variable $vSuffix contains the statically-unknown string -- in this case '1'.

Using local-name() as in Tomalak's answer is both unnecessarily long and imprecise, as in the general case it allows elements with a variety of (possibly unwanted and unexpected) names to be selected, such as:

  • OhMy:Temp_GridTypeTable_1
  • Different:Temp_GridTypeTable_1
  • UnWanted:Temp_GridTypeTable_1
Dimitre Novatchev
I see your point, but with the case at hand I would think that it's safe to assume that they are far from actually using XML namespaces. The question shows what looks like a complete XML document with no namespaces whatsoever. Even *if* there was a default namespace involved somewhere, I'm almost certain that they want to ignore it, given their current lack of XML expertise.
Tomalak
@Tomalak name() works perfectly in case of a default namespace -- no need for local-name().
Dimitre Novatchev
Oops, you are right of course. What I had in mind was "...if there was a namespace involved somewhere...". As I said, I can perfectly see your point, it was the practical experience that made me recommend local-name() over name().
Tomalak
thanks it is working..
pankaj