问题
I'm a newbie to XSLT. I've an XML document and I need to come up with xslt to validate certain rules in the XML document. The XML and xsl file will be used in xsltproc tool and the output will be a simple Pass or Fail.
Sample XML:
...
<Manager mincount="4" grade="10"...>
<Employee id="1" grade="9" .... />
<Employee id="2" grade="8" .... />
.....
</Manager>
- The number of children under Manager (Employee in this case) must be equal to or greater than the value of mincount attribute.
- All the employee's grade must be less than the Manager grade.
Appreciate your help! TIA!
回答1:
Here's an XSLT 1.0 option that gives a pass/fail. There is additional detail in the "Fail", but that can be removed. It also outputs the message to both stdout and stderr and terminates processing.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/Manager">
<xsl:if test="not(count(Employee) >= @mincount)">
<xsl:variable name="vMessage" select="concat('Fail - Number of Employees (',count(Employee),') is not >= to @mincount (',@mincount,').')"/>
<!--stdout-->
<xsl:value-of select="$vMessage" disable-output-escaping="yes"/>
<!--stderr-->
<xsl:message terminate="yes"><xsl:value-of select="$vMessage"/></xsl:message>
</xsl:if>
<xsl:if test="Employee/@grade >= @grade">
<xsl:variable name="vMessage" select="concat('Fail - Employee (id ',Employee[@grade >= ancestor::Manager/@grade][1]/@id,') has a grade (',Employee[@grade >= ancestor::Manager/@grade][1]/@grade,') that is higher than the Manager grade (',@grade,').')"/>
<!--stdout-->
<xsl:value-of select="$vMessage" disable-output-escaping="yes"/>
<!--stderr-->
<xsl:message terminate="yes"><xsl:value-of select="$vMessage"/></xsl:message>
</xsl:if>
<xsl:text>Pass</xsl:text>
</xsl:template>
</xsl:stylesheet>
Here are a few XML/output examples:
<Manager mincount="1" grade="7" id="28">
<Employee id="6" grade="5"/>
<Employee id="7" grade="1"/>
<Employee id="8" grade="2"/>
<Employee id="3" grade="7"/>
</Manager>
Fail - Employee (id 3) has a grade (7) that is higher than the Manager grade (7).
<Manager mincount="1" grade="7" id="28">
<Employee id="6" grade="5"/>
<Employee id="7" grade="1"/>
<Employee id="8" grade="2"/>
<Employee id="3" grade="6"/>
</Manager>
Pass
<Manager mincount="10" grade="7" id="28">
<Employee id="6" grade="5"/>
<Employee id="7" grade="1"/>
<Employee id="8" grade="2"/>
<Employee id="3" grade="6"/>
</Manager>
Fail - Number of Employees (4) is not >= to @mincount (10).
回答2:
Use an XSD schema instead. It's designed to validate XML.
In particular you might be interested in XSD 1.1 assertions.
See http://www.w3schools.com/schema/ for a good tutorial.
回答3:
Here's an XSLT that checks mincount values vs actual number of occurrences of Employee. Note that xsl:function is used so this requires XSLT 2.0.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:custom="http://localhost:8080/customFunctions" exclude-result-prefixes="custom">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:function name="custom:checkMgr">
<xsl:param name="mgr"/>
<xsl:choose>
<xsl:when test="$mgr/@mincount > count($mgr/Employee)">false</xsl:when>
<xsl:when test="count($mgr/Employee[number(@grade) >= number($mgr/@grade)]) > 0">false</xsl:when>
<xsl:otherwise>true</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="/">
<root>
<xsl:apply-templates select="root/Manager"/>
</root>
</xsl:template>
<xsl:template match="Manager">
<mgrCheck>
<xsl:attribute name="id" select="@id"/>
<xsl:attribute name="mincount" select="@mincount"/>
<xsl:attribute name="actual" select="count(Employee)"/>
<xsl:attribute name="grade" select="@grade"/>
<xsl:attribute name="numEmpNoLessGrade" select="count(Employee[number(@grade) >= number(../@grade)])"/>
<xsl:attribute name="OK" select="custom:checkMgr (.)"/>
</mgrCheck>
</xsl:template>
</xsl:stylesheet>
When applied on the following input:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Manager mincount="4" grade="10" id="26">
<Employee id="1" grade="9"/>
<Employee id="2" grade="8"/>
</Manager>
<Manager mincount="1" grade="10" id="27">
<Employee id="3" grade="9"/>
<Employee id="4" grade="8"/>
<Employee id="5" grade="4"/>
</Manager>
<Manager mincount="1" grade="7" id="28">
<Employee id="6" grade="8"/>
<Employee id="7" grade="7"/>
<Employee id="8" grade="6"/>
<Employee id="9" grade="9"/>
</Manager>
<Manager mincount="3" grade="9" id="29">
<Employee id="10" grade="9"/>
<Employee id="11" grade="8"/>
<Employee id="12" grade="7"/>
</Manager>
</root>
the result is:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<mgrCheck id="26" mincount="4" actual="2" grade="10" numEmpNoLessGrade="0" OK="false"/>
<mgrCheck id="27" mincount="1" actual="3" grade="10" numEmpNoLessGrade="0" OK="true"/>
<mgrCheck id="28" mincount="1" actual="4" grade="7" numEmpNoLessGrade="3" OK="false"/>
<mgrCheck id="29" mincount="3" actual="3" grade="9" numEmpNoLessGrade="1" OK="false"/>
</root>
An alternative in XSLT 1.0 could be the following:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="root/Manager"/>
</root>
</xsl:template>
<xsl:template match="Manager">
<mgrCheck>
<id><xsl:value-of select="@id"/></id>
<mincount><xsl:value-of select="@mincount"/></mincount>
<actual><xsl:value-of select="count(Employee)"/></actual>
<grade><xsl:value-of select="@grade"/></grade>
<numEmpNoLessGrade><xsl:value-of select="count(Employee[@grade >= ../@grade])"/></numEmpNoLessGrade>
<OK>
<xsl:choose>
<xsl:when test="@mincount > count(Employee)">false</xsl:when>
<xsl:when test="count(Employee[@grade >= ../@grade]) > 0">false</xsl:when>
<xsl:otherwise>true</xsl:otherwise>
</xsl:choose>
</OK>
</mgrCheck>
</xsl:template>
</xsl:stylesheet>
with result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<mgrCheck>
<id>26</id>
<mincount>4</mincount>
<actual>2</actual>
<grade>10</grade>
<numEmpNoLessGrade>0</numEmpNoLessGrade>
<OK>false</OK>
</mgrCheck>
<mgrCheck>
<id>27</id>
<mincount>1</mincount>
<actual>3</actual>
<grade>10</grade>
<numEmpNoLessGrade>0</numEmpNoLessGrade>
<OK>true</OK>
</mgrCheck>
<mgrCheck>
<id>28</id>
<mincount>1</mincount>
<actual>4</actual>
<grade>7</grade>
<numEmpNoLessGrade>3</numEmpNoLessGrade>
<OK>false</OK>
</mgrCheck>
<mgrCheck>
<id>29</id>
<mincount>3</mincount>
<actual>3</actual>
<grade>9</grade>
<numEmpNoLessGrade>1</numEmpNoLessGrade>
<OK>false</OK>
</mgrCheck>
</root>
来源:https://stackoverflow.com/questions/9306433/newbie-xslt-transformation-to-validate-rules-in-xml-document