I’d like to create a Schematron schema that validates:
- That if elements at
/html/body/main/div/divhave an optionalclassattribute, it contains one of thecat1orcat2values in any case. - That if elements not at
/html/body/main/div/divhave an optionalclassattribute, it does not in any case contain one of the valuescat1orcat2.
Here’s a valid example:
<html xmlns="http://www.w3.org/1999/xhtml">
<head></head>
<body>
<main>
<div class="name">
<div class="cat1 cat3"> <!— Other values are acceptable as long as cat1 or cat2 is present. —>
<div></div>
</div>
<div class="cat2">
<div></div>
</div>
<div> <!— No class attribute is OK —>
<div></div>
</div>
</div>
</main>
</body>
</html>
And an invalid example:
<html xmlns="http://www.w3.org/1999/xhtml">
<head></head>
<body>
<main>
<div class="name cat1"> <!— cat1 not allowed here —>
<div class="cat3"> <!— cat1 or cat2 missing —>
<div></div>
</div>
<div class="cat2">
<div class="cat2"></div> <!— cat2 not allowed here —>
</div>
<div class=""> <!— Empty class attribute is not OK —>
</div>
</div>
</main>
</body>
</html>
Here’s what I’ve got so far. The XPath query //*[not(self::html/body/main/div/div)] doesn’t work. It also returns nodes at /html/body/main/div/div.
<schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
<pattern id="test">
<rule context="//*[not(self::html/body/main/div/div)]">
<assert test="
not(contains(@class, ’cat1’)) and
not(contains(@class, ’cat2’))
" role="error">
The class cannot contain
"<value-of select="@class"/>".
</assert>
</rule>
</pattern>
</schema>
Martins Answer works (except of a small mistake) but it is unnecessarily complex in my opinion. If you want it simpler or needs to use XSLT2, you can do it in this way: