Jump to content
Claris Engage 2025 - March 25-26 Austin Texas ×

Position() meeting given criteria


This topic is 7088 days old. Please don't post here. Open a new topic instead.

Recommended Posts

Posted

Hi all. My presence in the XML forum can only mean I want something again.

Working in FMP XML grammar, I am trying to determine the position of the first field name to start with the string Transact. I have been able to create a variable that gives me the field name, but I cannot figure out how to get the position. My efforts thus far:


<xsl:variable name="firstTransactionField">

    <xsl:value-of select="//fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD[substring(@NAME,1,8)='Transact']/@NAME"/>

</xsl:variable>

This, as I mentioned, will return me the name of the field. How do I modify this to return the position of the field within the <METADATA> element?

Jerry

Posted

So I banged my head against the brick wall until it broke. I got a result, in fact, the result I was looking for, but I would appreciate comments from others, because it looks really inefficient to me.


<xsl:variable name="firstTransactionField">

   <xsl:for-each select="//fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD">

      <xsl:if test="substring(@NAME,1,8)='Transact'">

         <xsl:value-of select="position()"/>

      </xsl:if>

   </xsl:for-each>

</xsl:variable>

I've observed in other situations that the XML processor seems to work from the bottom up when transforming data, and this may be another example -- it appears to process all field names, returning the position() of each one that starts with Transact, and since the last one processed is the first one in the list, that satisfies my condition.

Anything I'm not seeing in here that may trip me up?

J

Posted

That's an interesting question, and I can understand your headache smirk.gif

The result of position() is context-dependent, e.g. it's the position of a certain element with respect to its parent. Context here means where you are in the XML tree.

The XSLT processor does not work bottom up. In your xsl:for-each loop, the context is /fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD (I think you have written a / too much). It steps through every FIELD, and if the beginning of the name matches 'Transact', the position() is stored in the variable. Because there is no way to exit the loop (no loop-breaking condition) - because of its functional language nature XSLT does not allow this to avoid side-effects - all FIELDs are stepped through, and of course then position of the last 'Transact' match is stored in the variable.

So this can not be a solution if you have more than one match of 'Transact'.

I found a possible solution in the

XSLT FAQ (look at the FAQs under "Position", but I'm not sure if it works:

<xsl:variable name="firstTransactionField" select="count((/fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD[substring(@NAME,1,8) = 'Transact'])[1]/preceding-sibling:???*) + 1"/>

The translation would be the following:

Count all siblings, i.e. all FIELD elements, that precede the first FIELD element whose name matches 'Transact', and add 1 (one).

Posted

all FIELDs are stepped through, and of course then position of the last 'Transact' match is stored in the variable.

This is exactly the type of thing that makes me think the XML is processed bottom-up. The result I get from the last snippet of code is 912. Inspecting my list of fields in XMLShell, I see that the 912th field is the first (from the top) to start with Transact. (BTW, I thought I was supposed to use // to denote the top-level element? Otherwise, wouldn't it be relative to the current node?)

The snippet you wrote is quite ingenious. But I think I am going to sleep on it before I try it out. It is boggling my mind even as we speak. :c

Thanks, Martin.

J

Posted

According to the XPath spec (http://www.w3.org/TR/xpath)???

/ (a single slash) selects the document root (e.g. the top level element)

//para selects all the para descendants of the document root and thus selects all para elements in the same document as the context node

//olist/item selects all the item elements in the same document as the context node that have an olist parent

ok, then probably //fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD would mean

"selects all FIELD elements as context node that are child of METADATA which have FMPXMLRESULT as parent".

Now what would this mean within xsl:for-each?

Answer: It does just one step. Because all FIELD elements are in one group (being the descendants). And it get's just one match, the first FIELD element that matches 'Transact'.

So again no bottom-up.

That's probably why you get the right answer with the "wrong" technique!

Now what would happen if you had several METADATA or several FMPXMLRESULT elements in the tree?

Posted

We always assume your // technique that gets all the descendants:

Case 1:

Several METADATA elements would make no difference, they all belong to the descendant group of FMPXMLRESULT. So still one step in the xsl:for-each.


root - FMPXMLRESULT - METADATA - FIELDS

                    - METADATA - FIELDS

                    - METADATA - FIELDS





Case 2:



Several FMPXMLRESULT elements in the tree however would make a difference. You get several groups of descendants. The groups are stepped through in xsl:for-each. The last match in the descendant group would overwrite the previous ones. So if you had 'Transact' fields in the last FMPXMLRESULT group, the first of these that matches would determine the position.





root - FMPXMLRESULT - METADATA - FIELDS

     - FMPXMLRESULT - METADATA - FIELDS

     - FMPXMLRESULT - METADATA - FIELDS

Well, the discussion got quite academic, because anyway such a grammar is not defined. But my question probably helped to increase the headache. smile.gif Everything clear? cool.gif

  • 4 weeks later...
Posted

Just an FYI. I *never* use "//". I found it more time consuming to use full paths as needed, but at least I always got the correct results! :

This topic is 7088 days old. Please don't post here. Open a new topic instead.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.