mdesalvo / RDFSharp

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

[BUG] RDF/XML elements with `rdf:ID` sets subject URI improperly. #307

Closed Pingviinituutti closed 1 year ago

Pingviinituutti commented 1 year ago

Describe the bug Validating cardinalities on instances defined with rdf:ID instead of rdf:about is not consistent.

Expected behavior A required child-parent association is expected to validate the same way independently of if the child's or parent's subject URI is set with rdf:ID or rdf:about. See the shacl playgrounds here:

I noticed that when I added a # in front of the rdf:ID value, i.e., #parent0, then the error did not appear anymore (based on the minimal working example below). It looks like the IRI for an element with rdf:ID set is not concatenated with an # as described in the w3c RDF 1.1 XML Syntax recommendations Section 2.14 (second paragraph)?

Library Version RDFSharp 3.6.0

Additional context Minimal working example.

# SHACL constraints - SHACL_parent_child.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:Parent  rdf:type  sh:NodeShape ;
        sh:property     ex:Parent.Child-cardinality ;
        sh:targetClass  ex:Parent .

ex:Parent.Child-cardinality
        rdf:type        sh:PropertyShape ;
        sh:description  "This constraint validates the cardinality of the association at the inverse direction." ;
        sh:group        ex:CardinalityGroup ;
        sh:message      "Cardinality violation. Lower bound shall be 1. ex:Parent.Child-cardinality" ;
        sh:minCount     1 ;
        sh:name         "Parent.Child-cardinality" ;
        sh:order        0 ;
        sh:path     [ sh:inversePath ex:Child.Parent ] ;
        sh:minCount 1 ;
        sh:severity     sh:Violation .

ex:Child  rdf:type  sh:NodeShape ;
        sh:property     ex:Child.Parent-cardinality ;
        sh:targetClass  ex:Child .

ex:Child.Parent-cardinality
        rdf:type        sh:PropertyShape ;
        sh:description  "This constraint validates the cardinality of the association at the inverse direction." ;
        sh:group        ex:CardinalityGroup ;
        sh:message      "Cardinality violation. A child should have at least one parent. ex:Parent.Child-cardinality" ;
        sh:minCount     1 ;
        sh:name         "Parent.Child-cardinality" ;
        sh:order        0 ;
        sh:path         ex:Child.Parent ;
        sh:severity     sh:Violation .
<!-- data - parent-child.xml -->
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF
    xml:base="http://test.com/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:ex="http://example.com/ns#"
    >

    <ex:Child rdf:ID="child">
        <ex:Child.Parent rdf:resource="#parent0" />
    </ex:Child>

    <ex:Parent rdf:ID="parent0" />

    <ex:Parent rdf:ID="parent1" />

    <ex:Child rdf:about="#child10">
        <ex:Child.Parent rdf:resource="#parent10" />
    </ex:Child>

    <ex:Parent rdf:about="#parent10" />

    <ex:Parent rdf:about="#parent11" />
</rdf:RDF>
// sample Program.cs
// See https://aka.ms/new-console-template for more information
using RDFSharp.Model;
using System;
using System.Text;

string shapesValidationFilePath = "SHACL_parent_child.ttl";
string dataFilePath = "parent-child.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);
}

This outputs

DFSharp Validation errors:
#Child.Parent Cardinality violation. Lower bound shall be 1. ex:Parent.Child-cardinality. ID: http://test.com/parent0
#Child.Parent Cardinality violation. Lower bound shall be 1. ex:Parent.Child-cardinality. ID: http://test.com/parent1
#Child.Parent Cardinality violation. Lower bound shall be 1. ex:Parent.Child-cardinality. ID: http://test.com/#parent11

It is expected that the element with id parent0 should not trigger an sh:Violation.

mdesalvo commented 1 year ago

Hi Henrik, thanks as usual for the detailed report: it makes me working on the issue at minute 0!

I posted a fix for the incorrect parsing of rdf:ID elements in RDF/XML. Nice catch. I'll do a bugfix release for this.

Regards, Marco

Pingviinituutti commented 1 year ago

Hi Marco!

Thanks again for your effort! Much appreciated! :)

Best regards, Henrik