Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old November 12th, 2017, 03:49 PM
Registered User
 
Join Date: Nov 2017
Posts: 2
Thanks: 1
Thanked 0 Times in 0 Posts
Default Find XPath using XSLT/preceding-sibling::* does not seem to work in for-each

Hello,

Could someone please advise about the following? I am trying to determine the XPath of an issue reported by an XSLT file.

The XML is
Code:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="sample.sch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<movies xmlns="http://me.com/ns"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://me.com/ns sample-input2.xsd">
    <movie version="1.2">
        <title>A History of Violence</title>
        <year>2005</year>
        <country>US</country>
        <genre>Crime</genre>
        <summary>Tom Stall, a humble family man and owner of a
            popular neighborhood restaurant, lives a quiet but
            fulfilling existence in the Midwest. One night Tom
            foils a crime at his place of business and, to his
            chagrin, is plastered all over the news for his
            heroics. Following this, mysterious people follow
            the Stalls' every move, concerning Tom more than
            anyone else. As this situation is confronted, more
            lurks out over where all these occurrences have
            stemmed from compromising his marriage, family
            relationship and the main characters' former
            relations in the process.</summary>
        <director>
            <last_name>Cronenberg</last_name>
            <first_name>David</first_name>
            <birth_date>1943</birth_date>
        </director>
        <actor>
            <first_name>Vigo</first_name>
            <last_name>Mortensen</last_name>
            <birth_date>1958</birth_date>
            <role>Tom Stall</role>
        </actor>
        <actor>
            <first_name>Maria</first_name>
            <last_name>Bello</last_name>
            <birth_date>1967</birth_date>
            <role>Eddie Stall</role>
        </actor>
        <actor>
            <first_name>Ed</first_name>
            <last_name>Harris</last_name>
            <birth_date>1950</birth_date>
            <role>Carl Fogarty</role>
        </actor>
        <actor>
            <first_name>William</first_name>
            <last_name>Hurt</last_name>
            <birth_date>1950</birth_date>
            <role>Richie Cusack</role>
        </actor>
    </movie>
    <movie>
        <title>heat</title>
        <year>1795</year>
        <country>USA</country>
        <genre>Crime</genre>
        <summary>Hunters and their prey--Neil and his professional
            criminal crew hunt to score big money targets
            (banks, vaults, armored cars) and are, in turn,
            hunted by Lt. Vincent Hanna and his team of cops
            in the Robbery/Homicide police division. A botched
            job puts Hanna onto their trail while they regroup
            and try to put together one last big 'retirement'
            score. Neil and Vincent are similar in many ways,
            including their troubled personal lives. At a
            crucial moment in his life, Neil disobeys the
            dictum taught to him long ago by his criminal
            mentor--'Never have anything in your life that you
            can't walk out on in thirty seconds flat, if you
            spot the heat coming around the corner'--as he
            falls in love. Thus the stage is set for the
            suspenseful ending....</summary>
        <director>
            <last_name>Mann</last_name>
            <first_name>Michael</first_name>
            <birth_date>1943</birth_date>
        </director>
        <actor>
            <first_name>Al</first_name>
            <last_name>Pacino</last_name>
            <birth_date>1940</birth_date>
            <role>Lt. Vincent Hanna</role>
        </actor>
        <actor>
            <first_name>Robert</first_name>
            <last_name>De Niro</last_name>
            <birth_date>1943</birth_date>
            <role>Neil McCauley</role>
        </actor>
        <actor>
            <first_name>Val</first_name>
            <last_name>Kilmer</last_name>
            <birth_date>1959</birth_date>
            <role>Chris Shiherlis</role>
        </actor>
        <actor>
            <first_name>Jon</first_name>
            <last_name>Voight</last_name>
            <birth_date>1938</birth_date>
            <role>Nate</role>
        </actor>
    </movie>
</movies>
Please note that at /movies/movie[2]/title[1] the text() starts with a lower-case letter. As a test, I declared this to be incorrect in a .sch file. The .sch file works as expected and catches the error.

.sch snippet
Code:
<schema xmlns="http://purl.oclc.org/dsdl/schematron" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:regexp="http://exslt.org/regular-expressions" queryBinding="xslt2" xmlns:q="http://me.com/ns">
    <title>Sample_Schematron</title>
    <ns uri="http://www.w3.org/1998/Math/MathML" prefix="m"/>
    <ns uri="http://www.w3.org/2005/xpath-functions" prefix="fn"/>
    <ns uri="http://exslt.org/regular-expressions" prefix="regexp"/>
    <ns uri="http://me.com/ns" prefix="q"/>
...
<!-- title -->
    <pattern>
        <rule context="q:title">
            <assert test="matches(substring(., 1, 1), '[A-Z]')">The first letter in title should be capitalized.</assert>
        </rule>
    </pattern>
...
As stated, the above works fine in the XML editor I am currently using. But I am an individual programmer working on my own non-profit projects. The evaluation license will soon run out for that editor. I therefore sought to develop my own basic solution using Java [SaxonHE9-7-0-20J as the processor]. I got DTD and XSD validation and xsl transformations to work, but it appears one needs SaxonEE9-7-0-20J to work with .sch files in Java. [If anyone knows this to be incorrect, could you please point me in the correct direction for a Java solution using HE? Likewise, if I should look at a different programming language other than Java.]

To get around this limitation, I decided to try placing the Schematron rules into an XSL and output an html file. The <title> error is detected and a message is printed. But a user would need to know where to find the error, so I attempted to generate the XPath using for-each.

XSL file
Code:
<?xml version="1.0" encoding="UTF-8"?>
<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" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:q="http://me.com/ns">

<xsl:output method="html" />

<xsl:template match="/">
        <xsl:result-document href="file:/C:/Sandbox/output/test.html" method="html">
            <HTML>
                <HEAD>
                    <TITLE>What title should go here?</TITLE>
                </HEAD>
                <BODY>
                    <xsl:apply-templates/>
                </BODY>
            </HTML>
        </xsl:result-document>
</xsl:template>

<xsl:template match="*|@node">
        <xsl:apply-templates/>
</xsl:template>

<xsl:template match="q:title">
        <xsl:if test="matches(substring(., 1, 1), '[A-Z]')">
            <xsl:text>The first letter in title should be capitalized. </xsl:text>
            <xsl:variable name="xpath">
                    
                <xsl:for-each select="ancestor-or-self::*">
                    <xsl:if test="./preceding-sibling::*">
                        <xsl:text>was this reached?</xsl:text>
                    </xsl:if>
                    <xsl:text>/</xsl:text>
                    <xsl:value-of select="string-join(name(.), name())"/>
                    <xsl:variable name="elemName" select="name()"/>
                    <xsl:text>[</xsl:text>
                    <xsl:value-of select="count(./preceding-sibling::*[name() = $elemName]) + 1"/>
                    
                    <xsl:text>]</xsl:text>
                    
                </xsl:for-each>
            </xsl:variable>
            <xsl:value-of select="$xpath"/>
        </xsl:if>
    </xsl:template>

<xsl:template match="text()"/>
The output is [I'll clean up the html once the xsl logic works.]
Code:
<HTML xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:q="http://me.com/ns">
   <HEAD>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <TITLE>What title should go here?</TITLE>
   </HEAD>
   <BODY>The first letter in title should be capitalized. /movies[1]/movie[1]/title[1]</BODY>
</HTML>
Note that a [2] is desired next to 'movie' and not [1], and 'was this reached?' is never reached. If I understand correctly, the <xsl:for-each select="ancestor-or-self::*"> would start with the <title> element where the condition is met. It looks for a preceding-sibling::* [either in the if statement or count()], but there is none. The <movie> element is then next and it should now be the context by which a preceding-sibling is judged. There is a preceding-sibling in this case, but it does not register. Could someone please explain why?

Also, if the xsl logic used cannot be fixed then could someone suggest an alternative method to get the XPath?

Thank your for your help.

Last edited by Ken_Doug_01; November 12th, 2017 at 03:53 PM..
 
Old November 12th, 2017, 04:12 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

I think that the line

Code:
<xsl:if test="matches(substring(., 1, 1), '[A-Z]')">
should be

Code:
<xsl:if test="not(matches(substring(., 1, 1), '[A-Z]'))">
Note, you could have saved us an awful lot of time by removing all the back-story about Schematron. Knowing the back-story isn't relevant to debugging this stylesheet.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
The Following User Says Thank You to mhkay For This Useful Post:
Ken_Doug_01 (November 12th, 2017)
 
Old November 12th, 2017, 05:01 PM
Registered User
 
Join Date: Nov 2017
Posts: 2
Thanks: 1
Thanked 0 Times in 0 Posts
Default Thank you

Thank you, Michael. Also, my apologies for reporting such a basic issue.





Similar Threads
Thread Thread Starter Forum Replies Last Post
xpath preceding-sibling problem Unicorn XSLT 2 August 21st, 2012 01:16 PM
Want to Pull multiple following-sibling data to preceding-sibling element sameer_kadam XSLT 4 May 9th, 2009 08:07 AM
Preceding Sibling mphare XSLT 3 March 4th, 2009 11:04 AM
Using preceding-sibling mcanne98 Infopath 0 September 11th, 2008 11:09 PM
preceding-sibling jonesyp XSLT 2 November 22nd, 2005 12:29 PM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.