Home > Enterprise >  SaxonCS - How to deal with XSLT transformation errors
SaxonCS - How to deal with XSLT transformation errors

Time:01-01

I'm a complete newbie about XSLT transformation. My goal is to "validate" different rules present in a given stylesheet against a XML file. This stylesheets is XSLT version 2.0 so in order to do that I'm trying Saxon library from Saxonica.

Just to lay out some context, I'm working around the Factur-X topic, an electronic invoice standard for some European countries.

I'm able to transform a XML input file with a given XSLT file but I would like to "catch" the errors at runtime ?

I read some docs, using SaxonCS 11.4.1 I tried the basic following code :


public static bool Validate(string schematronFilePath, string xmlFilePath)
{
    Processor processor = new();

    var compiler = processor.NewXsltCompiler();

    Xslt30Transformer transformer = compiler.Compile(new Uri(schematronFilePath)).Load30();

    transformer.SchemaValidationMode = SchemaValidationMode.Strict;

    List<Error> transformerErrorList = new();
    transformer.ErrorReporter = err =>
    {
        transformerErrorList.Add(err);
    };

        XdmNode input = processor.NewDocumentBuilder().Build(new Uri(xmlFilePath));
    transformer.GlobalContextItem = input;

    var outputFileStream = File.OpenWrite(@"pathToOuputFile");
    Serializer serializer = processor.NewSerializer(outputFileStream);

    transformer.ApplyTemplates(input, serializer);

    outputFileStream.Close();

    return transformerErrorList.Any();
}

It seems to work pretty well cause I have an output file that looks like this

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<svrl:schematron-output xmlns:ccts="urn:un:unece:uncefact:documentation:standard:CoreComponentsTechnicalSpecification:2"
                         xmlns:iso="http://purl.oclc.org/dsdl/schematron"
                         xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
                         xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
                         xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
                         xmlns:saxon="http://saxon.sf.net/"
                         xmlns:schold="http://www.ascc.net/xml/schematron"
                         xmlns:svrl="http://purl.oclc.org/dsdl/svrl"
                         xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
                         xmlns:xhtml="http://www.w3.org/1999/xhtml"
                         xmlns:xs="http://www.w3.org/2001/XMLSchema"
                         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                         schemaVersion=""
                         title="EN16931 model bound to CII"><!--   
           
           
         -->
   <svrl:ns-prefix-in-attribute-values prefix="rsm"
                                        uri="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"/>
   <svrl:ns-prefix-in-attribute-values prefix="ccts"
                                        uri="urn:un:unece:uncefact:documentation:standard:CoreComponentsTechnicalSpecification:2"/>
   <svrl:ns-prefix-in-attribute-values prefix="udt"
                                        uri="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"/>
   <svrl:ns-prefix-in-attribute-values prefix="qdt"
                                        uri="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"/>
   <svrl:ns-prefix-in-attribute-values prefix="ram"
                                        uri="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"/>
   <svrl:ns-prefix-in-attribute-values prefix="xs" uri="http://www.w3.org/2001/XMLSchema"/>
   <svrl:active-pattern document="file:///C:/Dev/Factur-X/DocTestFacturX/Resultats/xml_202212281826.xml"
                         id="EN16931-CII-Model"
                         name="EN16931-CII-Model"/>
   <svrl:fired-rule context="/rsm:CrossIndustryInvoice"/>
   <svrl:failed-assert test="(count(//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax[ram:CategoryCode='Z'])=0 and count(//ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax[ram:CategoryCode='Z'])=0 and count(//ram:CategoryTradeTax[ram:CategoryCode='Z'])=0) or ( count(//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax[ram:CategoryCode='Z'])=1 and (exists(//ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax[ram:CategoryCode='Z']) or exists(//ram:CategoryTradeTax[ram:CategoryCode='Z'])))"
                        id="BR-Z-01"
                        flag="fatal"
                        location="/*:CrossIndustryInvoice[namespace-uri()='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'][1]">
      <svrl:text>[BR-Z-01]-An Invoice that contains an Invoice line (BG-25), a Document level allowance (BG-20) or a Document level charge (BG-21) where the VAT category code (BT-151, BT-95 or BT-102) is "Zero rated" shall contain in the VAT breakdown (BG-23) exactly one VAT category code (BT-118) equal with "Zero rated".</svrl:text>
   </svrl:failed-assert>
   <svrl:failed-assert test="(count(//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax[ram:CategoryCode='AE'])=0 and count(//ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax[ram:CategoryCode='AE'])=0 and count(//ram:CategoryTradeTax[ram:CategoryCode='AE'])=0) or ( count(//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax[ram:CategoryCode='AE'])=1 and (exists(//ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax[ram:CategoryCode='AE']) or exists(//ram:CategoryTradeTax[ram:CategoryCode='AE'])))"
                        id="BR-AE-01"
                        flag="fatal"
                        location="/*:CrossIndustryInvoice[namespace-uri()='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'][1]">
      <svrl:text>[BR-AE-01]-An Invoice that contains an Invoice line (BG-25), a Document level allowance (BG-20) or a Document level charge (BG-21) where the VAT category code (BT-151, BT-95 or BT-102) is "Reverse charge" shall contain in the VAT breakdown (BG-23) exactly one VAT category code (BT-118) equal with "VAT reverse charge".</svrl:text>
   </svrl:failed-assert>
.....................

However at runtime the variable transformerErrorList does not get populated, I was expecting to find all the failed assertion in this list and thus my method to return false but it always returns true. Am I wrong or is it not supposed to behave like that by default (maybe I'm missing some configuration) ?

Thanks for your help

CodePudding user response:

I am afraid there is a difference between an XSLT error and a Schematron validation error or failed assertion. Saxon(CS) is simply an XSLT 3 or 2 processor running XSLT 3 or 2 code, that in your case seems to be an XSLT stylesheet performing a Schematron validation and producing an SVRL report as the transformation result, just like most XSLT transformations produce some XML or HTML result document(s).

There is no error occurring in the meaning of an XSLT run-time error, your XSLT (probably generated using XSLT from a Schematron file) just happens to create an SVRL report that can contain validation errors. But that is not something the XSLT processor would notice or report, it is up to you to further process the SVRL and check for failed assertions in the report. You can of course use XPath or XSLT or XQuery with Saxon(CS) again to do that. In https://github.com/martin-honnen/SchematronSchxsltSaxonHE11Net/blob/master/SchematronSchxsltSaxonHE11Net/Program.cs#L56 (using Saxon HE 11 cross-compiled with IKVM from Java to .NET) I use an XPath expression

not((/Q{http://purl.oclc.org/dsdl/svrl}schematron-output!(Q{http://purl.oclc.org/dsdl/svrl}failed-assert , Q{http://purl.oclc.org/dsdl/svrl}successful-report)))

to check whether the SVRL report contains no failed-asserts nor successful-reports to ensure the Schematron validation didn't report any errors.

There are also dedicated Schematron processing libraries but I am not sure you will find something in the .NET (Core) space supporting XPath or XSLT 2/3 based Schematron.

What might have confused you is parts of the API you are trying to use with e.g. SchemaValidationMode but that doesn't relate to Schematron at all, solely to schema-aware XSLT 2/3 performing XSD based validation of input and/or result documents.

  • Related