slanted W3C logo
Cover page images (keys)

ShEx by Example

Validation and Transformation

SWAT⁴LS
08 December, 2014

http://www.w3.org/2014/Talks/1209-shex-egp/ or http://w3.org/brief/NDI0

pirate pad for collaboration

Problem Statement

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> .

Expectations

What do users get elsewhere?

SQLXML
missing propertiesreportedBy UNSIGNED INT NOT NULLelement reportedBy { User },
missing/bad type arcsN/AN/A
missing referentsFOREIGN KEY (reportedBy)
REFERENCES Users(ID)
<keyref refer="UserID">
inconsistent stateCHECK(assignedOn>reportedOn)[schematron]
value set violationsENUM('unasigned', 'assigned')attribute state
{ "unassigned" | "assigned" }

Existing data

Technologies

in order of specificity:

Keep calm and validate.

SPARQL

ASK {
    { SELECT ?S (COUNT(*) AS ?S_c0) {
      ?S foaf:givenName ?o .
    } GROUP BY ?S}
    { SELECT ?S (COUNT(*) AS ?S_c1) {
      ?S foaf:givenName ?o .
    } GROUP BY ?S}
    FILTER (?S_c0 = ?S_c1 &&
            ?S_c0 = 1)
    { SELECT ?S (COUNT(*) AS ?S_c2) {
      ?S ex:state ?o .
    } GROUP BY ?S HAVING (COUNT(*)=1)}
    { SELECT ?S (COUNT(*) AS ?S_c3) {
      ?S ex:state ?o .
      FILTER ((?o = ex:unassigned ||
               ?o = ex:assigned))
    } GROUP BY ?S HAVING (COUNT(*)=1)}
    FILTER (?S_c2 = ?S_c3 &&
            (?S_c0 = 0 || ?S_c0 = 1))
}

Path-indexed ASK

per Simister, Brickley

{
  "@context": { … },
  "constraints": [{
    "context": "ex:status",
    "constraint": "ASK { ?s ex:assignee ?o }",
    "severity": "warning",
    "message": "a status of assigned requires an assignee"
  }]
}

SPARQL as SPIN

ASK {
    { SELECT ?this (COUNT(*) AS ?this_c0) {
      ?this foaf:givenName ?o .
    } GROUP BY ?this}
    { SELECT ?this (COUNT(*) AS ?this_c1) {
      ?this foaf:givenName ?o .
    } GROUP BY ?this}
    FILTER (?this_c0 = ?this_c1 &&
            ?this_c0 = 1)
    { SELECT ?this (COUNT(*) AS ?this_c2) {
      ?this ex:state ?o .
    } GROUP BY ?this HAVING (COUNT(*)=1)}
    { SELECT ?this (COUNT(*) AS ?this_c3) {
      ?this ex:state ?o .
      FILTER ((?o = ex:unassigned ||
               ?o = ex:assigned))
    } GROUP BY ?this HAVING (COUNT(*)=1)}
    FILTER (?this_c2 = ?this_c3 &&
            (?this_c0 = 0 || ?this_c0 = 1))
}

SPIN as templates

:Issue a owl:Class ;
  rdfs:subClassOf owl:Thing ;
  spin:constraint [
      a spl:ObjectCountPropertyConstraint ;
      arg:property ex:name ;
      arg:minCount 1 ;
      arg:maxCount 1 ;
    ] ;
  spin:constraint [
      a spl:ObjectCountPropertyConstraint ;
      arg:property ex:state ;
      arg:minCount 0 ;
      arg:maxCount 1 ;
    ] ;
  spin:constraint [
      a spl:UntypedObjectPropertyConstraint ;
      arg:property ex:state ;
    ] .

ex:name a owl:DatatypeProperty ;
  rdfs:domain my:name-status ;
  rdfs:range xsd:string .

:ValidState a owl:Class ;
  rdfs:label "Valid state" ;
  rdfs:subClassOf owl:Thing ;
.
:state a owl:ObjectProperty ;
  rdfs:domain my:name-status ;
  rdfs:range ex:ValidState .
:unassigned a ex:ValidState .
:assigned a ex:ValidState .

OWL

Combine OWL with a premise type associated with data.

Datatype: rdfs:Literal 
DataProperty: ex:name 
ObjectProperty: ex:status 
    
Class: ex:name-status 
    SubClassOf: 
        ex:name exactly 1 rdfs:Literal ,
        ex:status max 1 ({ ex:assigned , ex:unassigned }) ,
        ex:status min 0 owl:Thing 
    
Individual: ex:assigned 
Individual: ex:unassigned 

Not a real proposal; instead used with a different interpretation…

OWL/ICV

OWL with unique name assumption and closed world

Datatype: rdfs:Literal 
DataProperty: ex:name 
ObjectProperty: ex:status 
    
Class: ex:name-status 
    SubClassOf: 
        ex:name exactly 1 rdfs:Literal ,
        ex:status max 1 ({ ex:assigned , ex:unassigned }) ,
        ex:status min 0 owl:Thing 
    
Individual: ex:assigned 
Individual: ex:unassigned 

Resource Shapes/Description Set Profiles/TQ schema

my:name-status a rs:ResourceShape ;
    rs:property [
        rs:name "name" ;
        rs:propertyDefinition foaf:name ;
        rs:valueType xsd:string ;
        rs:occurs rs:Exactly-one ;
    ] ;
    rs:property [
        rs:name "state" ;
        rs:propertyDefinition ex:state ;
        rs:allowedValue ex:unassigned> , ex:assigned ;
        rs:occurs rs:Zero-or-one ;
    ] .

Shape Expressions

my:name-status {
  ex:name xsd:string ,
  ex:status ( ex:unassigned ex:assigned )?
}

Issues:

Terminology


    

ShEx deeper dive

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
}

User example

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
}

User example

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
}

User example

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...

RelaxNG Compact Syntax

    (element foaf:name { xsd:string }
     | (element foaf:givenName { xsd:string }+,
        element foaf:familyName { xsd:string })),
    element foaf:mbox { xsd:anyURI }

Regex

(N|(G+F))M
NM
GFM
GGGFM

W3C XML Schema

  <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>

User example

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> .

Exercise 1

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
}

Test at ericP's or Jose's interface

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 ignore.

Solution 1

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
}

Test at ericP's or Jose's interface

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> .

Universal constraints

my:name-status a rs:ResourceShape ;
    rs:property [
        rs:name "name" ;
        rs:propertyDefinition ex:name ;
        rs:valueType xsd:string ;
        rs:occurs rs:Exactly-one ;
    ] ;
    rs:property [
        rs:name "status" ;
        rs:propertyDefinition ex:status ;
        rs:allowedValue ex:unassigned , ex:assigned ;
        rs:occurs rs:Zero-or-one ;
    ] .

equivalent to

my:name-status {
  ex:name xsd:string ,
  ex:status ( ex:unassigned ex:assigned )?
}

universal, non-qualified constraints

SELECT ?S {
    { SELECT ?S (COUNT(*) AS ?S_c0) {
      ?S ex:name ?o .
    } GROUP BY ?S}
    { SELECT ?S (COUNT(*) AS ?S_c1) {
      ?S ex:name ?o .
      FILTER(datatype(?o) = xsd:string)
    } GROUP BY ?S}
    FILTER (?S_c0 = ?S_c1 &&
            ?S_c0 = 1)
    { SELECT ?S (COUNT(*) AS ?S_c2) {
      ?S ex:status ?o .
    } GROUP BY ?S}
    { SELECT ?S (COUNT(*) AS ?S_c3) {
      ?S ex:status ?o .
      FILTER ((?o = ex:unassigned ||
               ?o = ex:assigned))
    } GROUP BY ?S}
    FILTER (?S_c2 = ?S_c3 &&
            (?S_c0 = 0 || ?S_c0 = 1))
}
Datatype: xsd:string 
DataProperty: ex:name 
ObjectProperty: ex:status 
    
Class: ex:name-status 
    SubClassOf: 
        ex:name exactly 1 rdfs:Literal ,
        ex:status only ({ ex:assigned , ex:unassigned }) ,
        ex:status max 1 ,
        ex:status min 0 
    
Individual: ex:assigned 
Individual: ex:unassigned 

User example

What's that look like in SPARQL?

PREFIX foaf: <http://xmlns.com/foaf/>

<UserShape> {
    (foaf:name LITERAL
     | foaf:givenName LITERAL+,
       foaf:familyName LITERAL),
    foaf:mbox IRI
}
PREFIX foaf: <http://xmlns.com/foaf/>
ASK {
    { SELECT ?UserShape WHERE {
        {
            { SELECT ?UserShape {                             
              ?UserShape foaf:name ?o .                       
            } GROUP BY ?UserShape HAVING (COUNT(*)=1)}        
            { SELECT ?UserShape {                             
              ?UserShape foaf:name ?o . FILTER (isLiteral(?o))
            } GROUP BY ?UserShape HAVING (COUNT(*)=1)}        
        } UNION {
            { SELECT ?UserShape (COUNT(*) AS ?UserShape_c0) {      
              ?UserShape foaf:givenName ?o .                       
            } GROUP BY ?UserShape HAVING (COUNT(*)>=1)}            
            { SELECT ?UserShape (COUNT(*) AS ?UserShape_c1) {      
              ?UserShape foaf:givenName ?o . FILTER (isLiteral(?o))
            } GROUP BY ?UserShape HAVING (COUNT(*)>=1)}            
            FILTER (?UserShape_c0 = ?UserShape_c1)                 
            { SELECT ?UserShape {                                   
              ?UserShape foaf:familyName ?o .                       
            } GROUP BY ?UserShape HAVING (COUNT(*)=1)}              
            { SELECT ?UserShape {                                   
              ?UserShape foaf:familyName ?o . FILTER (isLiteral(?o))
            } GROUP BY ?UserShape HAVING (COUNT(*)=1)}              
        }
    } GROUP BY ?UserShape HAVING (COUNT(*) = 1)}
    { SELECT ?UserShape {                         
      ?UserShape foaf:mbox ?o .                   
    } GROUP BY ?UserShape HAVING (COUNT(*)=1)}    
    { SELECT ?UserShape {                         
      ?UserShape foaf:mbox ?o . FILTER (isIRI(?o))
    } GROUP BY ?UserShape HAVING (COUNT(*)=1)}    
}

ShExC is a grammar

Issue +:reportedOn: xsd:date +:assignedOn: xsd:date User +foaf:name: LITERAL +foaf:givenName: LITERAL +foaf:familyName: LITERAL +foaf:mbox: IRI :reportedBy 1 0..* :assignedTo 0..* 0..1

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 grammar

Issue +:reportedOn: xsd:date +:assignedOn: xsd:date User +foaf:name: LITERAL +foaf:givenName: LITERAL +foaf:familyName: LITERAL +foaf:mbox: IRI :reportedBy 1 0..* :assignedTo 0..* 0..1

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> .

Exercise 2

Different rules for Users from Employees.

Adjust previous example:

PREFIX issue: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>


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
}

Create a <EmployeeShape> distinct from an <UserShape>.

example instance:

PREFIX ex: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

<Issue1>
    ex:reportedBy   <User2> ;
    ex:reportedOn   "2013-01-23T10:18:00"^^xsd:dateTime ;
    ex:assignedTo <Thompson.J> ;
    ex:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime .

<User2>
    foaf:givenName "Bob" ;
    foaf:familyName "Smith" ;
    foaf:mbox <mailto:bob@example.org> .

<Thompson.J>
    foaf:givenName "Joe", "Joseph" ;
    foaf:familyName "Thompson" ;
    foaf:phone <tel:+456> ;
    foaf:mbox <mailto:joe@example.org> .

Solution 2

Different rules for Users from Employees.

A solution:

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: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:givenName LITERAL+,
    foaf:familyName LITERAL,
    foaf:phone IRI*,
    foaf:mbox IRI
}

Create a <EmployeeShape> distinct from an <UserShape>.

example instance:

PREFIX ex: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

<Issue1>
    ex:reportedBy   <User2> ;
    ex:reportedOn   "2013-01-23T10:18:00"^^xsd:dateTime ;
    ex:assignedTo <Thompson.J> ;
    ex:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime .

<User2>
    foaf:givenName "Bob" ;
    foaf:familyName "Smith" ;
    foaf:mbox <mailto:bob@example.org> .

<Thompson.J>
    foaf:givenName "Joe", "Joseph" ;
    foaf:familyName "Thompson" ;
    foaf:phone <tel:+456> ;
    foaf:mbox <mailto:joe@example.org> .

Value sets

A value set is a set of possible values.

ex:mood ("happy" "sad" "indigo")
ex:mood (mood:happy mood:sad mood:indigo)
ex:mood (mood:~)

Exercise 3

Extend previous example:

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: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:givenName LITERAL+,
    foaf:familyName LITERAL,
    foaf:phone IRI*,
    foaf:mbox IRI
}

example instance:

PREFIX issue: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX myco: <http://myco.example/#>

<Issue1>
    issue:status     issue:assigned ;
    issue:reportedBy <User2> ;
    issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ;
    issue:assignedTo <Thompson.J> ;
    issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime .

<User2>
    a              foaf:Person ;
    foaf:givenName "Bob" ;
    foaf:familyName "Smith" ;
    foaf:mbox <mailto:bob@example.org> .

<Thompson.J>
    a              foaf:Person ;
    foaf:page      myco:Employee7 ;
    foaf:givenName "Joe", "Joseph" ;
    foaf:familyName "Thompson" ;
    foaf:phone <tel:+456> ;
    foaf:mbox <mailto:joe@example.org> .

Solution 3

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:resolved),
    issue:reportedBy @<UserShape>,
    issue:reportedOn xsd:dateTime,
    ( issue:assignedTo @<EmployeeShape>,
      issue:assignedOn xsd:dateTime )?
}

<UserShape> {
    a (foaf:Person),
    (foaf:name LITERAL
     | foaf:givenName LITERAL+,
       foaf:familyName LITERAL),
    foaf:mbox IRI
}

<EmployeeShape> {
    a (foaf:Person),
    foaf:page (myco:Employee~),
    foaf:givenName LITERAL+,
    foaf:familyName LITERAL,
    foaf:phone IRI*,
    foaf:mbox IRI
}

example instance:

PREFIX issue: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX myco: <http://myco.example/#>

<Issue1>
    issue:status     issue:assigned ;
    issue:reportedBy <User2> ;
    issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ;
    issue:assignedTo <Thompson.J> ;
    issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime .

<User2>
    a              foaf:Person ;
    foaf:givenName "Bob" ;
    foaf:familyName "Smith" ;
    foaf:mbox <mailto:bob@example.org> .

<Thompson.J>
    a              foaf:Person ;
    foaf:page      myco:Employee7 ;
    foaf:givenName "Joe", "Joseph" ;
    foaf:familyName "Thompson" ;
    foaf:phone <tel:+456> ;
    foaf:mbox <mailto:joe@example.org> .

Complex business logic

PREFIX issue: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

start = <IssueShape>

<IssueShape> {
    issue:status (issue:unassigned
        issue:assigned issue:resolved),
    issue:reportedBy @<UserShape>,
    issue:reportedOn xsd:dateTime,
    ( issue:assignedTo @<EmployeeShape>,
      issue:assignedOn xsd:dateTime )?
}

What's wrong with this picture?

PREFIX issue: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

<Issue1>
    issue:status     issue:unassigned ;
    issue:reportedBy <User2> ;
    issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ;
    issue:assignedTo <Thompson.J> ;
    issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime .

<User2>
    a              foaf:Person ;
    foaf:givenName "Bob" ;
    foaf:familyName "Smith" ;
    foaf:mbox <mailto:bob@example.org> .

<Thompson.J>
    a              foaf:Person ;
    foaf:page      myco:Employee7 ;
    foaf:givenName "Joe", "Joseph" ;
    foaf:familyName "Thompson" ;
    foaf:phone <tel:+456> ;
    foaf:mbox <mailto:joe@example.org> .

How much can we capture?

Not a lot, but we can improve the free-for-all in the previous example

Exercise 4

PREFIX issue: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

start = <IssueShape>

<IssueShape> {
    issue:status (issue:unassigned
        issue:assigned issue:resolved),
    issue:reportedBy @<UserShape>,
    issue:reportedOn xsd:dateTime,
    ( issue:assignedTo @<UserShape>,
      issue:assignedOn xsd:dateTime )?
}

<UserShape> {
    foaf:givenName LITERAL+,
    foaf:familyName LITERAL,
    foaf:mbox IRI
}

example instance:

PREFIX issue: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

<Issue1>
    issue:status     issue:assigned ;
    issue:reportedBy <User2> ;
    issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ;
    issue:assignedTo <Thompson.J> ;
    issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime .

<User2>
    foaf:givenName "Bob" ;
    foaf:familyName "Smith" ;
    foaf:mbox <mailto:bob@example.org> .

<Thompson.J>
    foaf:givenName "Joe", "Joseph" ;
    foaf:familyName "Thompson" ;
    foaf:mbox <mailto:joe@example.org> .

You will need to use '!' to negate a rule

Solution 4

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:reportedOn xsd:dateTime,
    (  issue:status (issue:unassigned),
      !issue:assignedTo .,
      !issue:assignedOn .
     | issue:status
         (issue:assigned issue:resolved),
       issue:assignedTo @<UserShape>,
       issue:assignedOn xsd:dateTime
    )
}

<UserShape> {
    foaf:givenName LITERAL+,
    foaf:familyName LITERAL,
    foaf:mbox IRI
}

example instance:

PREFIX issue: <http://ex.example/>
PREFIX foaf: <http://xmlns.com/foaf/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

<Issue1>
    issue:status     issue:assigned ;
    issue:reportedBy <User2> ;
    issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ;
    issue:assignedTo <Thompson.J> ;
    issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime .

<User2>
    foaf:givenName "Bob" ;
    foaf:familyName "Smith" ;
    foaf:mbox <mailto:bob@example.org> .

<Thompson.J>
    foaf:givenName "Joe", "Joseph" ;
    foaf:familyName "Thompson" ;
    foaf:mbox <mailto:joe@example.org> .

IRI stems and exclusions

IRIs in predicates and value sets can have:

PREFIX annot: http://www.w3.org/annotea/ns#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>

start={
  annot:context LITERAL,
  dc:~ - dc:author - dc:creator .*
}
PREFIX annot: http://www.w3.org/annotea/ns#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>

<Annot1>
  annot:context "xpath...";
  dc:abstract "stuff" ;
  dc:audience "9606" ;
  dc:description """some
long
description""" .

Reverse arcs

Test arcs coming into an object.

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" .

Closed shapes

Use case: storing application data in a

"If I tell you X, will you understand it?"

Closed shapes

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> .

Semantic actions

For extensibility:

For actions:

API-native

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> .

SPARQL

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> .

Generators

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

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

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

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
  ] .

this example

example converting between clinical data schemas

GenN

Normalize RDF.

Limited use:

%GenN{ %}

example

Issues:

Standardization process

boys fighting

Resources