Before we use data we have to trust it.
Folks responsible for clinical data are especially conservative/sensitive/paranoid.
How do we enforce data integrity?
SQL | XML | |
---|---|---|
missing properties | reportedBy UNSIGNED INT NOT NULL | element reportedBy { User }, |
missing/bad type arcs | N/A | N/A |
missing referents | FOREIGN KEY (reportedBy) REFERENCES Users(ID) | <keyref refer="UserID"> |
inconsistent state | CHECK(assignedOn>reportedOn) | [schematron] |
value set violations | ENUM('unasigned', 'assigned') | attribute state { "unassigned" | "assigned" } |
Useful data needs consistent structure:
Detect and correct errors:
@prefix : <http://www.w3.org/2012/12/rdf-val/SOTA-ex#> . @prefix foaf: <http://xmlns.com/foaf/0.1/'> . @prefix xsd: <http://www.w3.org/2001/XMLSchema#> . <issue7> a :Issue , :SecurityIssue ; :state :unassigned ; :reportedBy <user6> , <user2> ; # cardinality 1 :reportedOn "2012-12-31T23:57:00"^^xsd:dateTime ; :assignedTo <user2>, <user1> ; :assignedOn "2012-11-31T23:57:00"^^xsd:dateTime ; # reproduced before being reported :related <issue4>, <issue3>, <issue2> . # referenced issues not included <issue4> # a ??? missing type arc :state :unsinged ; # misspelled # :reportedBy ??? - missing :reportedOn "2012-12-31T23:57:00"^^xsd:dateTime . <user2> a foaf:Person ; foaf:givenName "Alice" ; foaf:familyName "Smith" ; foaf:phone <tel:+1.555.222.2222> ; foaf:mbox <mailto:alice@example.com> . <user6> a foaf:Agent ; # should be foaf:Person foaf:givenName "Bob" ; # foaf:familyName "???" - missing foaf:phone <tel:+.555.222.2222> ; # malformed tel: URL foaf:mbox <mailto:alice@example.com> .
in order of specificity:
<Obs1> :component [ :code "systolic"; :value "140"^^:mmHg ] .
<BPShape> a rs:ResourceShape ; rs:property [ rs:name "component" ; rs:propertyDefinition :component ; rs:valueShape [ a rs:ResourceShape ; rs:property [ rs:name "code" ; rs:propertyDefinition :code ; rs:allowedValue "systolic" ; rs:occurs rs:Exactly-one ; ] ; rs:property [ rs:name "value" ; rs:propertyDefinition :value ; rs:valueType :mmHg ; rs:occurs rs:Exactly-one ; ] ] ; rs:occurs rs:Exactly-one ; ] .
PREFIX : <http://a.example/#> PREFIX sh:<http://www.w3.org/ns/shacl#> <BPShape> a sh:Shape ; sh:property [ sh:predicate :component ; sh:minCount 1 ; sh:maxCount 1 ; sh:shape [ sh:property [ sh:predicate :code ; sh:in ("systolic") ; sh:minCount 1 ; sh:maxCount 1 ; ] ; sh:property [ sh:predicate :value ; sh:valueType :mmHg ; sh:minCount 1 ; sh:maxCount 1 ; ] ] ; sh:minCount 1 ; sh:maxCount 1 ; ] .
visible diffs from Resource Shapes:
(many more diffs not seen in these examples)
<Obs1> :component [ :code "systolic"; :value "140"^^:mmHg ] :component [ :code "diastolic"; :value "80"^^:mmHg ] .
<BPShape> a rs:ResourceShape ; rs:property [ rs:name "component" ; rs:propertyDefinition :component ; rs:valueShape [ a rs:ResourceShape ; rs:property [ rs:name "code" ; rs:propertyDefinition :code ; rs:allowedValue "systolic" ; rs:occurs rs:Exactly-one ; ] ; rs:property [ rs:name "value" ; rs:propertyDefinition :value ; rs:valueType :mmHg ; rs:occurs rs:Exactly-one ; ] ] ; rs:occurs rs:Exactly-one ; ] ;
… rs:property [ rs:name "component" ; rs:propertyDefinition :component ; rs:valueShape [ a rs:ResourceShape ; rs:property [ rs:name "code" ; rs:propertyDefinition :code ; rs:allowedValue "diastolic" ; rs:occurs rs:Exactly-one ; ] ; rs:property [ rs:name "value" ; rs:propertyDefinition :value ; rs:valueType :mmHg ; rs:occurs rs:Exactly-one ; ] ] ; rs:occurs rs:Exactly-one ; ] .
<Obs1> :component [ :code "systolic"; :value "140"^^:mmHg ] :component [ :code "diastolic"; :value "80"^^:mmHg ] .
PREFIX : <http://a.example/#> PREFIX sh:<http://www.w3.org/ns/shacl#> <BPShape> a sh:Shape ; sh:property [ sh:predicate :component ; sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ; sh:qualifiedShape [ sh:property [ sh:predicate :code ; sh:in ("systolic") ; sh:minCount 1 ; sh:maxCount 1 ; ] ; sh:property [ sh:predicate :value ; sh:valueType :mmHg ; sh:minCount 1 ; sh:maxCount 1 ; ] ] ; ] ;
… sh:property [ sh:predicate :component ; sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ; sh:qualifiedShape [ sh:property [ sh:predicate :code ; sh:in ("diastolic") ; sh:minCount 1 ; sh:maxCount 1 ; ] ; sh:property [ sh:predicate :value ; sh:valueType :mmHg ; sh:minCount 1 ; sh:maxCount 1 ; ] ] ; ] .
<Obs1> :component [ :code "systolic"; :value "140"^^:mmHg ] :component [ :code "diastolic"; :value "80"^^:mmHg ] .
<Obs2> :component [ :code "systolic"; :value "140"^^:mmHg ] :component [ :code "diastolic"; :value "80"^^:mmHg ] :component "something invalid" .
PREFIX : <http://a.example/#> PREFIX sh:<http://www.w3.org/ns/shacl#> <BPShape> a sh:Shape ; sh:property [ sh:predicate :component ; sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ; sh:qualifiedShape [ … ] ; sh:minCount 1 ; sh:maxCount 1 ; ] ; sh:property [ sh:predicate :component ; sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ; sh:qualifiedShape [ … ] ; ] .
sh:property [ sh:predicate :component ; sh:Shape [ sh:Or ( [ sh:property [ sh:predicate :code ; sh:in ("systolic") ; sh:minCount 1 ; sh:maxCount 1 ; ] ; sh:property [ sh:predicate :value ; sh:valueType :mmHg ; sh:minCount 1 ; sh:maxCount 1 ; ] ] [ sh:property [ sh:predicate :code ; sh:in ("diastolic") ; sh:minCount 1 ; sh:maxCount 1 ; ] ; sh:property [ sh:predicate :value ; sh:valueType :mmHg ; sh:minCount 1 ; sh:maxCount 1 ; ] ] ) ] ] .
PREFIX : <http://a.example/#> <BPShape> { :component { :code ["systolic"]; :value :mmHg } }
PREFIX : <http://a.example/#> <BPShape> { :component { :code ["systolic"]; :value :mmHg } ; :component { :code ["diastolic"]; :value :mmHg } }
What is Shape Expressions?
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX myco: <http://myco.example/#> start = <IssueShape> <IssueShape> { issue:status (issue:unassigned issue:assigned), issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<EmployeeShape>, issue:assignedOn xsd:dateTime )? } <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI } <EmployeeShape> { foaf:page (myco:Employee~), foaf:givenName LITERAL+, foaf:familyName LITERAL, foaf:phone IRI*, foaf:mbox IRI }
What's my user profile look like?
PREFIX foaf: <http://xmlns.com/foaf/> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
What's my user profile look like?
PREFIX foaf: <http://xmlns.com/foaf/> <UserShape> { (foaf:name LITERAL |foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
What's my user profile look like?
PREFIX foaf: <http://xmlns.com/foaf/> <UserShape> { (foaf:name LITERAL |foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
compare with other schema languages...
(element foaf:name { xsd:string } | (element foaf:givenName { xsd:string }+, element foaf:familyName { xsd:string })), element foaf:mbox { xsd:anyURI }
(N|(G+F))M
NM
GFM
GGGFM
<xs:complexType name="UserContent"> <xs:sequence> <xs:choice> <xs:element name="name" type="xs:string"/> <xs:sequence> <xs:element maxOccurs="unbounded" name="givenName" type="xs:string"/> <xs:element name="familyName" type="xs:string"/> </xs:sequence> </xs:choice> <xs:element name="mbox" type="xs:anyURI"/> </xs:sequence> </xs:complexType>
Trying it out
PREFIX foaf: <http://xmlns.com/foaf/> start = <UserShape> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
Try it at ericP's indulgent interface
or Jose's more correct interface:
<Alice>,
<Bob>,
<Gene>,
<Pat>,
<Eve> (passes, OR is non-exclusive)
PREFIX foaf: <http://xmlns.com/foaf/> <Alice> foaf:name "Alice" ; foaf:mbox <mailto:alice@example.org> . <Bob> foaf:givenName "Robert", "Edward" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> . <Gene> foaf:givenname "Gene" ; foaf:familyName "Smith" ; # misspelling foaf:mbox <mailto:gene@example.org> . <Pat> foaf:givenName "Patricia" ; # no foaf:familyName foaf:mbox <mailto:pat@example.org> . <Eve> foaf:name "Eve" ; # both name and givenName/familyName foaf:givenName "Eve" ; foaf:familyName "石川" ; foaf:mbox <mailto:eve@example.org> .
Fix Gene, Pat and Eve
PREFIX foaf: <http://xmlns.com/foaf/> start = <UserShape> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
PREFIX foaf: <http://xmlns.com/foaf/> <Gene> foaf:givenname "Gene" ; foaf:familyName "Smith" ; foaf:mbox <mailto:gene@example.org> . <Pat> foaf:givenName "Patricia" ; foaf:mbox <mailto:pat@example.org> . <Eve> foaf:name "Eve" ; foaf:givenName "Eve" ; foaf:familyName "石川" ; foaf:mbox <mailto:eve@example.org> .
Now break it;
it's more fun to demonstrate what validators whine about than what they accept.
Fixed Gene, Pat and Eve
PREFIX foaf: <http://xmlns.com/foaf/> start = <UserShape> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
PREFIX foaf: <http://xmlns.com/foaf/> <Gene> foaf:givenName "Gene" ; foaf:familyName "Smith" ; foaf:mbox <mailto:gene@example.org> . <Pat> foaf:givenName "Patricia" ; foaf:familyName "Gordon" ; foaf:mbox <mailto:pat@example.org> . <Eve1> foaf:name "Eve" ; foaf:mbox <mailto:eve@example.org> . <Eve2> foaf:givenName "Eve" ; foaf:familyName "石川" ; foaf:mbox <mailto:eve@example.org> .
ShExC is a BNF for RDF:
Issue: reportedOn reportedBy ( assignedOn assignedTo )? reportedBy: User assignedTo: User User: ( name | givenName+ familyName ) mbox
example instance
<Issue1> issue:reportedBy <Bob> ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime . <Bob> foaf:givenName "Robert", "Edward" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> .
ShExC is a BNF for RDF:
start = <IssueShape> <IssueShape> { issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<UserShape>, issue:assignedOn xsd:dateTime )? } <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
example instance
<Issue1> issue:reportedBy <Bob> ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime . <Bob> foaf:givenName "Robert", "Edward" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> .
Schema:
PREFIX : <http://a.example/#> <BPShape> { :component { :code ["systolic"]; :value :mmHg }; :component { :code ["diastolic"]; :value :mmHg } }
Nested patterns can be labeled:
<BPShape> { :component @<systolicComponent>; :component @<diastolicComponent> } <systolicComponent> { :code ["systolic"]; :value :mmHg } <diastolicComponent> { :code ["diastolic"]; :value :mmHg }
Data:
<Obs1> :component [ :code "systolic"; :value "140"^^:mmHg ] :component [ :code "diastolic"; :value "80"^^:mmHg ] .
Decompose this schema:
PREFIX obs: <http://hl7.org/fhir/Observations.> <Observation> { obs:code @<CodeableConcept>; ( obs:valueQuantity @<Quantity> | obs:valueCodeableConcept @<CodeableConcept> )?; obs:component { obs:component.code @<CodeableConcept>; ( obs:component.valueQuantity @<Quantity> | obs:component.valueCodeableConcept @<CodeableConcept> )?; }*; }
Split the obs:component object into a <Observation.component>.
example instance:
<Obs1> obs:code [ cc:coding [ cd:system [ fhir:value "http://a.example/sys1"; fhir:code "code1" ] ] ]; obs:valueCodeableConcept [ cc:coding [ cd:system [ fhir:value "http://a.example/sys1"; fhir:code "code2" ] ] ]; obs:component [ obs:component.code [ cc:coding [ cd:system [ fhir:value "http://a.example/sys1"; fhir:code "code3" ] ] ]; obs:component.valueQuantity [ fhir:Quantity.value [ fhir:value 1.23] ]; ] .
Decomposed schema:
PREFIX obs: <http://hl7.org/fhir/Observations.> <Observation> { obs:code @<CodeableConcept>; ( obs:valueQuantity @<Quantity> | obs:valueCodeableConcept @<CodeableConcept> )?; obs:component @<Observation.component>*; } <Observation.component> { obs:component.code @<CodeableConcept>; ( obs:component.valueQuantity @<Quantity> | obs:component.valueCodeableConcept @<CodeableConcept> )?; }
Create a <EmployeeShape> distinct from an <UserShape>.
example instance:
<Obs1> obs:code [ cc:coding [ cd:system [ fhir:value "http://a.example/sys1"; fhir:code "code1" ] ] ]; obs:valueCodeableConcept [ cc:coding [ cd:system [ fhir:value "http://a.example/sys1"; fhir:code "code2" ] ] ]; obs:component [ obs:component.code [ cc:coding [ cd:system [ fhir:value "http://a.example/sys1"; fhir:code "code3" ] ] ]; obs:component.valueQuantity [ fhir:Quantity.value [ fhir:value 1.23] ]; ] .
A value set is a set of possible values.
issue:assigned~
matches
issue:assignedBy
,
issue:assignedTo
,
issue:assignedOn
.
ex:mood ["happy" "sad" "indigo"]
ex:mood [mood:happy mood:sad mood:indigo]
ex:mood [mood:~]
Schema:
PREFIX fhir: <http://hl7.org/fhir/> PREFIX fhirvs: <http://hl7.org/fhir/ValueSet/> <Observation> { a [fhir:Observation]; fhir:nodeRole [fhir:treeRoot]?; fhir:DomainResource.text { fhir:Narrative.status @<code> AND {fhir:value .; # <-- fhir:Narrative.div LITERAL; }?; fhir:Observation.status @<code> AND {fhir:value .; # <-- fhir:Observation.related { fhir:Observation.related.type @<code> AND {fhir:value .?; # <-- fhir:Observation.related.target { fhir:link @<Observation> OR CLOSED {a [fhir:Observation]}; } }*; } <code> NONLITERAL { fhir:value LITERAL?; }
Data:
PREFIX fhir: <http://hl7.org/fhir/> <Obs1> a fhir:Observation ; fhir:nodeRole fhir:treeRoot ; fhir:DomainResource.text [ fhir:Narrative.status [ fhir:value "generated" ]; fhir:Narrative.div "blah blah" ; ]; fhir:Observation.status [ fhir:value "registered" ] ; fhir:Observation.related [ fhir:Observation.related.type [ fhir:value "replaces" ] ; fhir:Observation.related.target [ fhir:link <Obs2> ] ] . <Obs2> a fhir:Observation .
Schema:
PREFIX fhir: <http://hl7.org/fhir/> PREFIX fhirvs: <http://hl7.org/fhir/ValueSet/> <Observation> { a [fhir:Observation]; fhir:nodeRole [fhir:treeRoot]?; fhir:DomainResource.text { fhir:Narrative.status @<code> AND {fhir:value @fhirvs:narrative-status}; fhir:Narrative.div LITERAL; }?; fhir:Observation.status @<code> AND {fhir:value @fhirvs:observation-status}; fhir:Observation.related { fhir:Observation.related.type @<code> AND {fhir:value @fhirvs:observation-relationshiptypes}?; fhir:Observation.related.target { fhir:link @<Observation> OR CLOSED {a [fhir:Observation]}; } }*; } <code> NONLITERAL { fhir:value LITERAL?; } fhirvs:narrative-status ["generated" "extensions" "additional" "empty"] fhirvs:observation-status ["registered" "preliminary" "final" "amended" "cancelled" "entered-in-error" "unknown"] fhirvs:observation-relationshiptypes ["has-member" "derived-from" "sequel-to" "replaces" "qualified-by" "interfered-by"]
Data:
PREFIX fhir: <http://hl7.org/fhir/> <Obs1> a fhir:Observation ; fhir:nodeRole fhir:treeRoot ; fhir:DomainResource.text [ fhir:Narrative.status [ fhir:value "generated" ]; fhir:Narrative.div "blah blah" ; ]; fhir:Observation.status [ fhir:value "registered" ] ; fhir:Observation.related [ fhir:Observation.related.type [ fhir:value "replaces" ] ; fhir:Observation.related.target [ fhir:link <Obs2> ] ] . <Obs2> a fhir:Observation .
fhirvs:narrative-status ["generated" "extensions" "additional" "empty"]
fhirvs:claim-type { fhir:Coding.system { fhir:value ["http://hl7.org/fhir/ex-claimtype"]}; fhir:Coding.code { fhir:value ["oral" "institutional" "pharmacy" "professional" "vision"] } }
fhirvs:device-action EXTRA fhir:CodeableConcept.coding { fhir:CodeableConcept.coding { fhir:Coding.system {fhir:value ["http://hl7.org/fhir/device-action"]}?; fhir:Coding.code {fhir:value ["implanted" "explanted" "manipulated"]} } }
Schema:
PREFIX fhir: <http://hl7.org/fhir/> PREFIX fhirvs: <http://hl7.org/fhir/ValueSet/> fhirvs:device-action EXTRA fhir:CodeableConcept.coding { fhir:CodeableConcept.coding { fhir:Coding.system {fhir:value ["http://hl7.org/fhir/device-action"]}?; fhir:Coding.code {fhir:value ["implanted" "explanted" "manipulated"]} } }
Data:
PREFIX fhir: <http://hl7.org/fhir/> <da1> fhir:CodeableConcept.coding [ fhir:Coding.system [fhir:value "http://hl7.org/fhir/device-action"]; fhir:Coding.code [fhir:value "implanted"] ]; fhir:CodeableConcept.coding [ ] .
Test arcs coming into an object.
Useful for e.g. two Observations connected through a common Encounter.
PREFIX issue: <http://ex.example/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:status (issue:assigned issue:resolved ), issue:reportedOn xsd:dateTime, issue:assignedOn xsd:dateTime, issue:related @<RefdIssueShape> } <RefdIssueShape> { issue:name LITERAL, ^issue:related <IssueShape> }
PREFIX issue: <http://ex.example/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> issue:status issue:assigned ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime ; issue:related <Issue3> . <Issue3> issue:name "smokey" .
Use case: storing application data in a
"If I tell you X, will you understand it?"
What about re-used nodes?
Closed shapes:
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:reportedBy @<UserShape>, issue:assignedTo @<EmployeeShape>? } <UserShape> { foaf:name LITERAL foaf:mbox IRI } <EmployeeShape> { foaf:givenName LITERAL+, foaf:familyName LITERAL, foaf:mbox IRI }
<User2> fits multiple shapes:
PREFIX ex: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:reportedBy <User2> ; ex:assignedTo <User2> ; <User2> foaf:name "Bob Smith" ; foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> .
For extensibility:
For actions:
see ShExMap docs
Provides purpose-fit expressivity with minimal implementation cost.
Can count on order.
Javascript API actions embedded in %js{ … %}.
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:state (issue:unassigned issue:assigned), issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime %js{ report = _.o; return true; %}, (issue:reproducedBy @<EmployeeShape>, issue:reproducedOn xsd:dateTime %js{ return _.o.lex > report.lex; %} ), issue:related @<IssueShape>* } <UserShape> { (foaf:name xsd:string | foaf:givenName xsd:string+, foaf:familyName xsd:string), foaf:mbox IRI } <EmployeeShape> { foaf:givenName xsd:string+, foaf:familyName xsd:string, foaf:phone IRI*, foaf:mbox IRI }
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:state ex:unassigned ; ex:reportedBy <User2> ; ex:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; ex:reproducedBy <Thompson.J> ; ex:reproducedOn "2013-01-23T10:00:00"^^xsd:dateTime . <User2> foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mail:bob@example.org> . <Thompson.J> foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:phone <tel:+456> ; foaf:mbox <mail:joe@example.org> .
Isn't there an RDF query language?
%sparql{ … %}
can complement our %js{ … %} actions.
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:state (issue:unassigned issue:assigned), issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime %js{ report = _.o; return true; %}, (issue:reproducedBy @<EmployeeShape>, issue:reproducedOn xsd:dateTime %js{ return _.o.lex > report.lex; %} %sparql{ ?s issue:reportedOn ?rpt . FILTER (?o > ?rpt) %} ), issue:related @<IssueShape>* } <UserShape> { (foaf:name xsd:string | foaf:givenName xsd:string+, foaf:familyName xsd:string), foaf:mbox IRI } <EmployeeShape> { foaf:givenName xsd:string+, foaf:familyName xsd:string, foaf:phone IRI*, foaf:mbox IRI }
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:state ex:unassigned ; ex:reportedBy <User2> ; ex:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; ex:reproducedBy <Thompson.J> ; ex:reproducedOn "2013-01-23T10:00:00"^^xsd:dateTime . <User2> foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mail:bob@example.org> . <Thompson.J> foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:phone <tel:+456> ; foaf:mbox <mail:joe@example.org> .
Semantic actions can have side-effects.
PREFIX ex: <http://ex.example/#> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> %js{ action.arguments[1].message("in init"); doc = document.implementation.createDocument ("http://ex.example/xml", "Issue", undefined); issue = doc.childNodes[0]; %} start = <IssueShape> <IssueShape> { ex:state (ex:unassigned ex:assigned) %js{ issue.setAttribute("id", _.s.lex); issue.setAttribute("state", _.o.lex.substr(19)); person = user = doc.createElement("Reported"); %}, ex:reportedBy @<Person> %js{{ issue.appendChild(user); %}, ex:reportedOn xsd:dateTime %js{{ user.setAttribute("reportedOn", _.o.lex); person = reproducer = doc.createElement("Reproduced"); %}, (ex:reproducedBy @<EmployeeShape> %js{{ issue.appendChild(person);%}, ex:reproducedOn xsd:dateTime %js{{ reproducer.setAttribute("reportedOn", _.o.lex); %})?, ex:related @<IssueShape>* } %js{ window.open("data:application/xml;charset=utf-8;base64," + Base64.encode((new XMLSerializer()).serializeToString(doc)), "toy"); %} VIRTUAL <Person> { (foaf:name xsd:string | foaf:givenName xsd:string+ %js{ gn = doc.createElement("GivenName"); gn.appendChild(doc.createTextNode(_.o.lex)); person.appendChild(gn); %}, foaf:familyName xsd:string %js{ fn = doc.createElement("FamilyName"); fn.appendChild(doc.createTextNode(_.o.lex)); person.appendChild(fn); %} ), foaf:mbox IRI %js{ fn = doc.createElement("Email-address"); fn.appendChild(doc.createTextNode(_.o.lex)); person.appendChild(fn); %} } <UserShape> & <Person> { } <EmployeeShape> { & <Person>, foaf:phone IRI+ %js{ fn = doc.createElement("Telephone-Number"); fn.appendChild(doc.createTextNode(_.o.lex)); person.appendChild(fn); %} }
<Issue xmlns="http://ex.example/xml" id="Issue1" state="unassigned"> <Reported reportedOn="2013-01-23T10:18:00"> <GivenName>Bob</GivenName> <FamilyName>Smith</FamilyName> <Email-address>mailto:bob@example.org</Email-address> </Reported> <Reproduced reportedOn="2013-01-23T11:00:00"> <GivenName>Joe</GivenName> <GivenName>Joseph</GivenName> <FamilyName>Thompson</FamilyName> <Email-address>mailto:joe@example.org</Email-address> <Telephone-Number>tel:+456</Telephone-Number> </Reproduced> </Issue>
GenX details.
=<function>?
, $<namespace>
, !debugger
PREFIX ex: <http://ex.example/#> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> %GenX{ issue $http://ex.example/xml %} start = <IssueShape> <IssueShape> { ex:state (ex:unassigned ex:assigned) %GenX{ @state =substr(19) %}, ex:reportedBy @<Person> %GenX{ reported = %}, ex:reportedOn xsd:dateTime %GenX{ [-1]@date %}, (ex:reproducedBy @<EmployeeShape> , ex:reproducedOn xsd:dateTime %GenX{ @date %} )? %GenX{ reproduced = %}, ex:related @<IssueShape>* } %GenX{ @id %} VIRTUAL <Person> { (foaf:name xsd:string %GenX{ full-name %} | foaf:givenName xsd:string+ %GenX{ given-name %}, foaf:familyName xsd:string %GenX{ family-name %} ), foaf:mbox IRI %GenX{ email-addr %} } <UserShape> & <Person> { } <EmployeeShape> { & <Person>, foaf:phone IRI+ %GenX{ tel-number %} }
<Issue xmlns="http://ex.example/xml" id="Issue1" state="unassigned"> <Reported reportedOn="2013-01-23T10:18:00"> <GivenName>Bob</GivenName> <FamilyName>Smith</FamilyName> <Email-address>mailto:bob@example.org</Email-address> </Reported> <Reproduced reportedOn="2013-01-23T11:00:00"> <GivenName>Joe</GivenName> <GivenName>Joseph</GivenName> <FamilyName>Thompson</FamilyName> <Email-address>mailto:joe@example.org</Email-address> <Telephone-Number>tel:+456</Telephone-Number> </Reproduced> </Issue>
GenJ is GenX's JSON counterpart.
PREFIX ex: <http://ex.example/#> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> %GenJ{ @id %} start = <IssueShape> <IssueShape> { ex:state (ex:unassigned ex:assigned) %GenJ{ state %}, ex:reportedBy @<User> %GenJ{ rprtBy @id %}, ex:reportedOn xsd:dateTime %GenJ{ rprtOn %}, (ex:reproducedBy @<User> %GenJ{ rpdcBy @id %}, ex:reproducedOn xsd:dateTime %GenJ{ rpdcOn %} )?, ex:related @<IssueShape>* } <User> { (foaf:name xsd:string %GenJ{ name %} | foaf:givenName xsd:string+ %GenJ{ fname %}, foaf:familyName xsd:string %GenJ{ lname %} ), foaf:mbox IRI %GenJ{ email %}, foaf:phone IRI? %GenJ{ tel %} }
{"@context": {"ns0":"http://ex.example/#", "state":"ns0:state", "rprtBy":"ns0:reportedBy", "ns1":"http://xmlns.com/foaf/", "fname":"ns1:givenName", "lname":"ns1:familyName", "email":"ns1:mbox", "ns2":"http://www.w3.org/2001/XMLSchema#", "rprtOn":{ "@id":"ns0:reportedOn", "@type":"ns2:dateTime" }, "rpdcBy":"ns0:reproducedBy", "tel":"ns1:phone", "rpdcOn":{ "@id":"ns0:reproducedOn", "@type":"ns2:dateTime" } }, "@id":"Issue1", "state":"http://ex.example/#unassigned", "rprtBy":{ "@id":"User2", "fname":"Bob", "lname":"Smith", "email":"mailto:bob@example.org" }, "rprtOn":"2013-01-23T10:18:00", "rpdcBy":{ "@id":"Thompson.J", "fname":["Joe","Joseph"], "lname":"Thompson", "email":"mailto:joe@example.org", "tel":"tel:+456" }, "rpdcOn":"2013-01-23T11:00:00" }
GenR is GenJ's RDF counterpart.
Generate RDF from RDF?!?
PREFIX ex: <http://ex.example/#> PREFIX my: <http://my.example/#> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> %GenR{ %} start = <IssueShape> <IssueShape> { ex:state (ex:unassigned ex:assigned ) %GenR{ ISSUE = IRI(CONCAT(STR(s), "/myMark")) . STATE = SUBSTR(STR(o), 19) . ISSUE my:status STATE %}, ex:reportedOn xsd:dateTime %GenR{ ON = BNODE("on") . ISSUE my:on ON . DATE=CONCAT(YEAR(o),'-'MONTH(o),'-',DAY(o)) . TIME=CONCAT(HOURS(o),':'MINUTES(o),':',SECONDS(o),TZ(o)) . ON ex:date STRDT(DATE, xsd:date) . ON ex:time STRDT(TIME, xsd:time) %} }
PREFIX ex: <http://ex.example/#> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:state ex:unassigned ; ex:reportedOn "2013-01-23T10:18:12Z"^^xsd:dateTime .
<Issue1/myMark> my:status "unassigned" ; my:on [ my:date "2013-01-23"^^xsd:date ; my:time "10:18:12Z"^^xsd:time ] .
Normalize RDF.
Limited use:
%GenN{ %}