Last active
April 25, 2020 10:45
-
-
Save EmmanuelOga/2fcb3a76ad58c0a73122544306d4b3fb to your computer and use it in GitHub Desktop.
Validate ShEx from Clojure usign the Shaclex scala library and Jena. Started as a port of https://github.com/weso/simpleShExScala/blob/master/src/main/scala/es/weso/simpleShEx/Main.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{:paths | |
["src" "test" "target" "resources"] | |
:mvn/repos | |
{"bintray" {:url "https://dl.bintray.com/labra/maven/"}} | |
:deps | |
{; Clojure | |
org.clojure/clojure {:mvn/version "1.10.1"} | |
; Shaclex | |
es.weso/shaclex_2.13 {:mvn/version "0.1.44" :extension "pom"} | |
; Jena | |
org.apache.jena/jena {:mvn/version "3.13.1" :extension "pom"}}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[{:node "<http://example.org/alice>", | |
:shape "<http://example.org/User>", | |
:status "conformant", | |
:appInfo "Shaclex", | |
:reason | |
"Alice has datatype xsd:string\nschema:Female is equal to schema:Female\nschema:Female passes OR\n:bob is an IRI"} | |
{:node "<http://example.org/bob>", | |
:shape "<http://example.org/User>", | |
:status "conformant", | |
:appInfo "Shaclex", | |
:reason | |
"Robert has datatype xsd:string\n1980-03-10 has datatype xsd:date\nschema:Male is equal to schema:Male\nschema:Male passes OR\nRobert has datatype xsd:string\n1980-03-10 has datatype xsd:date\nschema:Male is equal to schema:Male\nschema:Male passes OR"} | |
{:node "<http://example.org/carol>", | |
:shape "<http://example.org/User>", | |
:status "conformant", | |
:appInfo "Shaclex", | |
:reason | |
"Carol has datatype xsd:string\nunspecified has datatype xsd:string\nunspecified passes OR"}] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@prefix : <http://example.org/> | |
@prefix schema: <http://schema.org/> | |
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> | |
@prefix foaf: <http://xmlns.com/foaf/0.1/> | |
:alice | |
schema:name "Alice" ; | |
schema:gender schema:Female ; | |
schema:knows :bob . | |
:bob schema:gender schema:Male ; | |
schema:name "Robert"; | |
schema:birthDate "1980-03-10"^^xsd:date . | |
:carol schema:name "Carol"; | |
schema:gender "unspecified" ; | |
foaf:name "Carol" . |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PREFIX : <http://example.org/> | |
PREFIX schema: <http://schema.org/> | |
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> | |
:User { | |
schema:name xsd:string ; | |
schema:birthDate xsd:date? ; | |
schema:gender [ schema:Male schema:Female ] OR xsd:string ; | |
schema:knows IRI @:User* | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns rdfex.jena | |
(:require [clojure.java.io :as io]) | |
(:import [org.apache.jena.rdf.model Model ModelFactory])) | |
(def ^:dynamic rdf-base-url | |
"https://emmanueloga.com") | |
(defn create-empty-model | |
[] | |
(ModelFactory/createDefaultModel)) | |
(defn parse | |
([input] (parse input rdf-base-url "TURTLE")) | |
([input base-url format] | |
(let [model (create-empty-model) | |
input-stream (io/input-stream input)] | |
(.read model input-stream base-url format)))) | |
(defn write | |
([model] (write model "JSON-LD")) | |
([model format] | |
(let [buffer (java.io.ByteArrayOutputStream.)] | |
(.write model buffer format) | |
(str buffer)))) | |
(def ^:dynamic *max-print-prefixes* 50) | |
; Extend print to understand Jena models. | |
(defmethod print-method Model [v ^java.io.Writer w] | |
(let [graph (.getGraph v) | |
sample (create-empty-model) | |
size (.size graph) | |
statements (iterator-seq (.listStatements v))] | |
(.setNsPrefixes sample (.getPrefixMapping graph) ) | |
(.write w (str "Jena, " size " statements. Sample:\n\n")) | |
(run! (fn [s] (.add sample s)) (take *max-print-prefixes* statements)) | |
(.write w (write sample "N3")))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns rdfex.shex | |
(:require [clojure.data.json :as json] | |
[clojure.java.io :as io] | |
[rainbowfish.jena :as jena]) | |
(:import es.weso.rdf.jena.RDFAsJenaModel | |
es.weso.shapeMaps.ShapeMap$ | |
es.weso.shex.Schema$ | |
es.weso.shex.validator.Validator$ | |
org.apache.jena.rdf.model.Model | |
[scala Option Some])) | |
(defn $either | |
"Attempt to return the right side of a scala Either type. | |
May raise a left if the right is not available." | |
[either] | |
(try | |
(-> either (.right) (.get)) | |
(catch Exception e | |
(throw (ex-info "Unexpected value" {:value (-> either (.left) (.get))}))))) | |
(defn $wrap | |
"Wrap a value in a scala Option: either empty or (Some. value)" | |
[value] | |
(if value (Some. value) (Option/empty))) | |
(defn get-srdf | |
"Shaclex uses Srdf, which wraps Jena in a custom interface" | |
([model] | |
(get-srdf model nil nil)) | |
([model base src-iri] | |
(cond | |
(instance? org.apache.jena.rdf.model.Model model) | |
(RDFAsJenaModel. model ($wrap base) ($wrap src-iri)) | |
:else | |
(throw (ex-info "Unsupported model" {::model model}))))) | |
(defn parse-shex | |
"Parses the Shex schema provided on the src on a given format" | |
([^String src] | |
(parse-shex src "ShexC" nil nil)) | |
([^String src format schema-name base] | |
($either | |
(.fromString Schema$/MODULE$ src "ShexC" ($wrap schema-name) ($wrap base))))) | |
(defn parse-shape-map | |
"Parses the shape map on the src with the given format" | |
([^String src srdf-pfx shex-pfx] | |
(parse-shape-map | |
src (.defaultFormat ShapeMap$/MODULE$) srdf-pfx shex-pfx nil)) | |
([^String src format srdf-pfx shex-pfx base] | |
($either | |
(.fromString ShapeMap$/MODULE$ src format ($wrap base) srdf-pfx shex-pfx)))) | |
(defn fix-shape-map | |
"Fixes the shape map into a form usable by the validator" | |
[shape-map srdf srdf-pfx shex-pfx] | |
($either | |
(.fixShapeMap ShapeMap$/MODULE$ shape-map srdf srdf-pfx shex-pfx))) | |
(defn validate | |
"Validates an RDF model with a given shex schema and shape map" | |
[model ^String shex-compact ^String shape-map] | |
(let [srdf (get-srdf model) | |
shex (parse-shex shex-compact) | |
srdf-pfx (.getPrefixMap srdf) | |
shex-pfx (.prefixMap shex) | |
shape-map (parse-shape-map shape-map srdf-pfx shex-pfx) | |
fixed-shape-map (fix-shape-map shape-map srdf srdf-pfx shex-pfx)] | |
(-> | |
($either (.validate Validator$/MODULE$ shex fixed-shape-map srdf)) | |
(.toJson) | |
(.toString) | |
(json/read-str :key-fn keyword)))) | |
; Example: | |
(let result | |
(let [model (jena/parse (io/resource "sandbox/example.ttl")) | |
result (validate-jena | |
model | |
(slurp (io/resource "schemas/example.shex")) | |
":alice@:User,:bob@:User,:carol@:User")] | |
result)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment