Newbie: XSLT Transformation to validate rules in XML document

爱⌒轻易说出口 提交于 2019-12-05 01:49:56

问题


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>
  1. The number of children under Manager (Employee in this case) must be equal to or greater than the value of mincount attribute.
  2. 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 &gt; count($mgr/Employee)">false</xsl:when>
            <xsl:when test="count($mgr/Employee[number(@grade) &gt;= number($mgr/@grade)]) &gt; 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 &gt;= ../@grade])"/></numEmpNoLessGrade>
            <OK>
                <xsl:choose>
                    <xsl:when test="@mincount &gt; count(Employee)">false</xsl:when>
                    <xsl:when test="count(Employee[@grade &gt;= ../@grade]) &gt; 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!