mdesalvo / RDFSharp

Lightweight and friendly .NET library for realizing Semantic Web applications
Apache License 2.0
118 stars 26 forks source link

[BUG] Validation of `sh:path` with `sh:alternativePath` not working per the SHACL spec #304

Closed Pingviinituutti closed 1 year ago

Pingviinituutti commented 1 year ago

Describe the bug It seems that sh:alternativePath is not working according to the SHACL spec.

Expected behavior Link to SHACL Playground

sh:alternativePath should basically be an or to check that either one (or both) of the given paths should exist. So in this case for a ex:Person with sh:path [ sh:alternativePath ( ex:Person.mobilePhone ex:Person.landLine ) ], either the path ex:Person.MobilePhone or the path ex:Person.landLine should be given.

The issue is that RDFSharp triggers an sh:Violation for every a person with or without the any of the given paths.

Library Version RDFSharp version '3.5.0'

Additional context Minimal working example:

# SHACL_constraints_person.ttl
@prefix ex:     <http://example.com/ns#> .
@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix sh:      <http://www.w3.org/ns/shacl#> .

ex:Person  rdf:type  sh:NodeShape ;
        sh:property     ex:Person.mobile-landline-cardinality ;
        sh:targetClass  ex:Person .

ex:Person.mobile-landline-cardinality
        rdf:type        sh:PropertyShape ;
        sh:description  "This constraint validates that a Person has either mobileNumber or landLine set." ;
        sh:minCount     1 ;
        sh:message      "Either mobileNumber or landLine should be present" ;
        sh:name         "Person.mobileNumber-landLine-cardinality" ;
        sh:order        0 ;
        sh:path         [ sh:alternativePath ( ex:Person.mobilePhone ex:Person.landLine ) ] ;
        sh:severity     sh:Violation .
<!-- person.xml -->
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:ex="http://example.com/ns#"
    >

    <ex:Person rdf:about="http://example.com/ns#person0">
        <ex:Person.mobilePhone>0123456789</ex:Person.mobilePhone>
    </ex:Person>

    <ex:Person rdf:about="http://example.com/ns#person1">
        <ex:Person.landLine>0123456789</ex:Person.landLine>
    </ex:Person>

    <ex:Person rdf:about="http://example.com/ns#person2">
        <ex:Person.mobilePhone>0123456789</ex:Person.mobilePhone>
        <ex:Person.landLine>0123456789</ex:Person.landLine>
    </ex:Person>

    <ex:Person rdf:about="http://example.com/ns#person3">
    </ex:Person>
</rdf:RDF>
// Program.cs
using RDFSharp.Model;
using System;
using System.Text;

string shapesValidationFilePath = "SHACL_constraints_person.ttl";
string dataFilePath = "person.xml";

try
{
    RDFGraph dataGraph = RDFGraph.FromFile(RDFModelEnums.RDFFormats.RdfXml, dataFilePath);

    RDFGraph shapesGraphObject = RDFGraph.FromFile(
        RDFModelEnums.RDFFormats.Turtle,
        shapesValidationFilePath
    );
    RDFShapesGraph shapesGraphValidation = RDFShapesGraph.FromRDFGraph(shapesGraphObject);
    //validate
    StringBuilder sb = new StringBuilder();
    RDFValidationReport validationResults = shapesGraphValidation.Validate(dataGraph);
    if (!validationResults.Conforms)
    {
        foreach (RDFValidationResult result in validationResults)
        {
            foreach (RDFLiteral resultMessage in result.ResultMessages)
            {
                sb.AppendLine(result.ResultPath.URI.Fragment + " " + resultMessage.ToString() + ". ID: " + result.FocusNode.ToString());
            }
        }
        Console.WriteLine("RDFSharp Validation errors:");
        string errors = sb.ToString();
        Console.WriteLine(errors);
    }
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Results:

RDFSharp Validation errors:
 Either mobileNumber or landLine should be present. ID: http://example.com/ns#person0
 Either mobileNumber or landLine should be present. ID: http://example.com/ns#person1
 Either mobileNumber or landLine should be present. ID: http://example.com/ns#person2
 Either mobileNumber or landLine should be present. ID: http://example.com/ns#person3

Expected: only person3 should throw an sh:Violation.

mdesalvo commented 1 year ago

Hi Henrik, thanks for the report (precise as usual).

I'm working on this to add support for "sh:alternativePath" syntax and will let you know here the progress.

Regards, Marco

mdesalvo commented 1 year ago

Hi Henrik, I merged the work for proper detection and handling of "sh:path" declared with "sh:alternativePath", which should allow you to successfully deal with this use case. It was my glitch (again) to not know this kind of syntax.

Expect a new release containing also this feature in the next days.

Thanks again and regards, Marco

Pingviinituutti commented 1 year ago

Hi Marco!

No problems here! Thanks a bunch for adding the fix :)

Best regards, Henrik

mdesalvo commented 1 year ago

I'm also adding "sh:path (prop1 prop2)" which I saw is the sequential form of paths.

Other variants can't be supported (like the ones stating cardinality with "+" or "*") because unfortunately our algebra is not capable of computing them neither in SPARQL. But, at least, with your support our SHACL engine has improved nicely :)

Thanks and regards, Marco