mdesalvo / RDFSharp

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

Creating NodeShapes with connected PropertyShapes #332

Closed StephanLupp closed 6 months ago

StephanLupp commented 6 months ago

Hello!

A few colleagues and I are currently working on creating SHACL shapes graphs to validate local RDF/XML files. We are using RDFSharp to create the necessary shapes, but are having problems with some of the rules we want to create.

Our goal is to create "closed" NodeShapes with defined properties (PropertyShapes), but have't found a way to connect a RDFNodeShape to a set of RDFPropertyShapes. (see example here: Shapes Constraint Language - Shapes Graph Example)

SHACL Shapes Graph Example

ex:PersonShape
    a sh:NodeShape ;
    sh:targetClass ex:Person ;    # Applies to all persons
    sh:property [                        # _:b1
        sh:path ex:ssn ;            # constrains the values of ex:ssn
        sh:maxCount 1 ;
        sh:datatype xsd:string ;
        sh:pattern "^\\d{3}-\\d{2}-\\d{4}$" ;
    ] ;
    sh:property [                 # _:b2
        sh:path ex:worksFor ;
        sh:class ex:Company ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:closed true ;
    sh:ignoredProperties ( rdf:type ) .

Questions

Current Workaround

  1. Create RDFNodeShape
  2. Create RDFPropertyShape
  3. Create an RDFShapes Graph
  4. Attach both shapes to the graph
  5. Convert the graph to an RDFGraph
  6. Attach an RDFTriple to the graph, that connects the PropertyShape as "property" to the NodeShape
  7. Save everything to a .ttl file and/or use the graph to validate data graphs
var Graph = new RDFShapesGraph();
var nodeShape = new RDFNodeShape(new RDFResource("http://iec.ch/TC57/2013/CIM-schema-cim16#CoordinateSystem"));
var PropertyShape = new RDFPropertyShape(new RDFResource("http://iec.ch/TC57/2013/CIM-schema-cim16#CoordinateSystem.crsUrn"), new RDFResource("http://iec.ch/TC57/2013/CIM-schema-cim16#CoordinateSystem.crsUrn"));

Graph.AddShape(nodeShape);
Graph.AddShape(PropertyShape);

var rdfGraph = Graph.ToRDFGraph();

rdfGraph.AddTriple(new RDFTriple(
    new RDFResource("http://iec.ch/TC57/2013/CIM-schema-cim16#CoordinateSystem"),
    new RDFResource("http://www.w3.org/ns/shacl#property"),
    new RDFResource("http://iec.ch/TC57/2013/CIM-schema-cim16#CoordinateSystem.crsUrn")));

rdfGraph.ToFile(RDFModelEnums.RDFFormats.Turtle, path_shacl_rules);

Result from Workaround

@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix sh: <http://www.w3.org/ns/shacl#>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
@base <https://rdfsharp.codeplex.com/>.

<http://iec.ch/TC57/2013/CIM-schema-cim16#CoordinateSystem> a sh:NodeShape; 
                                                            sh:deactivated "false"^^xsd:boolean; 
                                                            sh:property <http://iec.ch/TC57/2013/CIM-schema-cim16#CoordinateSystem.crsUrn>; 
                                                            sh:severity sh:Violation. 
<http://iec.ch/TC57/2013/CIM-schema-cim16#CoordinateSystem.crsUrn> a sh:PropertyShape; 
                                                                   sh:deactivated "false"^^xsd:boolean; 
                                                                   sh:path <http://iec.ch/TC57/2013/CIM-schema-cim16#CoordinateSystem.crsUrn>; 
                                                                   sh:severity sh:Violation. 

Wanted Features

We would love to have a way to directly add RDFPropertyShapes to RDFNodeShapes and to convert the resulting ShapesGraph to RDF etc..

mdesalvo commented 6 months ago

Hi Stephan, here is an implementation of the example turtle file showing usage of constraints and targets:

var shapesGraph = new RDFShapesGraph();

var nodeShape = new RDFNodeShape(new RDFResource("ex:PersonShape"));
nodeShape.AddTarget(new RDFTargetClass(new RDFResource("ex:Person")));
nodeShape.AddConstraint(new RDFPropertyConstraint(new RDFResource("ex:PropertyShapeSSN")));
nodeShape.AddConstraint(new RDFPropertyConstraint(new RDFResource("ex:PropertyShapeWorksFor")));
nodeShape.AddConstraint(new RDFClosedConstraint(true).AddIgnoredProperty(RDFVocabulary.RDF.TYPE));

var propertyShapeSSN = new RDFPropertyShape(new RDFResource("ex:PropertyShapeSSN"), new RDFResource("ex:ssn"));
propertyShapeSSN.AddConstraint(new RDFMaxCountConstraint(1));
propertyShapeSSN.AddConstraint(new RDFDatatypeConstraint(RDFModelEnums.RDFDatatypes.XSD_STRING));
propertyShapeSSN.AddConstraint(new RDFPatternConstraint(new Regex("^\\d{3}-\\d{2}-\\d{4}$")));

var PropertyShapeWorksFor = new RDFPropertyShape(new RDFResource("ex:PropertyShapeWorksFor"), new RDFResource("ex:worksFor"));
PropertyShapeWorksFor.AddConstraint(new RDFClassConstraint(new RDFResource("ex:Company")));
PropertyShapeWorksFor.AddConstraint(new RDFNodeKindConstraint(RDFValidationEnums.RDFNodeKinds.IRI));

shapesGraph.AddShape(nodeShape);
shapesGraph.AddShape(propertyShapeSSN);
shapesGraph.AddShape(PropertyShapeWorksFor);

It is of course possible to contribute to the library: posting questions, signalling issues, requesting new capabilities, also submitting PRs for targeting bugs or delivering new features.

Let me know if this coding example clarifies your doubts about usage of the library. Regards, marco

StephanLupp commented 6 months ago

Hello Marco,

You are a live-safer, this is exactly what I was looking for. Thanks for the very quick response and good example how to connect this. I never considered that "RDFPropertyConstraint" is used for this.

I do hope that we can contribute further, we will continue to use the library and post questions that we find or oddities that we notice.

Best Regards Stephan