Search code examples
schematron

Schematron - test if an attribute exists and if it does verify correct spelling of that attribute value


trying to figure out how to test if optional attributes do exist in a document, they can only contain one of two values and those values must be spelled correctly.

Here's an example of the XML:

<SIGNOFF ID="S0001" SIGN-ALL="YES">
  <ACCOMPBY ID="JCAT0001" INPUT-LABEL-REFS="IL00001 IL00002 IL00003" QUAL-REQS="RVPSM"/>
  <INSPBY ID="JCIT0001" NAFLAG="YES" USERINPUT="NO" USELATESTREV="YES" QUAL-REQS="LLM"/>
  <BUYBACK ID="JCBT0001"/>
</SIGNOFF>

Here's my Schematron test that's failing:

<rule context="ACCOMPBY/@QUALS-REQS">
  <assert test="@QUAL-REQS = 'RVSM' or @QUAL-REQS = 'LLM'" role="error" id="warn_signoff_accompby">SIGNOFF/ACCOMPBY @QUALS-REQS values RVSM or LLM are misspelled</assert>
</rule>

<rule context="INSPBY/@QUALS-REQS">
      <assert test="@QUAL-REQS = 'RVSM' or @QUAL-REQS = 'LLM'" role="error" id="warn_signoff_accompby">SIGNOFF/INSPBY @QUALS-REQS values RVSM or LLM are misspelled</assert>
</rule>

Have also tried this but it hits on every SIGNOFF whether the attribute is present or not:

<rule context="SIGNOFF/ACCOMPBY">
      <assert test="(@QUAL-REQS = 'RVSM' or @QUAL-REQS = 'LLM')" role="error" id="warn_signoff_accompby">SIGNOFF/ACCOMPBY qualifications RVSM or LLM are misspelled</assert>
</rule>

<rule context="SIGNOFF/INSPBY">
      <assert test="(@QUAL-REQS = 'RVSM' or @QUAL-REQS = 'LLM')" role="error" id="warn_signoff_accompby">SIGNOFF/ACCOMPBY qualifications RVSM or LLM are misspelled</assert>
</rule>

So if the Schematron worked correctly on the above XML example it would flag the user that ACCOMPBY/@QUALS-REQS value is spelled incorrectly.

Is this doable? Thank you so much for any point in the right direction.


Solution

  • When I use your sample XML document

    <SIGNOFF ID="S0001" SIGN-ALL="YES">
      <ACCOMPBY ID="JCAT0001" INPUT-LABEL-REFS="IL00001 IL00002 IL00003" QUAL-REQS="RVPSM"/>
      <INSPBY ID="JCIT0001" NAFLAG="YES" USERINPUT="NO" USELATESTREV="YES" QUAL-REQS="LLM"/>
      <BUYBACK ID="JCBT0001"/>
    </SIGNOFF>
    

    with the schematron schema (basically the second attempt you showed put into a complete schema)

    <schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt3">
        <pattern>
          <rule context="SIGNOFF/ACCOMPBY">
                <assert test="(@QUAL-REQS = 'RVSM' or @QUAL-REQS = 'LLM')" role="error" id="warn_signoff_accompby">SIGNOFF/ACCOMPBY qualifications RVSM or LLM are misspelled</assert>
          </rule>
          
          <rule context="SIGNOFF/INSPBY">
                <assert test="(@QUAL-REQS = 'RVSM' or @QUAL-REQS = 'LLM')" role="error" id="warn_signoff_accompby">SIGNOFF/ACCOMPBY qualifications RVSM or LLM are misspelled</assert>
          </rule>
        </pattern>
    </schema>
    

    at https://martin-honnen.github.io/schematron-fiddle/

    I get

       <svrl:failed-assert location="/Q{}SIGNOFF[1]/Q{}ACCOMPBY[1]" role="error" id="warn_signoff_accompby" test="(@QUAL-REQS = 'RVSM' or @QUAL-REQS = 'LLM')">
          <svrl:text>SIGNOFF/ACCOMPBY qualifications RVSM or LLM are misspelled</svrl:text>
       </svrl:failed-assert>
    

    That seems to be your wanted and intended result.

    If you want to explicitly express in the context that the attribute exists you could also change the context to check for the attribute with a predicate e.g.

    <rule context="SIGNOFF/ACCOMPBY[@QUAL-REQS]"><assert test="(@QUAL-REQS = 'RVSM' or @QUAL-REQS = 'LLM')" role="error" id="warn_signoff_accompby">SIGNOFF/ACCOMPBY qualifications RVSM or LLM are misspelled</assert></rule>