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.