XSPARQL Specification

Abstract

With currently available tools and languages, translating between an existing XML format and RDF is a tedious and error-prone task. The importance of this problem is acknowledged by the W3C GRDDL working group who faces the issue of extracting RDF data out of existing HTML or XML files, as well as by the Web service community around SAWSDL, who need to perform lowering and lifting between RDF data from a semantic client and XML messages for a Web service. However, at the moment, both these groups rely solely on XSLT transformations between RDF/XML and the respective other XML format at hand. We propose a more natural approach for such transformations based on merging XQuery and SPARQL into the novel language XSPARQL.

Recently, two new languages have entered the stage for processing XML and RDF data: XQuery is a W3C Recommendation since early last year and SPARQL has finally received W3C's Recommendation stamp in January 2008. While both languages operate in their own worlds - SPARQL in the RDF- and XQuery in the XML-world - we show in this specification that the merge of both in the novel language XSPARQL has the potential to finally bring XML and RDF closer together. XSPARQL provides concise and intuitive solutions for mapping between XML and RDF in either direction, addressing both the use cases of GRDDL and SAWSDL. As a side effect, XSPARQL may also be used for RDF to RDF transformations beyond the capabilities of "pure" SPARQL. We also describe an implementation of XSPARQL, available for user evaluation.

Contact

Any inquiries should be directed to: Axel Polleres or Nuno Lopes from DERI Galway at the National University of Ireland, Galway, Ireland (axel.polleres@deri.org, nuno.lopes@deri.org).

Changelog

For a list of changes and issues please check the Changelog.

XSPARQL Language Specification

Authors:
Axel Polleres - DERI, NUI Galway
Thomas Krennwallner - Institute of Information Systems, Vienna University of Technology
Nuno Lopes - DERI, NUI Galway
Jacek Kopecký - University of Innsbruck
Stefan Decker - DERI, NUI Galway

This work is supported by Science Foundation Ireland under grants number SFI/02/CE1/I131 and SFI/08/CE/I1380 and under the European Commission European FP6 project inContext (IST-034718).


Abstract

XSPARQL is a query language combining XQuery and SPARQL for transformations between RDF and XML. XSPARQL subsumes XQuery and most of SPARQL (excluding ASK and DESCRIBE). This document defines the XSPARQL language.


Table of Contents


1. Introduction

There is a gap within the Web of data: on the one hand, XML provides a popular format for data exchange with a rapidly increasing amount of semi-structured data available online. On the other hand, the Semantic Web builds on data represented in RDF, which is optimized for data interlinking and merging; the amount of RDF data published on the Web is also increasing. Therefore, the reuse of XML data in the RDF world and vice versa is becoming increasingly important. However, with currently available tools and languages, translating between XML and RDF is not a simple task.

The importance of this issue is currently being acknowledged within the W3C in several efforts. The Gleaning Resource Descriptions from Dialects of Languages [GRDDL] (GRDDL) working group faces the issue of extracting RDF data out of existing (X)HTML Web pages. In the Semantic Web Services community, RDF-based client software needs to communicate with XML-based Web services, thus it needs to perform transformations between its RDF data and the XML messages that are exchanged with the Web services. The Semantic Annotations for WSDL (SAWSDL) working group calls these transformations lifting and lowering (see [SAWSDL]). Both these groups propose solutions which rely on XSL transformations (XSLT) [XSLT20] or - more recently - XQuery [XQUERY] for translating between RDF/XML [RDFXML] and the respective other XML format at hand. Using XSLT or XQuery for handling RDF data is greatly complicated by the flexibility of the RDF/XML format. XSLT and XPath [XPATH20] were optimized to handle XML data with a simple and known hierarchical structure, whereas RDF is conceptually different, abstracting away from fixed, tree-like structures. In fact, RDF/XML provides a lot of flexibility in how one and the same RDF graph can be serialized. Thus, processors that handle RDF/XML as XML data (not as a set of triples) need to take different possible representations into account when looking for pieces of data. This is best illustrated by a concrete example: Figure 1 shows four representations of the same RDF graph using the FOAF vocabulary (cf. http://www.foaf-project.org). The first version uses Turtle [TURTLE], a simple and readable textual syntax for RDF, inaccessible to pure XML processing tools though; the other three versions are all RDF/XML, ranging from concise (b) to verbose (d). Apart from the shown formats, yet another representation for RDF within HTML and XHTML documents, namely RDFa [RDFa] is completing the portfolio of possible representations for RDF.

Figure 1: Different representations of the same RDF graph
@prefix alice: <alice/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .

alice:me a foaf:Person.
alice:me foaf:knows _:c.
_:c a foaf:Person.
_:c foaf:name "Charles".
<rdf:RDF xmlns:foaf="http://xmlns.com/foaf/0.1/"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
   <foaf:Person rdf:about="alice/me">
      <foaf:knows>
         <foaf:Person foaf:name="Charles"/>
      </foaf:knows>
   </foaf:Person>
</rdf:RDF>
(a) (b)
<rdf:RDF xmlns:foaf="http://xmlns.com/foaf/0.1/"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
   <rdf:Description rdf:nodeID="x">
      <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
      <foaf:name>Charles</foaf:name>
   </rdf:Description>
   <rdf:Description rdf:about="alice/me">
      <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
      <foaf:knows rdf:nodeID="x"/>
   </rdf:Description>
</rdf:RDF>
<rdf:RDF xmlns:foaf="http://xmlns.com/foaf/0.1/"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
   <rdf:Description rdf:about="alice/me">
      <foaf:knows rdf:nodeID="x"/>
   </rdf:Description>
   <rdf:Description rdf:about="alice/me">
      <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
   </rdf:Description>
   <rdf:Description rdf:nodeID="x">
      <foaf:name>Charles</foaf:name>
   </rdf:Description>
   <rdf:Description rdf:nodeID="x">
      <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
   </rdf:Description>
</rdf:RDF>
(c) (d)

The three RDF/XML variants in Figure 1 look very different to XML tools, yet exactly the same to RDF tools. For any variant we could create simple XPath expressions that extract for instance the names of the persons known to Alice, but a single expression that would correctly work in all the possible variants would become more involved. The following particular features of the RDF data model and RDF/XML syntax complicate XPath+XSLT/XQuery processing:

These issues complicate the formulation of adequate XPath expressions that cater for every possible alternative in how one and the same RDF data might be structured in its concrete RDF/XML representation.

Apart from that, simple reasoning (e.g., RDFS materialization) improves data queries when accessing RDF data. For instance, in FOAF, every Person (and Group and Organization etc.) is also an Agent, therefore we should be able to select all the instances of foaf:Agent. If we wanted to write such a query in XPath+XSLT, we literally would need to implement an RDFS inference engine within XSLT. Given the availability of RDF tools and engines, this should be avoided, and instead adequate languages for formulating transformations of RDF independent of its syntactical representation is needed.

2. Lifting and Lowering

Figure 2 shows a mapping between FOAF data and a customized XML format. The task here in either direction is to extract for all persons the names of people they know. For illustration, we use element and attribute names corresponding to the respective classes and properties in the FOAF vocabulary (i.e., Person, knows, and name) in this example. Names in our XML file uniquely identify a person which actually complicates the transformation from XML to RDF, since we need to create a unique, distinct blank node per name. The example data is a slight variant of the data from Figure 1, where Alice knows both Bob and Charles, Bob knows Charles, and all parties are identified by blank nodes.

Because semantic data in RDF is on a higher level of abstraction than semi-structured XML data, the translation from XML to RDF is often called "lifting" while the opposite direction is called "lowering" (for instance in the SAWSDL specification [SAWSDL]) as shown in Figure 2.

Existing specifications such as GRDDL [GRDDL] or SAWSDL [SAWSDL] propose XSLT, XQuery, or combinations of either of these two languages with SPARQL for performing the lifting and lowering tasks. For instance Figure 3 shows the lifting task in XQuery and Figure 4 shows the lowering task in XSLT.

Both these examples generate and consume only one specific RDF/XML representation shown in Figure 1. If the consumed RDF data is in a different format, a different transformation would be needed. Likewise, if the RDF data is to be queried from or stored into an RDF store, an intermediate transformation into RDF/XML is undesirable. In contrast to XSLT and XQuery, SPARQL is agnostic of the concrete RDF representation. Moreover, SPARQL is the standard format to access RDF stores. However, SPARQL cannot generate or consume XML. The present specification presents an integrated approach to combine SPARQL with one of the XML transformation languages - XQuery - in order to close this gap.


Figure 3: Lifting (XML-to-RDF mapping) using XQuery
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
declare namespace foaf="http://xmlns.com/foaf/0.1/";
declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
let $persons := //*[@name or ../knows]
return
<rdf:RDF>
 {
 for $p in $persons
 let $n := if( $p[@name] )
           then $p/@name else $p
 let $id := count($p/preceding::*)
           +count($p/ancestor::*)
 where
  not(exists($p/following::*[
      @name=$n or data(.)=$n]))
 return
 <foaf:Person rdf:nodeId="b{$id}">
  <foaf:name>{data($n)}</foaf:name>
  {
  for $k in $persons
  let $kn := if( $k[@name] )
             then $k/@name else $k
  let $kid := count($k/preceding::*)
             +count($k/ancestor::*)
  where
   $kn = data(//*[@name=$n]/knows) and
   not(exists($kn/../following::*[
       @name=$kn or data(.)=$kn]))
  return
  <foaf:knows>
   <foaf:Person rdf:nodeID="b{$kid}"/>
  </foaf:knows>
  }
 </foaf:Person>
 }
</rdf:RDF>

Figure 4: Lowering (RDF-to-XML mapping) by XSLT
<xsl:stylesheet version="1.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
                xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="/rdf:RDF">
      <relations>
         <xsl:apply-templates select=".//foaf:Person"/>
      </relations>
   </xsl:template>
   <xsl:template match="foaf:Person">
      <person name="{./@foaf:name}">
         <xsl:apply-templates select="./foaf:knows"/>
      </person>
   </xsl:template>
   <xsl:template match="foaf:knows[@rdf:nodeID]">
      <knows>
         <xsl:value-of select="//foaf:Person[@rdf:nodeID=./@rdf:nodeID]/@foaf:name"/>
      </knows>
   </xsl:template>
   <xsl:template match="foaf:knows[foaf:Person]">
      <knows>
         <xsl:value-of select="./foaf:Person/@foaf:name"/>
      </knows>
   </xsl:template>
</xsl:stylesheet>

Figure 5: RDF-to-RDF mapping in SPARQL
prefix vc: <http://www.w3.org/2001/vcard-rdf/3.0#>
prefix foaf: <http://xmlns.com/foaf/0.1/>
construct {$X foaf:name $FN.}
from <vc.rdf>
where { $X vc:FN $FN .}

3. Starting Points: XQuery and SPARQL

In order to arrive at a more suitable language for specifying translations between XML and RDF addressing both the lifting and lowering use cases outlined above, XSPARQL builds up on two main starting points: XQuery [XQUERY] and SPARQL [SPARQL]. Whereas the former allows a more convenient and often more concise syntax than XSLT for XML query processing and XML transformation in general, the latter is the standard for RDF querying and construction - independent of the syntactic representation of an RDF graph.

As shown in a schematic abstraction of both query languages in Figure 6, queries in each of the two languages can roughly be divided in two parts: (i) the retrieval part (body) and (ii) the result construction part (head). XSPARQL combines these components for both languages in a unified language, where XQuery's and SPARQL's heads and bodies may be used interchangeably. Before we go into the details of this merge, we briefly describe the two constituent languages to the extent necessary for the current specification.


Figure 6: An overview of XQuery, SPARQL, and XSPARQL
Prolog: P declare namespace
   prefix="namespace-URI"
Body: F
L
W
O
for var in XPath-expression
let var := XPath-expression
where XPath-expression
order by XPath-expression
Head: R return XML+ nested XQuery
(a) Schematic view on XQuery
 
Prolog: P prefix prefix: <namespace-URI>
Head: C construct { template }
Body: D
W
M
from / from named <dataset-URI>
where { pattern }
order by expression
limit integer > 0 offset integer > 0
(b) Schematic view on SPARQL
 
 
Prolog: P declare namespace prefix="namespace-URI"
or prefix prefix: <namespace-URI>
Body: F
L
W
O
for var in XPath-expression
let var := XPath-expression
where XPath-expression
order by expression
or  F'
D
W
M
for varlist
from / from named <dataset-URI>
where { pattern }
order by expression
limit integer > 0 offset integer > 0
Head: C construct
   { template (with nested XSPARQL) }
or  R return XML+ nested XSPARQL
(c) Schematic view on XSPARQL

3.1. XQuery

As shown in Figure 6(a) an XQuery starts with a (possibly empty) prolog (P) for namespace, library, function, and variable declarations, followed by so called FLWOR - or "flower" - expressions, denoting body (FLWO) and head (R) of the query.

As for the body, for clauses (F) can be used to declare variables looping over the XML nodeset returned by an XPath expression. Alternatively, to bind the entire result of an XPath query to a variable, let assignments can be used. The where part (W) defines an XPath condition over the current variable bindings. Processing order of results of a for can be specified via a condition in the order by clause (O).

In the head (R) arbitrary well-formed XML is allowed following the return keyword, where variables scoped in an enclosing for or let as well as nested XQuery FLWOR expressions are allowed.

Any XPath expression in FLWOR expressions can again possibly involve variables defined in an enclosing for or let, or even nested XQuery FLWOR expressions. Together with a large catalogue of built-in functions [XPATHFUNCT], XQuery [XQUERY,XQUERYSEMANTICS] thus offers a flexible instrument for arbitrary transformations.

The lifting task of Figure 2 can be solved with XQuery as shown in Figure 3. The resulting query is quite involved, but completely addresses the lifting task, including unique blank node generation for each person: We first select all nodes containing person names from the original file for which a blank node needs to be created in variable $p (line 3). Looping over these nodes, we extract the actual names from either the value of the name attribute or from the knows element in variable $n. Finally, we compute the position in the original XML tree as blank node identifier in variable $id. The where clause (lines 12-14) filters out only the last name for duplicate occurrences of the same name. The nested for (lines 19-31) to create nested foaf:knows elements again loops over persons, with the only differences that only those nodes are filtered out (line 25), which are known by the person with the name from the outer for loop.

While this is a valid solution for lifting, we still observe the following drawbacks: (1) We still have to build RDF/XML "manually" and cannot make use of the more readable and concise Turtle syntax; and (2) if we had to apply XQuery for the lowering task, we still would need to cater for all kinds of different RDF/XML representations. As we will see, both these drawbacks are alleviated by adding some SPARQL to XQuery.

3.2. SPARQL

Figure 6(b) shows a schematic overview of the building blocks of SPARQL queries, see [SPARQL] for formal details. Like in XQuery, namespace prefixes can be specified in the Prolog (P). In analogy to FLWOR expressions in XQuery, we define so-called DWMC expressions for SPARQL.

The body (DWM) offers the following features. A dataset (D), i.e., the set of source RDF graphs, can be specified in from or from named clauses (which are optional since the dataset may be implicitly given). The where part (W) - unlike in XQuery - allows to match parts of the dataset by specifying a graph pattern possibly involving variables. This pattern is given in a Turtle-based syntax, in the simplest case by a set of triple patterns, i.e., triples with variables. More involved patterns allow unions of graph patterns, optional matching of parts of a graph, matching of named graphs, etc. Matching patterns on the conceptual level of RDF graphs rather than on a concrete XML syntax alleviates the difficulties of having to deal with different RDF/XML representations; SPARQL is agnostic to the actual XML representation of the underlying source graphs. Also the RDF merge of several source graphs specified in consecutive from clauses, which would involve renaming of blank node identifiers at the pure XML level, comes for free in SPARQL. Finally, variable bindings matching the where pattern in the source graphs can be ordered like in XQuery, but also other solution modifiers (M) such as limit and offset are allowed to restrict the number of solutions considered in the result.

In the head, SPARQL's construct clause (C) offers convenient and XML-independent means to create an output RDF graph. Since XSPARQL focuses on RDF construction, the ask and select SPARQL query forms are ommitted in Figure 6(b) for brevity. A construct template consists of a list of triple patterns in Turtle syntax possibly involving variables that carry over bindings from the where part. SPARQL can be used as transformation language between different RDF formats, just like XSLT and XQuery can be used for transforming between XML formats. A simple example for mapping full names from vCard/RDF (http://www.w3.org/TR/vcard-rdf) to foaf:name is given by the SPARQL query in Figure 5.

Note that SPARQL does not offer the generation of new values in the head which on the contrary comes for free in XQuery by offering the full range of XPath/XQuery built-in functions. For instance, the simple query in Figure 7 which attempts to merge family names and given names into a single foaf:name is beyond SPARQL's capabilities.

4. XSPARQL

XSPARQL does not only make reuse of SPARQL for transformations from and to RDF, but also aims at enhancing SPARQL itself for RDF-to-RDF transformations enabling queries like the one in Figure 7. Conceptually, XSPARQL is a simple merge of SPARQL components into XQuery. In order to benefit from the more intuitive features of SPARQL in terms of RDF graph matching for retrieval of RDF data and the use of Turtle-like syntax for result construction, XSPARQL syntactically just adds these facilities to XQuery. Figure 6(c) shows the result of this combination. First of all, every native XQuery query is also an XSPARQL query. However we also allow the following modifications, extending XQuery's FLWOR expressions to so-called FLWOR' expressions: (i) In the body we allow SPARQL-style F'DWM blocks alternatively to XQuery's FLWO blocks. The new F' for clause is very similar to XQuery's native for clause, but instead of assigning a single variable to the results of an XPath expression it allows the assignment of a whitespace separated list of variables (varlist) to the bindings for these variables obtained by evaluating the graph pattern of a SPARQL query of the form: select varlist DWM. (ii) In the head we allow to create RDF/Turtle directly using construct statements (C) alternatively to XQuery's native return (R).

These modifications enable a reformulation of the lifting query of Figure 3 into the more concise XSPARQL version of Figure 8. Figure 9 shows a lowering query which is unaffected by the RDF representation in XSPARQL, corresponding to the XSL transformation of Figure 4.


Figure 7: RDF-to-RDF enhanced mapping in XSPARQL
prefix vc: <http://www.w3.org/2001/vcard-rdf/3.0#>
prefix foaf: <http://xmlns.com/foaf/0.1/>
construct {_:b foaf:name
             { fn:concat($N," ",$F) } . }
from <vc.rdf>
where { $P vc:Given $N. $P vc:Family $F.}

Figure 8: Lifting using XSPARQL
declare namespace foaf="http://xmlns.com/foaf/0.1/";
declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
let $persons := //*[@name or ../knows]
return


 for $p in $persons
 let $n := if( $p[@name] )
           then $p/@name else $p
 let $id := count($p/preceding::*)
           +count($p/ancestor::*)
 where
  not(exists($p/following::*[
      @name=$n or data(.)=$n]))
 construct {
 _:b{$id} a foaf:Person;
                 foaf:name {data($n)}.
  {
  for $k in $persons
  let $kn := if( $k[@name] )
             then $k/@name else $k
  let $kid := count($k/preceding::*)
             +count($k/ancestor::*)
  where
   $kn = data(//*[@name=$n]/knows) and
   not(exists($kn/../following::*[
       @name=$kn or data(.)=$kn]))
  construct {
   _:b{$id} foaf:knows _:b{$kid}.
   _:b{$kid} a foaf:Person.
   }
  }
 }

Figure 9: Lowering using XSPARQL
<relations>{
   for $Person $Name from <relations.rdf>
   where {$Person foaf:name $Name}
   order by $Name
   return
      <person name="{$Name}">{
         for $FName from <relations.rdf>
         where {
            $Person foaf:knows $Friend.
            $Person foaf:name $Name.
            $Friend foaf:name $Fname. }
         return
            <knows>{$FName}</knows>
      }</person>
}</relations>

As a shortcut notation, XSPARQL also allows to write "for *" in place of "for [list of all variables appearing in the where clause]"; this is also the default value for the F' clause whenever a SPARQL-style where clause is found and a for clause is missing. By this treatment, XSPARQL is also a syntactic superset of native SPARQL construct queries, since we additionally allow the following: (1) XQuery and SPARQL namespace declarations (P) may be used interchangeably; and (2) SPARQL-style construct result forms (R) may appear before the retrieval part; note that we allow this syntactic sugar only for queries consisting of a single FLWOR' expression, with a single construct appearing right after the query prolog, as otherwise, syntactic ambiguities may arise. This feature is mainly added in order to encompass SPARQL style queries, but in principle, we expect the (R) part to appear in the end of a FLWOR' expression. This way, the queries of Figure 7 are also syntactically valid for XSPARQL.

Note however, that as opposed to native SPARQL, XSPARQL makes three syntactic restrictions due to consistency with XQuery: Firstly, all keywords (construct, where, from, union, optional, filter, ...) have to be written in lower-case, whereas the native SPARQL grammar is case-insensitive with regards to its keywords. Secondly, variables in XSPARQL are preceded by '$', whereas SPARQL allows alternatively the use of '?'-preceded variables. Thirdly, variable names in XSPARQL are not allowed to contain underscores '_'. These restrictions are explained in more detail below.

4.1. Syntax

The XSPARQL syntax is a small extension of the grammar production rules in [XQUERY]. To simplify the definition of XSPARQL, we inherit SPARQL's and XQuery's grammar productions and add the prime symbol (') to those rules which have been modified. We only have two new productions: ReturnClause and SparqlForClause (which loosely reflect lifting and lowering).

The basic elements of the XSPARQL syntax are the following:

[6]   Prolog'   ::=  
(((DefaultNamespaceDecl
| Setter
| NamespaceDecl
| Import) Separator)*
| Prologue)
((VarDecl |
FunctionDecl
| OptionDecl) Separator)*
[33]   FLWORExpr'   ::=   (ForClause | LetClause | SparqlForClause)+ WhereClause? OrderByClause? ReturnClause
[33a]   ReturnClause   ::=   "return" ExprSingle | "construct" ConstructTemplate'
[33b]   SparqlForClause   ::=   "for" ("$" VarName ("$" VarName)* | "*") DatasetClause "where" GroupGraphPattern SolutionModifier

ConstructTemplate' is defined in the same way as the production ConstructTemplate in SPARQL [SPARQL], but we additionally allow XSPARQL nested FLWORExpr' in subject, verb, and object place. These expressions need to evaluate to a valid RDF term, i.e.:

To define this we use the SPARQL grammar rules as a starting point and replace the following productions:

[42]   VarOrTerm'   ::=   Var' | GraphTerm' | literalConstruct1
[43]   VarOrIRIref'   ::=   Var' | IRIref | iriConstruct1
[44]   Var'   ::=   VAR2
[45]   GraphTerm'   ::=   IRIref | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode
| NIL | bnodeConstruct1 | iriConstruct1
[42a]   literalConstruct   ::=   "{" FLWORExpr' "}" | ("{" FLWORExpr' "} @ {" FLWORExpr' "}" | "{" FLWORExpr' "}^^" iriConstruct
[43a]   iriConstruct   ::=   "<{" FLWORExpr' "}>" | ("{" FLWORExpr' "}")? ":" ("{" FLWORExpr' "}")?
[45a]   bnodeConstruct   ::=   "_:{" FLWORExpr' "}"

The SourceSelector SPARQL [SPARQL] production is also extended, allowing the source to be specified by a previously bound XSPARQL variable. A possible use case for this extension is shown in Section 2.2 of [XSPARQLUSECASES].

[12']   SourceSelector'   ::=   IRIref | Var'

Without loss of generality, we make two slight restrictions on variable names in XSPARQL compared with XQuery and SPARQL. Firstly, we disallow variable names with '_' in it; we need this restriction in XSPARQL to distinguish new auxiliary variables which we introduce in the semantics definition and in our rewriting algorithm and avoid ambiguity with user-defined variables. Secondly, in SPARQL-inherited parts we only allow variable names prefixed with '$' in order to be compliant with variable names as allowed in XQuery. Pure SPARQL allows variables of this form, but additionally allows '?' as a valid variable prefix.

Likewise, we also disallow other identifier names to start with '_', namely namespace prefixes, function identifiers, and also blank node identifiers, for similar considerations: In our normalization we assume fixed namespaces (e.g. we always need the namespace prefix _sparql_result: associated with the namespace-URI http://www.w3.org/2005/sparql-results# which when overridden by the user might create ambiguities. Similarly, we use underscores in our rewriting to disambiguate blank node identifiers created from constructs from those extracted from a query result, by appending '_' to the latter. As for function names, we use underscores to denote auxiliary functions defined in our rewriting algorithm, which again we do not want to be overridden by user-defined functions. For further details we refer to [XSPARQLIMPLEMENTATION].

In total, we restrict the SPARQL grammar by redefining VARNAME to disallow leading underscores:

[97]   VARNAME'   ::=   ( PN_CHARS_U - '_' | [0-9] ) ( PN_CHARS_U | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] )*

And likewise in the XQuery grammar we do not allow underscores in the beginning of NCNames (defined in Namespaces in XML 1.0 (Second Edition)), i.e. we modify:

[6]   NCNameStartChar'   ::=   Letter

Finally, we understand the SPARQL grammar [SPARQL] and all references to it with all keywords in lower-case, and being case-sensitive, that is, XSPARQL does not allow the capitalized versions of SPARQL keywords (construct, where, from, union, optional, filter, ...). This restriction is made to be in line with XQuery, where all keywords are lower-case.

Note that XSPARQL allows nesting of FLWOR' expressions within SPARQL heads (i.e., in SPARQL construct clauses), cf. grammar productions [42a,43a,45a] above, but not in SPARQL bodies (i.e., in SPARQL where clauses). However, certain interaction with SPARQL where clauses is allowed. That is, previously bound variables used within SPARQL where clauses keep their binding from outside the where clause. This means a subtle difference between the treatment of unbracketed variables in XSPARQL construct clauses and where clauses, though, as illustrated by the following examples. Figure 10 shows that for instance for injecting an IRI value into the where clause, the user needs to ensure that the variable value is properly delimited by angle brackets as required by Turtle syntax, whereas for injecting an IRI value into the construct clause (Figure 11), the special syntax for iriConstruct has to be used. The latter will ensure that the constructed output is checked for validity according to the Turtle syntax.


Figure 10: IRI in the SPARQL where clause
prefix : <http://www.example.org>
prefix foaf: <http://xmlns.com/foaf/0.1/>
let $y := "<http://www.example.org/Alice>"
for $x from <foaf.rdf>
where {$x foaf:knows $y}
construct {$x a <http://www.example.org/AliceContact> }

Figure 11: IRI in the SPARQL construct clause
prefix : <http://www.example.org>
prefix foaf: <http://xmlns.com/foaf/0.1/>
let $y := "http://www.example.org/knownPerson"
for $x from <foaf.rdf>
where {$s foaf:knows $x}
construct {$x a <{$y}> }

Note further that any bound variables used within SPARQL style where or construct clauses is supposed to hold a value of type xs:anyAtomicType (cf. Section 1.6 of [XPATHFUNCT]). If this is not the case, it is not a syntax error, but implementations are expected to return a type error err:XPTY0004 (cf. Section Appendix G of [XPATH20]). For further details we refer to [XSPARQLSEMANTICS].2 That means that for instance the query in Figure 12 will only return a result if there is only at maximum one element <a> in the file example.xml.


Figure 12: Variables bound to sequences in SPARQL construct or where clauses may cause errors.
prefix : <http://www.example.org>
let $o := doc("example.xml")//a
construct {:s :p $o  }

4.2. Comments in XSPARQL

Comments in XSPARQL queries take the form of '#', outside an IRI, or string expressions and continue to the end of line (marked by characters 0x0D or 0x0A) or end of file if there is no end of line after the comment marker. Comments are treated as white space. Note that XSPARQL in its current version does not support XQuery extension expressions (cf. Section 3.14 of [XQUERY]), since the '(#' and '#)' delimiters for these expressions conflict with SPARQL style comments. Likewise, XQuery style comments (cf. Section 2.6 of [XQUERY]) are not supported, due to possible ambiguities of the '(:' and ':)' delimiters with valid SPARQL filter expressions. Summarizing, we replace the XQuery grammar rule [151] by an empty production and allow SPARQL style comments outside IRI, or string expressions and secondly we replace production [59] of the XQuery grammar by

[59]   ValueExpr'   ::=   ValidateExpr |
PathExpr

1 literalConstruct, iriConstruct, and bnodeConstruct are only allowed in GroupGraphPatterns within a ConstructTemplate'.

2Typing errors by variables which are bound to sequences and used within SPARQL style where or construct clauses are implicit in XSPARQL's semantics, since they are normalized using the fn:concat (cf. [XPATHFUNCT]) function which only accepts arguments of type xs:anyAtomicType.

5. References

[GRDDL]
Dan Connolly (ed.). Gleaning Resource Descriptions from Dialects of Languages (GRDDL). W3C Recommendation, 11 September 2007. Available at http://www.w3.org/TR/grddl/.
[RDFa]
Ben Adida, Mark Birbeck, Shane McCarron, and Steven Pemberton (eds.). RDFa in XHTML: Syntax and Processing, W3C Recommendation, 14 October 2008, Available at http://www.w3.org/TR/rdfa-syntax/.
[RDFXML]
Dave Beckett and Brian McBride (eds.) RDF/XML Syntax Specification (Revised). W3C Recommendation, W3C, 10 February 2004. Available at http://www.w3.org/TR/rdf-syntax-grammar/.
[SAWSDL]
Joel Farrell and Holger Lausen (eds.). Semantic Annotations for WSDL and XML Schema. W3C Recommendation, W3C, 28 August 2007. Available at http://www.w3.org/TR/sawsdl/.
[SPARQL]
Eric Prud'hommeaux and Andy Seaborne (eds.). SPARQL Query Language for RDF, 15 January 2008. W3C Recommendation, available at http://www.w3.org/TR/rdf-sparql-query/.
[TURTLE]
David Beckett and Tim Berners-Lee. Turtle - Terse RDF Triple Language, W3C Team Submission, 14 January 2008, Available at http://www.w3.org/TeamSubmission/turtle/.
[XPATH20]
Anders Berglund, Scott Boag, Don Chamberlin, Mary F. Fernández, Michael Kay, Jonathan Robie, and Jérôme Siméon (eds.). XML Path Language (XPath) 2.0. W3C Recommendation, 23 January 2007. Available at http://www.w3.org/TR/xpath20/.
[XPATHFUNCT]
Ashok Malhotra, Jim Melton, and Norman Walsh (eds.). XQuery 1.0 and XPath 2.0 Functions and Operators. W3C Recommendation, 23 January 2007. Available at http://www.w3.org/TR/xpath-functions/.
[XQUERY]
Don Chamberlin, Jonathan Robie, Scott Boag, Mary F. Fernández, Jérôme Siméon, and Daniela Florescu (eds.). XQuery 1.0: An XML Query Language. W3C Recommendation, 23 January 2007. Available at http://www.w3.org/TR/xquery/.
[XQUERYSEMANTICS]
Denise Draper, Peter Fankhauser, Mary Fernández, Ashok Malhotra, Kristoffer Rose, Michael Rys, Jérôme Siméon, and Philip Wadler (eds.). XQuery 1.0 and XPath 2.0 Formal Semantics. W3c Recommendation, W3C, 23 January 2007. Available at http://www.w3.org/TR/xquery-semantics/.
[XSLT20]
Michael Kay (ed.). XSL Transformations (XSLT) Version 2.0. W3C Recommendation, 23 January 2007. Available at http://www.w3.org/TR/xslt20.
[XSPARQLIMPLEMENTATION]
XSPARQL: Implementation and Test-cases. Document included in the present specification.
[XSPARQLSEMANTICS]
XSPARQL: Semantics. Document included in the present specification.
[XSPARQLUSECASES]
XSPARQL: Use cases. Document included in the present specification.

XSPARQL Semantics

Authors:
Thomas Krennwallner - Institute of Information Systems, Vienna University of Technology
Nuno Lopes - DERI, NUI Galway
Axel Polleres - DERI, NUI Galway

This work is supported by Science Foundation Ireland under grants number SFI/02/CE1/I131 and SFI/08/CE/I1380 and under the European Commission European FP6 project inContext (IST-034718).


Abstract

XSPARQL is a query language combining XQuery and SPARQL for transformations between RDF and XML. This document defines the semantics of XSPARQL.


Table of Contents


1. Introduction

This document defines the semantics of XSPARQL based on the XQuery semantics [XQUERYSEMANTICS]. Each pragma-free XQuery is an XSPARQL query and has the same result under both XQuery and XSPARQL semantics.

2. XSPARQL Semantics

The semantics of XSPARQL follows the formal treatment of the XQuery semantics [XQUERYSEMANTICS]. We adopt the notation provided there and define XSPARQL semantics by means of respective normalization mapping rules and inference rules.

We define dynamic evaluation inference rules for a new built-in function fs:sparql which evaluates SPARQL queries according to the SPARQL semantics, cf. [SPARQL]. Other modifications include normalization of the XSPARQL constructs to XQuery expressions. This means that we do do not need new grammar productions but only those defined in the XQuery Core syntax.

2.1. FLWOR' Expressions

The XSPARQL syntax [XSPARQLLANGUAGE] defines, together with the XQuery FLWOR expression, a new for-loop for iterating over SPARQL results: SparqlForClause. This object stands at the same level as XQuery's for and let expressions, i.e., such type of clauses are allowed to start new FLWOR' expressions, or may occur inside nested XSPARQL queries.

To this end, our new normalization mapping rules [·]Expr' inherit from the definitions of XQuery's [·]Expr mapping rules and overload some expressions to accommodate XSPARQL's new syntactic objects. The semantics of XSPARQL expressions hence stands on top of XQuery's semantics.

A single SparqlForClause is normalized as follows:

[ for $VarName1 ... $VarNamen DatasetClause where
GroupGraphPattern SolutionModifier ReturnClause
]Expr'
==
[
let $_aux_queryresult :=
  [ VarName1 ... VarNamen DatasetClause
where GroupGraphPattern SolutionModifier
]SparqlQuery
for $_aux_result in $_aux_queryresult//_sparql_result:result
  [VarName1]SparqlResult
            ⋮
  [VarNamen]SparqlResult
ReturnClause
]Expr

Here, [·]SparqlQuery and [·]SparqlResult are auxiliary mapping rules for expanding the given expressions:

[$VarName]SparqlResult
==
let $_VarName_Node := $_aux_result/_sparql_result:binding[@name = "VarName"]
let $VarName := data($_VarName_Node/*)
let $_VarName_NodeType := name($_VarName_Node/*)
let $_VarName_NodeDatatype := string($_VarName_Node/*/@datatype)
let $_VarName_NodeLang := string($_VarName_Node/*/@lang)
let $_VarName_RDFTerm := _rdf_term($_VarName_Node)

here, for each SPARQL variable:

  and

  [ $VarName1 ... $VarNamen DatasetClause
where GroupGraphPattern SolutionModifier
]SparqlQuery
==
fs:sparql([ fs:serialize("SELECT $VarName1 ... $VarNamen", DatasetClause, " where { ",
    fs:serialize( GroupGraphPattern ), " } SolutionModifier")
]Expr')

The function _rdf_term($_VarName_Node) is defined as follows:

statEnv |- $_VarName_Node bound

[_rdf_term($_VarName_Node)]Expr
=
statEnv |- [
if ( $_VarName_NodeType = "literal") then
fn:concat("""", $VarName ,"""",
if ($_VarName_NodeLang) then fn:concat("@", $_VarName_NodeLang) else "",
if ($_VarName_NodeDatatype) then fn:concat("^^<", $_VarName_NodeDatatype,">") else "",
else if ($_VarName_NodeType = "bnode") then fn:concat("_:", $VarName)
else if ($_VarName_NodeType = "uri") then fn:concat("<", $VarName , ">")
else ""
]Expr

We now define the meaning of fs:sparql. It is, following the style of [XQUERYSEMANTICS], an abstract function which returns a SPARQL query result XML document [SPARQLPROTOCOL] expected as the result of a SPARQL select query. I.e., the result of fs:sparql conforms to the XML Schema definition http://www.w3.org/2007/SPARQL/result.xsd. We further assume that the _sparql_result: namespace is declared implicitly in each XSPARQL query and tied to the namespace URI http://www.w3.org/2007/SPARQL/result#.

fs:sparql($query as xs:string) as document-node(schema-element(_sparql_result:sparql))

Firstly, fs:sparql implicitly expands PrefixedNames within the SPARQL query string it is given, according to the namespace declarations in the prolog of XSPARQL queries.

Static typing rules apply here according to the rules given in the XQuery semantics.

The fs:serialize function behaves like the fn:concat function on arguments of xs:anyAtomicType, cf. [XPATHFUNCT], and serializes arguments bound to XML nodes to xs:string by converting them to a corresponding string in the lexical space of rdf:XMLLiteral as defined in [RDFCONCEPTS]. This ensures that the structure of the XML literals is preserved in the output, while the conversion to xs:string of fn:concat only retains the text data of the XML Literal.

Since this function must be evaluated according to the SPARQL semantics, we need to get the value of fs:sparql in the dynamic evaluation semantics of XSPARQL.

The built-in function fs:sparql applied to Value1 yields Value

dynEnv |- function fs:sparql with types (xs:string) on values (Value1) yields Value

In case of error (for instance, the query string is not syntactically correct, or the DatasetClause cannot be accessed), fs:sparql issues an error:

Value1 cannot be evaluated according to SPARQL semantics

dynEnv |- function fs:sparql with types (xs:string) on values (Value1) yields fn:error()

The only remaining part is defining the semantics of a GroupGraphPattern using our extended [·]Expr'. This mapping rule takes care that variables in scope of XSPARQL expressions are properly substituted using the evaluation mechanism of XQuery. To this end, we assume that [·]Expr' takes expressions in SPARQL's GroupGraphPattern syntax and constructs a sequence of strings and variables, by applying the auxiliary mapping rule [·]VarSubst to each of the graph pattern's variables. This rule looks up bound variables from the static environment and possibly replaces them to variables or to a string expression, where the value of the string is the name of the variable. This has the effect that unbound variables in GroupGraphPattern will be evaluated by SPARQL instead of XQuery. The statical semantics for [·]VarSubst is defined below using the next inference rules. They use the new judgement $VarName bound, which holds if the variable $VarName is bound in the current static environment.

statEnv |- $_VarName_RDFTerm bound

statEnv |- [$VarName]VarSubst = [$_VarName_RDFTerm]Expr

 

statEnv |- $VarName bound
        
statEnv |- not($_VarName_RDFTerm bound)

statEnv |- [$VarName]VarSubst = [$VarName]Expr

 

statEnv |- not($VarName bound)
        
statEnv |- not($_VarName_RDFTerm bound)

statEnv |- [$VarName]VarSubst = ["$VarName"]Expr

Next, we define the normalization of for expressions. In order to handle blank nodes appropriately in construct expressions, we need to decorate the variables of standard XQuery for-expressions with position variables. First, we must normalize for-expressions to core for-loops:

[ for $VarName1 OptTypeDeclaration1 OptPositionalVar1 in Expr1, ...,
$VarNamen OptTypeDeclarationn OptPositionalVarn in Exprn ReturnClause
]Expr'
==
[ for $VarName1 OptTypeDeclaration1 OptPositionalVar1 in Expr1 return

for $VarNamen OptTypeDeclarationn OptPositionalVarn in Exprn
ReturnClause
]Expr'

Now we can apply our decoration of the core for-loops (without position variables) recursively:

[for $VarNamei OptTypeDeclarationi in ExpriReturnClause]Expr'
==
[for $VarNamei OptTypeDeclarationi at $_VarNamei_Pos in[Expri]Expr' [ReturnClause]Expr']Expr

Similarly, let expressions must be normalized as follows:

[ let $VarName1 OptTypeDeclaration1 := Expr1, ...,
$VarNamen OptTypeDeclarationn := Exprn ReturnClause
]Expr'
==
[ let $VarName1 OptTypeDeclaration1 := Expr1 return

let $VarNamen OptTypeDeclarationn := Exprn
ReturnClause
]Expr'

Now we can recursively apply [·]Expr' on the core let-expressions:

[let $VarNamei OptTypeDeclarationi := Expri ReturnClause]Expr'
==
[let $VarNamei OptTypeDeclarationi :=[Expri]Expr' [ReturnClause]Expr']Expr

We do not specify where and order by clauses here, as they can be handled similarly as above let and for expressions.

2.2. CONSTRUCT Expressions

We define now the semantics for the ReturnClause. Expressions of form return Expr are evaluated as defined in the XQuery semantics. Stand-alone construct-clauses are normalized as follows:

[construct ConstructTemplate]Expr'
==
[return fs:serialize([ConstructTemplate]SubjPredObjList)]Expr

The auxiliary mapping rule [·]SubjPredObjlist rewrites variables and blank nodes inside of ConstructTemplates using the normalization mapping rules [·]Subject, [·]PredObjList, and [·]ObjList. They use the judgements expr is valid subject, valid predicate, and valid object, which holds if the expression expr is, according to the RDF specification [RDFCONCEPTS], a valid subject, predicate, and object, resp: i.e., subjects must be bound and not literals, predicates, must be bound, not literals and not blank nodes, and objects must be bound. If, for any reason, one criterion fails, the triple containing the ill-formed expression will be removed from the output. Free variables in the construct are unbound, hence triples containing such variables must be removed too. The boundness condition can be checked at runtime by wrapping each variable and FLWOR' into a fn:empty() assertion, which removes the corresponding triple from the ConstructTemplate output. Next, we sketch only some of the normalization rules; the missing rules should be clear from the context:

statEnv |- VarOrTerm is valid subject

statEnv |-     [VarOrTerm PropertyListNotEmpty]SubjPredObjList    ==
[fs:serialize([VarOrTerm]Subject, [PropertyListNotEmpty]PredObjlist)]Expr

 

[[ PropertyListNotEmpty]]SubjPredObjList
==
statEnv |-
[fs:serialize("[ ", [PropertyListNotEmpty]PredObjectList, " ]")]Expr

 

statEnv |- Verb is valid predicate
statEnv |- Object1 is valid object

statEnv |- Objectn is valid object

statEnv |-     [ Verb Object1, ..., Objectn]PredObjectList
    ==
[ fs:serialize([Verb]Expr', ",", [Object1]Expr',",",...,",",[Objectn]Expr')]Expr

Otherwise, if one of the premises is not true, we suppress the generation of this triple. One of the negated rules is the following:

statEnv |- not(VarOrTerm is valid subject)

statEnv |- [VarOrTerm PropertyListNotEmpty]SubjPredObjList = [""]Expr

The normalization for subjects, verbs, and objects according to [·]Expr' is similar to GroupGraphPattern: all variables in it will be replaced using [·]VarSubst.

Blank nodes inside of construction templates must be treated carefully by adding position variables from surrounding for expressions. To this end, we use [·]BNodeSubst. Since we normalize every for-loop by attaching position variables, we just need to retrieve the available position variables from the static environment. We assume a new static environment component statEnv.posVars which holds - similar to the statEnv.varType component - all in-context positional variables in the given static environment, that is, the variables defined in the at clause of any enclosing for loop.

statEnv |- statEnv.posVars = VarName1_Pos, ..., VarNamen_Pos

statEnv |- [_:BNodeName]BNodeSubst = [fs:serialize("_:",BNodeName,"_", VarName1_Pos , ..., VarNamen_Pos ) ]Expr

2.3. SPARQL Filter Operators

SPARQL filter expressions in WHERE GroupGraphPattern are evaluated using fs:sparql. But we additionally allow the following functions inherited from SPARQL in XSPARQL:

BOUND($A as xs:string) as xs:boolean
isIRI($A as xs:string) as xs:boolean
isBLANK($A as xs:string) as xs:boolean
isLITERAL($A as xs:string) as xs:boolean
LANG($A as xs:string) as xs:string
DATATYPE($A as xs:string) as xs:anyURI

The semantics of above functions is defined as follows:

statEnv |- $_Varname_Node bound

statEnv |- [BOUND($VarName)]Expr' = [if (fn:empty($_Varname_Node)) then fn:false() else fn:true()]Expr

 

statEnv |- $_Varname_NodeType bound

statEnv |- [isIRI($VarName)]Expr' =
[
if (fn:empty($_Varname_NodeType = "uri"))
then fn:false() else fn:true()
]Expr

 

statEnv |- $_Varname_NodeType bound

statEnv |- [isBLANK($VarName)]Expr' =
[
if (fn:empty($_Varname_NodeType = "blank"))
then fn:false() else fn:true()
]Expr

 

statEnv |- $_Varname_NodeType bound

statEnv |- [isLITERAL($VarName)]Expr' =
[
if (fn:empty($_Varname_NodeType = "literal"))
then fn:false() else fn:true()
]Expr

 

statEnv |- $_Varname_Node bound

statEnv |- [LANG($VarName)]Expr' = [fn:string($_Varname_Node/@xml:lang)]Expr

 

statEnv |- $_Varname_Node bound

statEnv |- [DATATYPE($VarName)]Expr' = [$_Varname_Node/@datatype]Expr

3. Correspondence between XSPARQL and XQuery

XSPARQL syntactically subsumes XQuery and - taking into account the shortcut notations defined in Section 4 of [XSPARQLLANGUAGE] - SPARQL construct queries. Concerning semantics, XSPARQL equally builds on top of its constituent languages. As shown above, we have extended the formal semantics of XQuery from [XQUERYSEMANTICS] by additional reduction rules which reduce each XSPARQL query to XQuery expressions which operate on results of SPARQL queries in the SPARQL's XML result format [SPARQLPROTOCOL].

Since we add only new reduction rules for SPARQL-like heads and bodies, it is easy to see that each native XQuery is treated in a semantically equivalent way in XSPARQL. The only thing affecting native XSPARQL queries are the "decoration" rules, which however, do not affect the final query result

Proposition 1. Each pragma-free XQuery is an XSPARQL query and has the same result under both XQuery and XSPARQL semantics

Proof (Sketch). As easily seen, given an XSPARQL query falling in the XQuery fragment, the result of our normalization is again an XQuery. Note that, however, even this fragment, our additional rewriting rules do change the original query in some cases. More concretely, what happens is that by our "decoration" rule each position-variable free for loop (i.e., that does not have an at clause) is decorated with a new position variable. As these new position variables start with an underscore they cannot occur in the original query, so this rewriting does not interfere with the semantics of the original query. The only rewriting rules which use the newly created position variables are those for rewriting blank nodes in construct parts, i.e., the [·]BNodeSubst rule. However, this rule only applies to XSPARQL queries which fall outside the native XQuery fragment.

A similar correspondence holds for native SPARQL queries. Let us now sketch the proof showing the equivalence of XSPARQL's semantics and the evaluation of rewritten SPARQL queries into native XQuery. Intuitively, we "inherit" the SPARQL semantics from the fs:sparql "oracle".

Let Ω denote a solution sequence of a an abstract SPARQL query q =(E, DS, R) where E is a SPARQL algebra expression, DS is an RDF Dataset and R is a set of variables called the query form (cf. [SPARQL]). Then, by SPARQLResult(Ω) we denote the SPARQL result XML format representation of Ω.

We are now ready to state some properties about our transformations. The following proposition states that any SPARQL select query can be equivalently viewed as an XSPARQL F'DWMR query.

Proposition 2. Let q = (EWM,DS,$x1,...,$xn) be a SPARQL query of the form select $x1,...,$xn DWM, where we denote by DS the RDF dataset (cf. [SPARQL]) corresponding to the DatasetClause (D), by G the respective default graph of DS, and by EWM the SPARQL algebra expression corresponding to WM and P be the pattern defined in the where part (W). If eval(DS(G), q) = Ω1, and

statEnv; dynEnv |- for $x1 ... $xn from D(G) where P return ($x1, ..., $xn) ⇒ Ω2.

Then, Ω1 ≡ Ω2 modulo representation. Here, by equivalence (≡) modulo representation we mean that both Ω1 and Ω2 represent the same sequences of (partial) variable bindings.

Proof (Sketch).
By the rule

[ for $x1 ... $xn from D(G) where P return ($x1, ..., $xn)]Expr'
==
[ let $aux_queryresult := [·]SparqlQuery ... for ... [·]SparqlResult ... return ($x1, ..., $xn)]Expr'

[·]SparqlQuery builds q as string without replacing any variable, since all variables in P are free. Then, the resulting string is applied to fs:sparql, which - since q was unchanged - by definition returns exactly SPARQLResult(Ω1), and thus the return part return ($x1, ..., $xn) which extracts Ω2 is obviously just a representational variant of Ω1.

By similar arguments, we can see that SPARQL's construct queries are treated semantically equivalent in XSPARQL and in SPARQL, taking into account the shortcut notations defined in Section 4 of [XSPARQLLANGUAGE]. The idea here is that the rewriting rules constructs from Section 2.2 extract exactly the triples from the solution sequence from the body defined as defined in the SPARQL semantics [SPARQL].


4. References

[RDFCONCEPTS]
Graham Klyne and Jeremy Carroll (eds.). Resource Description Framework (RDF): Concepts and Abstract Syntax, February 2004. W3C Recommendation, available at http://www.w3.org/TR/rdf-concepts/.
[SPARQL]
Eric Prud'hommeaux and Andy Seaborne (eds.). SPARQL Query Language for RDF, 15 January 2008. W3C Recommendation, available at http://www.w3.org/TR/rdf-sparql-query/.
[SPARQLPROTOCOL]
Kendall Grant Clark, Lee Feigenbaum, and Elias Torres. SPARQL Protocol for RDF, 15 January 2008. W3C Recommendation, available at http://www.w3.org/TR/rdf-sparql-protocol/.
[XPATHFUNCT]
Ashok Malhotra, Jim Melton, and Norman Walsh (eds.). XQuery 1.0 and XPath 2.0 Functions and Operators. W3C Recommendation, 23 January 2007. Available at http://www.w3.org/TR/xpath-functions/.
[XQUERYSEMANTICS]
Denise Draper, Peter Fankhauser, Mary Fernández, Ashok Malhotra, Kristoffer Rose, Michael Rys, Jérôme Siméon, and Philip Wadler. XQuery 1.0 and XPath 2.0 Formal Semantics. W3c recommendation, W3C, January 2007. W3C Recommendation, available at http://www.w3.org/TR/xquery-semantics/.
[XSPARQLLANGUAGE]
XSPARQL Language Specification. Document included in the present specification.

XSPARQL: Implementation and Test-cases

Authors:
Nuno Lopes - DERI, NUI Galway
Thomas Krennwallner - Institute of Information Systems, Vienna University of Technology
Axel Polleres - DERI, NUI Galway
Waseem Akhtar - DERI, NUI Galway
Stéphane Corlosquet - DERI, NUI Galway

This work is supported by Science Foundation Ireland under grants number SFI/02/CE1/I131 and SFI/08/CE/I1380 and under the European Commission European FP6 project inContext (IST-034718).


Abstract

XSPARQL is a query language combining XQuery and SPARQL for transformations between RDF and XML. This document provides a description of a prototype implementation of the language based on off-the-shelf XQuery and SPARQL engines. Along with a high-level description of the prototype the document presents a set of test queries and their expected output which are to be understood as illustrative help for possible other implementers.


Table of Contents


1. Introduction

This document introduces a simple implementation of the XSPARQL language as a proof of concept. The implementation described here can be found in the XSPARQLer Open Source project page at SourceForge.net.

2. XSPARQL Implementation

The main idea behind our implementation is translating XSPARQL queries to corresponding XQueries which possibly use interleaved calls to a SPARQL endpoint. The architecture of our prototype shown in Figure 1 consists of three main components: (1) a query rewriter, which turns an XSPARQL query into an XQuery; (2) a SPARQL endpoint, for querying RDF from within the rewritten XQuery; and (3) an XQuery engine for computing the result document.

The rewriter (Algorithm 1) takes as input a full XSPARQL QueryBody [XQUERYSEMANTICS] q (i.e., a sequence of FLWOR' expressions), a set of bound variables b and a set of position variables p, which we explain below. For a FL (or F', resp.) clause s, we denote by vars(s) the list of all newly declared variables (or the varlist, resp.) of s. We only sketch the core rewriting function rewrite() here; additional machinery handling the prolog including function, variable, module, and namespace declarations is needed in the full implementation. The rewriting is initiated by invoking rewrite(q, ∅, ∅) with empty bound and position variables an results in a syntactically valid XQuery that can be executed using an off-the-shelf XQuery implementation.

Algorithm 1: rewrite(q, b, p): Rewrite XSPARQL q to an XQuery
Input: XSPARQL query q, set of bounded variables b, set
of position variables p

Result: XQuery
1 if q is of form s1, ... , sk then
2     return rewrite(s1, b, p), ... , rewrite(sk, b, p)
3 else if q is of form for $x1in XPathExpr1, ... , $xkin XPathExprk s1then
4     return for $x1at $x1_pos in XPathExpr1,
... , $xkat $xk_pos in XPathExprk
5     rewrite(s1, b, p ∪ {$x1_pos, ... , $xk_pos})
6 else if q is of form for $x1... $xnfrom D where { pattern } M s1then
7     return let $aux query := sparql(D, {$x1, ... , $xn}, pattern, M, b)
8     for $aux_result in doc($aux_query)//sparql:result
9     auxvars({$x1, ... , $xn})    rewrite(s1, b ∪ vars(q), p)
10 else if q is of form construct {template} then
11     return return (rewrite-template(template, b, p) )
12 else
13     split q into its subexpressions s1, ... , sn
14     for j := 1, ... , n do bj= b
1=<i=<j-1 = vars(si)
15     if n > 1 then return q [s1/rewrite(s1, b1, p), ... , sn/rewrite(sn, bn, p)]
16     else return q
17 end

The rewriter is implemented as a Python script which is part of the XSPARQLer Open Source distribution. We provide an online interface where example queries can be found and tested at http://xsparql.deri.org/demo/. Figure 3 shows the output of our translation for the construct query in Figure 2. Let us explain the algorithm, which may be viewed as consisting of two parts, responsible for lifting and lowering (cf. Section 2 of [XSPARQLLANGUAGE]), respectively, using this sample output.


Figure 2: RDF-to-RDF mapping in XSPARQL
prefix vc: <http://www.w3.org/2001/vcard-rdf/3.0#>
prefix foaf: <http://xmlns.com/foaf/0.1/>
construct {_:b foaf:name
             { fn:concat($N," ",$F) } . }
from <vc.rdf>
where { $P vc:Given $N. $P vc:Family $F.}
Figure 3: XQuery rewriting output for the query from Figure 2:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
25
26
27
28
29
30
31
32
33
34
35
36
37
import module namespace _xsparql = "http://xsparql.deri.org/XSPARQLer/xsparql.xquery"
 at "http://xsparql.deri.org/XSPARQLer/xsparql.xquery";
declare namespace vc = "http://www.w3.org/2001/vcard-rdf/3.0#";
declare namespace foaf = "http://xmlns.com/foaf/0.1/";
declare namespace _sparql_result = "http://www.w3.org/2005/sparql-results#";
declare variable $_NS1 := "prefix vc: <http://www.w3.org/2001/vcard-rdf/3.0#> ";
declare variable $_NS2 := "prefix foaf: <http://xmlns.com/foaf/0.1/> "; 
_xsparql:_serialize(("@",$_NS1,".","@",$_NS2,".")), 
let $_aux1 := _xsparql:_serialize(( 
 "http://example.org/sparql?query=", fn:encode-for-uri(_xsparql:_serialize($_NS1, $_NS2, 
 "select $P $N $F from <vc.rdf> where {$P vc:Given $N. $P vc:Family $F.}")))) 
for $_aux_result1 at $_aux_result1_pos in doc($_aux1)//_sparql_result:result 
 let $_P_Node := $_aux_result1/_sparql_result:binding[@name="P"] 
 let $P := data($_P_Node/*)
 let $_P_NodeType := name($_P_Node/*)
 let $_P_NodeDatatype := string($_P_Node/*/@datatype)
 let $_P_NodeLang := string($_P_Node/*/@lang)
 let $_P_RDFTerm := _xsparql:_rdf_term($_P_NodeType,$P)
 let $_N_Node := $_aux_result1/_sparql_result:binding[@name="N"]
 let $N := data($_N_Node/*)
 let $_N_NodeType := name($_N_Node/*)
 let $_N_NodeDatatype := string($_N_Node/*/@datatype)
 let $_N_NodeLang := string($_N_Node/*/@lang)
 let $_N_RDFTerm := _xsparql:_rdf_term($_N_NodeType,$N)
 let $_F_Node := $_aux_result1/_sparql_result:binding[@name="F"]
 let $F := data($_F_Node/*)
 let $_F_NodeType := name($_F_Node/*)
 let $_F_NodeDatatype := string($_F_Node/*/@datatype)
 let $_F_NodeLang := string($_F_Node/*/@lang)
 let $_F_RDFTerm := _xsparql:_rdf_term($_F_NodeType,$F) 
 let $_validSubject1 := _xsparql:_serialize(("_:b", "_", data($_aux_result1_Pos))) 
 let $_validObject2 := _xsparql:_serialize(('"',   fn:concat($N   , " "   , $F   )   ,  '"')) 
 return if (_xsparql:_validSubject("",  $_validSubject1)) then (   
   if (_xsparql:_validObject("",  $_validObject2)) then (
     _xsparql:_serialize(($_validSubject1,  " foaf:name ", $_validObject2, " ." ))
   ) else "") else "" 

Before we rewrite the QueryBody q, we process the prolog (P) of the XSPARQL query and output every namespace declaration as Turtle string literals "@prefix ns: <URI>." After generating the prolog (lines 1-8 of the output), the rewriting of the QueryBody is performed recursively following the syntax of XSPARQL. During the traversal of the nested FLWOR' expressions, SPARQL-like bodies (lowering) or heads (lifting) will be replaced by XQuery expressions, which handle our two tasks. The lowering part is processed first:

Lowering Normal XQuery-like FLWO expressions are simply copied to the output and "decorated" (cf. [XSPARQLSEMANTICS]) with position variables, see lines 3-5 of Algorithm 1. The lowering part of XSPARQL, i.e., SPARQL-like F'DWM blocks, is "encoded" in XQuery with interleaved calls to an external SPARQL endpoint. To this end, we translate F'DWM blocks into equivalent XQuery FLWO expressions which retrieve SPARQL result XML documents [SPARQLRESULT] from a SPARQL engine; i.e., we "push" each F'DWM body to the SPARQL side, by translating it to a native select query string, see lines 6-9 of Algorithm 1. The auxiliary function sparql() in line 7 of our rewriter provides the functionality of transforming the where { pattern } part of F'DWM clauses to XQuery expressions which have all bound variables in pattern replaced by the values of the variables; "free" XSPARQL variables serve as binding variables for the SPARQL query result. The outcome of the sparql() function is a list of expressions, which is concatenated and URI-encoded using XQuery's XPath functions, and wrapped into a URI with http scheme pointing to the SPARQL query service (lines 9-11 of the output), cf. [SPARQLPROTOCOL]. Then we create a new XQuery for-loop that iterates over variable $aux_result, i.e., it iterates over the query answers extracted from the SPARQL XML result returned by the SPARQL query processor (line 12 of the output). For each variable $xivars(s) (i.e., in the (F') for clause of the original F'DWM body), new auxiliary variables are defined in separate let-expressions extracting its node, content, type (i.e., literal, uri, or blank), datatype URI or language tag if present, and the corresponding RDFTerm ($xi_Node, $xi, $xi_NodeType, $xi_NodeDataype, $xi_NodeLang and $xi_RDFTerm, resp.) by appropriate XPath expressions (lines 13-31 of Figure 3); the auxvars() helper function in line 9 of Algorithm 1 is responsible for this. Thereafter, the rewriter is called again recursively, with the newly declared variables added to b.

Lifting For the lifting part, i.e., SPARQL-like constructs in the R part, the transformation process is straightforward: Algorithm 1 is called on q and recursively decorates every for $Var expression by fresh position variables (line 12 of our example output); ultimately, construct templates are rewritten to an assembled string of the pattern's constituents, filling in variable bindings and evaluated subexpressions (lines 32-37 of the output): Blank nodes in constructs need special care, since, according to SPARQL's semantics, these must create new blank node identifiers for each solution binding. This is solved by "adorning" each blank node identifier in the construct part with the above-mentioned position variables from any enclosing for-loops, thus creating a new, unique blank node identifier in each loop (line 32 in the output). The auxiliary function rewrite-template() in line 11 of the algorithm provides this functionality by simply creating concatenations the lists of all position variable p as expressions to each blank node id; if there are nested expressions in the supplied construct {template}, rewrite-template() will return a sequence of nested FLWORs with each having rewrite() applied recursively on these expressions with the in-scope bound and position variables. rewrite-template() will create new variables for each of the RDFTerms that need to be dynamically evaluated for validity (variables $_validSubject1 and in $_validObject2 lines 32 and 33 in the output) and finally rewrite-template() generates a return clause which checks validity of the generated triples in Turtle syntax by respective helper function calls (validSubject(), validPredicate(), validObject(), which are declared - along with all other helper functions in the http://xsparql.deri.org/XSPARQLer/xsparql.xquery library), see lines 34-37 of the output.

Note that expressions involving SPARQL-like construct clauses create Turtle [TURTLE] output. Generating other output formats such as RDF/XML if needed is optionally done in our implementation by simple post-processing of the Turtle output by using standard RDF processing tools.

Finally, let us remark that that although both our implementation as well as XSPARQL's semantics definition [XQUERYSEMANTICS] use the SPARQL result format [SPARQLRESULT] for retrieving the results of a SPARQL query, other implementations can be conceived that do not require this intermediate step, but either use optimized internal data-structures to pass results between SPARQL and XQuery native processors, or implement a completely native, integrated processor.

3. Test cases

In the following, a set of XSPARQL test queries is presented. All these queries, along with the necessary input data, are also available in the Examples, Test cases and Use cases file which is part of the specification. Along with the original queries we also present the rewriting performed by our implementation as well as the expected query results.

3.1 foaf_lifting_naive.xsparql

This example query generates FOAF data from attribute values and element content in an input XML extracted by respectively simple XPath expressions. It is intended to demonstrate a simple lifting transformation from XML to RDF.

XSPARQL query:

declare namespace foaf = "http://xmlns.com/foaf/0.1/";
for $person in doc("relations.xml")//person,
    $nameA in $person/@name,
    $nameB in $person/knows
construct
{
[ foaf:name {data($nameA)}; a foaf:Person ]
foaf:knows
[ foaf:name {data($nameB)}; a foaf:Person ].
}

Rewritten query:

import module namespace _xsparql = "http://xsparql.deri.org/XSPARQLer/xsparql.xquery"
at "http://xsparql.deri.org/XSPARQLer/xsparql.xquery";

declare namespace _sparql_result = "http://www.w3.org/2005/sparql-results#";
declare namespace foaf = "http://xmlns.com/foaf/0.1/" ;

declare variable $_NS1 := "prefix  foaf:  <http://xmlns.com/foaf/0.1/>";

 _xsparql:_serialize(("  @", $_NS1, ".", "")),

for $person at $_person_Pos  in doc("relations.xml")//person  , 
 $nameA at $_nameA_Pos  in $person/@name  , 
 $nameB at $_nameB_Pos  in $person/knows   

let $_validObject1 := _xsparql:_serialize(('"', data($nameA), '"')) 
let $_validObject2 := _xsparql:_serialize(('"', data($nameB), '"')) 

  return (_xsparql:_removeEmpty(_xsparql:_serialize((
                 "[", 
                  if ( _xsparql:_validObject("",  $_validObject1)) 
                      then (_xsparql:_serialize(" foaf:name ", $_validObject1, ";")) else "",
                  _xsparql:_serialize(" a ",   'foaf:Person', ";"),  
                  _xsparql:_serialize("foaf:knows ", 
                            "[",
                            if ( _xsparql:_validObject( "", $_validObject2)) 
                               then (_xsparql:_serialize(" foaf:name ", $_validObject2, ";")) else "",  
                            _xsparql:_serialize(" a ",   'foaf:Person',";"), 
                           " ]") , 
                  " ] ."))))

Expected output:

@prefix foaf: <http://xmlns.com/foaf/0.1/>.

[ foaf:name "Alice"; a foaf:Person;foaf:knows [ foaf:name "Bob"; a foaf:Person; ] ] . 
[ foaf:name "Alice"; a foaf:Person;foaf:knows [ foaf:name "Charles"; a foaf:Person; ] ] . 
[ foaf:name "Bob"; a foaf:Person;foaf:knows [ foaf:name "Charles"; a foaf:Person; ] ] . 

3.2 foaf_lifting.xsparql

This query performs a very similar task as the previous one: generate FOAF data from input XML. It demonstrates a full lifting transformation from XML to RDF. Particularly, the difference to the previous transformation lies in the fact that the same blank node identifier is given to people with the same name (assuming that names uniquely identify people in the input XML file at hand). The blank node identifier is "computed" from the position of the first occurrence of the name node in the source XML tree.

XSPARQL query:

declare namespace foaf="http://xmlns.com/foaf/0.1/"; 
let $doc := doc("relations.xml")
let $persons := $doc//*[@name or ../knows] 
return 
 for $p in $persons 
 let $n := if( $p[@name]) then $p/@name else $p 
 let $id := count($p/preceding::*) + count($p/ancestor::*) 
 where not(exists($p/following::*[@name=$n or data(.)=$n])) 
 construct 
 { _:b{$id} a foaf:Person; 
            foaf:name {data($n)}. 
   { for $k in $persons 
     let $kn := if( $k[@name]) then $k/@name else $k 
     let $kid :=count($k/preceding::*) + count($k/ancestor::*) 
     where $kn = data($doc//*[@name=$n]/knows) and 
                 not(exists($kn/../following::*[@name=$kn or data(.)=$kn])) 
     construct 
     { _:b{$id}  foaf:knows _:b{$kid}. 
       _:b{$kid} a foaf:Person. }
   } 
 }

Rewritten query:

import module namespace _xsparql = "http://xsparql.deri.org/XSPARQLer/xsparql.xquery"
at "http://xsparql.deri.org/XSPARQLer/xsparql.xquery";

declare namespace _sparql_result = "http://www.w3.org/2005/sparql-results#";
declare namespace foaf = "http://xmlns.com/foaf/0.1/" ;

declare variable $_NS1 := "prefix  foaf:  <http://xmlns.com/foaf/0.1/>";

_xsparql:_serialize("  @", $_NS1, ".", ""),

let $doc  := doc("relations.xml")  

let $persons  := $doc//*[@name  or ../knows ]  
 return 
  for $p at $_p_Pos  in $persons  
   let $n  := if ( $p[@name  ]) then $p/@name   else $p  
   let $id  := count($p/preceding::*)+count($p/ancestor::*)  

   let $_validSubject4 := _xsparql:_serialize(("_:b",  data($id))) 
   let $_validObject5 := _xsparql:_serialize(( '"',   data($n)   ,  '"')) 
   
   where not(exists($p/following::*[@name =$n  or data(.) =$n ]))  

  return ( 
   if ( _xsparql:_validSubject( "",  $_validSubject4)) 
          then (_xsparql:_serialize(($_validSubject4,  " a ",   'foaf:Person',  " .")),  
                if ( _xsparql:_validObject( "",  $_validObject5)) 
                 then (_xsparql:_serialize(($_validSubject4,  " foaf:name ", $_validObject5, " ."))) else ""  
          ) else "" ,
 
   for $k at $_k_Pos  in $persons  
    let $kn  := if ( $k[@name  ]) then $k/@name   else $k  
    let $kid  := count($k/preceding::*)+count($k/ancestor::*)  

    let $_validSubject1 := _xsparql:_serialize(("_:b",  data($id))) 
    let $_validObject2 := _xsparql:_serialize(("_:b",  data($kid))) 
    let $_validSubject3 := _xsparql:_serialize("_:b",  data($kid))) 

    where $kn =data($doc//*[@name =$n  ]/knows) and not(exists($kn/../following::*[@name =$kn  or data(.) =$kn ]))  
    return ( 
     if ( _xsparql:_validSubject( "",  $_validSubject1)) then (
        if ( _xsparql:_validObject( "",  $_validObject2)) 
          then (_xsparql:_serialize(($_validSubject1,  " foaf:knows ", $_validObject2, " ."))) else ""  
     ) else "" ,
    if ( _xsparql:_validSubject( "",  $_validSubject3)) 
     then (_xsparql:_serialize(($_validSubject3,  " a ",   'foaf:Person',  " ."))) else ""  
   )  
  )

Expected output:

@prefix foaf: <http://xmlns.com/foaf/0.1/>. 

_:b1 a foaf:Person . 
_:b1 foaf:name "Alice" . 
_:b1 foaf:knows _:b4 . 
_:b4 a foaf:Person . 
_:b1 foaf:knows _:b6 . 
_:b6 a foaf:Person . 
_:b4 a foaf:Person . 
_:b4 foaf:name "Bob" . 
_:b4 foaf:knows _:b6 . 
_:b6 a foaf:Person . 
_:b6 a foaf:Person . 
_:b6 foaf:name "Charles" . 

3.3 vCard2foaf.xsparql

This query performs a simple mapping from vCard given and family name properties into FOAF full names; it shows the use of XPath and XQuery built-in functions for manipulating RDF.

XSPARQL query:

prefix vc: <http://www.w3.org/2001/vcard-rdf/3.0#>
prefix foaf: <http://xmlns.com/foaf/0.1/>
construct { _:b foaf:name {fn:concat($N," ", $F)}.}
from <vCard.rdf>
where { $P vc:Given $N. $P vc:Family $F. }

Rewritten query:

import module namespace _xsparql = "http://xsparql.deri.org/XSPARQLer/xsparql.xquery"
at "http://xsparql.deri.org/XSPARQLer/xsparql.xquery";

declare namespace _sparql_result = "http://www.w3.org/2005/sparql-results#";
declare namespace vc = "http://www.w3.org/2001/vcard-rdf/3.0#";
declare namespace foaf = "http://xmlns.com/foaf/0.1/";

declare variable $_NS1 := "prefix  vc:  <http://www.w3.org/2001/vcard-rdf/3.0#>";
declare variable $_NS2 := "prefix  foaf:  <http://xmlns.com/foaf/0.1/>";

_xsparql:_serialize((  "@", $_NS1, ".", "  @", $_NS2, ".", "")),

let $_aux1 := _xsparql:_serialize(("http://example.org/sparql?query=", fn:encode-for-uri( _xsparql:_serialize((  $_NS1,  $_NS2, "
select $N $P $F from <vCard.rdf>  where {    $P   vc:Given    $N   .    $P   vc:Family    $F   . } ")))))
for $_aux_result1 at $_aux_result1_Pos in doc($_aux1)//_sparql_result:result
 let $_N_Node := $_aux_result1/_sparql_result:binding[@name="N"]
 let $N := data($_N_Node/*)
 let $_N_NodeType := name($_N_Node/*)
 let $_N_NodeDatatype := string($_N_Node/*/@datatype)
 let $_N_NodeLang := string($_N_Node/*/@lang)
 let $_N_RDFTerm :=  _xsparql:_rdf_term($_N_NodeType,$N)
 let $_P_Node := $_aux_result1/_sparql_result:binding[@name="P"]
 let $P := data($_P_Node/*)
 let $_P_NodeType := name($_P_Node/*)
 let $_P_NodeDatatype := string($_P_Node/*/@datatype)
 let $_P_NodeLang := string($_P_Node/*/@lang)
 let $_P_RDFTerm := _xsparql:_rdf_term($_P_NodeType,$P)
 let $_F_Node := $_aux_result1/_sparql_result:binding[@name="F"]
 let $F := data($_F_Node/*)
 let $_F_NodeType := name($_F_Node/*)
 let $_F_NodeDatatype := string($_F_Node/*/@datatype)
 let $_F_NodeLang := string($_F_Node/*/@lang)
 let $_F_RDFTerm := _xsparql:_rdf_term($_F_NodeType,$F)

 let $_validSubject1 := _xsparql:_serialize(("_:b", "_", data($_aux_result1_Pos))) 
 let $_validObject2 := _xsparql:_serialize(( '"',   fn:concat($N   , " "   , $F)   ,  '"')) 

 return if ( _xsparql:_validSubject( "",  $_validSubject1)) 
           then (if ( _xsparql:_validObject( "",  $_validObject2)) 
                    then (_xsparql:_serialize(($_validSubject1,  " foaf:name ", $_validObject2, " ."))) else ""  
           ) else "" 

Expected output:

@prefix vc: <http://www.w3.org/2001/vcard-rdf/3.0#>. 
@prefix foaf: <http://xmlns.com/foaf/0.1/>. 

_:b_1 foaf:name "Axel Polleres" . 

3.4 foaf_lowering.xsparql

This query generates XML data from an input RDF file containing FOAF data. It demonstrates the lowering task, i.e., mapping from RDF to XML.

XSPARQL query:

declare namespace foaf = "http://xmlns.com/foaf/0.1/";
<relations>
{ for $Person $Name from <relations.rdf>
  where { $Person foaf:name $Name }
  order by $Name
  return <person name="{$Name}">
         { for $FName from <relations.rdf>
           where { $Person foaf:knows $Friend.
                   $Person foaf:name $Name.
                   $Friend foaf:name $FName. }
           return <knows> { $FName }</knows>
         }
         </person>
}
</relations>

Rewritten query:

import module namespace _xsparql = "http://xsparql.deri.org/XSPARQLer/xsparql.xquery"
at "http://xsparql.deri.org/XSPARQLer/xsparql.xquery";

declare namespace _sparql_result = "http://www.w3.org/2005/sparql-results#";
declare namespace foaf = "http://xmlns.com/foaf/0.1/" ;

declare variable $_NS1 := "prefix  foaf:  <http://xmlns.com/foaf/0.1/>";
<relations>{ 
 let $_aux1 := _xsparql:_serialize(("http://example.org/sparql?query=", fn:encode-for-uri( _xsparql:_serialize((  $_NS1, "
  select $Person $Name from <relations.rdf>  where {    $Person   foaf:name    $Name   . } order by $Name")))))

 for $_aux_result1 at $_aux_result1_Pos in doc($_aux1)//_sparql_result:result
  let $_Person_Node := ($_aux_result1/_sparql_result:binding[@name = "Person"])
  let $Person := data($_Person_Node/*)
  let $_Person_NodeType := name($_Person_Node/*)
  let $_Person_NodeDatatype := string($_Person_Node/*/@datatype)
  let $_Person_NodeLang := string($_Person_Node/*/@lang)
  let $_Person_RDFTerm :=  _xsparql:_rdf_term($_Person_NodeType, $Person)
  let $_Name_Node := ($_aux_result1/_sparql_result:binding[@name = "Name"])
  let $Name := data($_Name_Node/*)
  let $_Name_NodeType := name($_Name_Node/*)
  let $_Name_NodeDatatype := string($_Name_Node/*/@datatype)
  let $_Name_NodeLang := string($_Name_Node/*/@lang)
  let $_Name_RDFTerm :=  _xsparql:_rdf_term($_Name_NodeType, $Name)

  return <person name = "{$Name}">{ 
   let $_aux2 := _xsparql:_serialize(("http://example.org/sparql?query=", fn:encode-for-uri( 
                  _xsparql:_serialize((  $_NS1, "select $FName from <relations.rdf>  where {     ", 
                            $_Person_RDFTerm, " foaf:knows $Friend .", 
                            $_Person_RDFTerm, " foaf:name ", 
                            $_Name_RDFTerm, " . $Friend foaf:name $FName . }")))
                 ))

   for $_aux_result2 at $_aux_result2_Pos in doc($_aux2)//_sparql_result:result
    let $_FName_Node := ($_aux_result2/_sparql_result:binding[@name = "FName"])
    let $FName := data($_FName_Node/*)
    let $_FName_NodeType := name($_FName_Node/*)
    let $_FName_NodeDatatype := string($_FName_Node/*/@datatype)
    let $_FName_NodeLang := string($_FName_Node/*/@lang)
    let $_FName_RDFTerm :=  _xsparql:_rdf_term($_FName_NodeType, $FName)

    return <knows>{ $FName   }</knows>   
  }</person>   
}</relations>  

Expected output:

<relations>
   <person name="Alice">
      <knows>Charles</knows>
      <knows>Bob</knows>
   </person>
   <person name="Bob">
      <knows>Charles</knows>
      </person>
   <person name="Charles"/>
</relations> 

3.5 simple.xsparql

This query selects only persons "known by somebody" in the input RDF data. All these persons are then mapped to a class where the class URI is assigned to a variable using an XQuery let clause. The example demonstrates the combination of constructs from XQuery and SPARQL, more specifically the reuse of XQuery variables within SPARQL like construct clauses.

XSPARQL query:

prefix : <http://www.example.org>
prefix foaf: <http://xmlns.com/foaf/0.1/>
let $y := "http://www.example.org/knownPerson"
for $x from <foaf.rdf>
where {$s foaf:knows $x}
construct {$x a <{$y}> }

Rewritten query:

import module namespace _xsparql = "http://xsparql.deri.org/XSPARQLer/xsparql.xquery"
at "http://xsparql.deri.org/XSPARQLer/xsparql.xquery";

declare namespace _sparql_result = "http://www.w3.org/2005/sparql-results#";
declare default element namespace "http://www.example.org";
declare namespace foaf = "http://xmlns.com/foaf/0.1/";

declare variable $_NS1 := "prefix  :  <http://www.example.org>";
declare variable $_NS2 := "prefix  foaf:  <http://xmlns.com/foaf/0.1/>";

_xsparql:_serialize((  "  @", $_NS1, ".", "  @", $_NS2, ".", "" )),

let $y  := "<http://www.example.org/knownPerson>"  

let $_aux1 := _xsparql:_serialize(("http://example.org/sparql?query=", fn:encode-for-uri( _xsparql:_serialize((  $_NS1,  $_NS2, "
 select $x from <http://www.polleres.net/foaf.rdf>  where {    $s   foaf:knows    $x   . } ")))))

for $_aux_result1 at $_aux_result1_Pos in doc($_aux1)//_sparql_result:result
 let $_x_Node := ($_aux_result1/_sparql_result:binding[@name = "x"])
 let $x := data($_x_Node/*)
 let $_x_NodeType := name($_x_Node/*)
 let $_x_NodeDatatype := string($_x_Node/*/@datatype)
 let $_x_NodeLang := string($_x_Node/*/@lang)
 let $_x_RDFTerm :=  _xsparql:_rdf_term($_x_NodeType, $x)

 let $_validObject1 := _xsparql:_serialize(( '"',  $y,  '"')) 

 return ( if ( _xsparql:_validSubject( "",  $_x_RDFTerm))
             then (if ( _xsparql:_validObject( "",  $_validObject1)) 
                      then (_xsparql:_serialize(($_x_RDFTerm,  " a ", $_validObject1, " ."))) else ""                 
             ) else ""  
        )

Expected output:

@prefix : <http://www.example.org>. 
@prefix foaf: <http://xmlns.com/foaf/0.1/>. 

_:b0 a <http://www.example.org/knownPerson> . 
<http://danbri.org/foaf.rdf#danbri> a <http://www.example.org/knownPerson> . 
_:b1 a <http://www.example.org/knownPerson> . 
<http://richard.cyganiak.de/foaf.rdf#cygri> a <http://www.example.org/knownPerson> . 
<http://nets.ii.uam.es/~rlara/foaf.rdf#me> a <http://www.example.org/knownPerson> . 
_:b2 a <http://www.example.org/knownPerson> . 
_:b3 a <http://www.example.org/knownPerson> . 
_:b4 a <http://www.example.org/knownPerson> . 
<http://eyaloren.org/foaf.rdf#me> a <http://www.example.org/knownPerson> . 
_:b5 a <http://www.example.org/knownPerson> . 
<http://harth.org/andreas/foaf#ah> a <http://www.example.org/knownPerson> . 
<http://www.aifb.uni-karlsruhe.de/Personen/viewPersonOWL/id2084instance> a <http://www.example.org/knownPerson> . 
<http://page.mi.fu-berlin.de/mochol/foaf.rdf#me> a <http://www.example.org/knownPerson> . 
_:b6 a <http://www.example.org/knownPerson> . 
<http://page.mi.fu-berlin.de/~nixon/foaf.rdf#nixon> a <http://www.example.org/knownPerson> . 
_:b7 a <http://www.example.org/knownPerson> . 
_:b8 a <http://www.example.org/knownPerson> . 
_:b9 a <http://www.example.org/knownPerson> . 
<http://www.postsubmeta.net/foaf.rdf#TK> a <http://www.example.org/knownPerson> . 
_:b10 a <http://www.example.org/knownPerson> . 
_:b11 a <http://www.example.org/knownPerson> . 
_:b12 a <http://www.example.org/knownPerson> . 
<http://sw.deri.org/~haller/foaf.rdf#ah> a <http://www.example.org/knownPerson> . 
_:b13 a <http://www.example.org/knownPerson> . 

3.6 simple_filter.xsparql

This query performs the same task as the previous one but removes persons only identified with a Blank Node using a SPARQL filter expression.

XSPARQL query:

prefix : <http://www.example.org>
prefix foaf: <http://xmlns.com/foaf/0.1/>
let $y := "http://www.example.org/knownPerson"
for * from <foaf.rdf>
where {$s foaf:knows $x filter (!isblank($x))}
construct {$x a <{$y}> }

Rewritten query:

import module namespace _xsparql = "http://xsparql.deri.org/XSPARQLer/xsparql.xquery"
at "http://xsparql.deri.org/XSPARQLer/xsparql.xquery";

declare namespace _sparql_result = "http://www.w3.org/2005/sparql-results#";
declare default element namespace "http://www.example.org";
declare namespace foaf = "http://xmlns.com/foaf/0.1/";

declare variable $_NS1 := "prefix  :  <http://www.example.org>";
declare variable $_NS2 := "prefix  foaf:  <http://xmlns.com/foaf/0.1/>";

_xsparql:_serialize((  "  @", $_NS1, ".", "  @", $_NS2, ".", "" )),

let $y  := "http://www.example.org/knownPerson"  

let $_aux1 := _xsparql:_serialize(("http://example.org/sparql?query=", fn:encode-for-uri( _xsparql:_serialize((  $_NS1,  $_NS2, "
 select $s $x from <http://www.polleres.net/foaf.rdf>  where {    $s   foaf:knows    $x   . filter(!isblank($x))} ")))))

for $_aux_result1 at $_aux_result1_Pos in doc($_aux1)//_sparql_result:result
 let $_s_Node := ($_aux_result1/_sparql_result:binding[@name = "s"])
 let $s := data($_s_Node/*)
 let $_s_NodeType := name($_s_Node/*)
 let $_s_NodeDatatype := string($_s_Node/*/@datatype)
 let $_s_NodeLang := string($_s_Node/*/@lang)
 let $_s_RDFTerm :=  _xsparql:_rdf_term($_s_NodeType, $s)
 let $_x_Node := ($_aux_result1/_sparql_result:binding[@name = "x"])
 let $x := data($_x_Node/*)
 let $_x_NodeType := name($_x_Node/*)
 let $_x_NodeDatatype := string($_x_Node/*/@datatype)
 let $_x_NodeLang := string($_x_Node/*/@lang)
 let $_x_RDFTerm :=  _xsparql:_rdf_term($_x_NodeType, $x)
 
 let $_validObject1 := _xsparql:_serialize(("<" , $y   , ">")) 

 return ( if ( _xsparql:_validSubject( "",  $_x_RDFTerm)) 
           then (if ( _xsparql:_validObject( "",  $_validObject1)) 
                  then (_xsparql:_serialize(( $_x_RDFTerm,  " a ",     $_validObject1  ,  " ."))) else ""
                ) else ""  
        )

Expected output:

@prefix : <http://www.example.org>. 
@prefix foaf: <http://xmlns.com/foaf/0.1/>. 

<http://danbri.org/foaf.rdf#danbri> a <http://www.example.org/knownPerson> . 
<http://richard.cyganiak.de/foaf.rdf#cygri> a <http://www.example.org/knownPerson> . 
<http://nets.ii.uam.es/~rlara/foaf.rdf#me> a <http://www.example.org/knownPerson> . 
<http://eyaloren.org/foaf.rdf#me> a <http://www.example.org/knownPerson> . 
<http://www.aifb.uni-karlsruhe.de/Personen/viewPersonOWL/id2084instance> a <http://www.example.org/knownPerson> . 
<http://harth.org/andreas/foaf#ah> a <http://www.example.org/knownPerson> . 
<http://page.mi.fu-berlin.de/mochol/foaf.rdf#me> a <http://www.example.org/knownPerson> . 
<http://page.mi.fu-berlin.de/~nixon/foaf.rdf#nixon> a <http://www.example.org/knownPerson> . 
<http://www.postsubmeta.net/foaf.rdf#TK> a <http://www.example.org/knownPerson> . 
<http://sw.deri.org/~haller/foaf.rdf#ah> a <http://www.example.org/knownPerson> . 

3.7 distribution_simple.xsparql

Given a set of dated entries, this example extracts the distribution of these entries over time, grouping the entries by day and counting the entries for each day. A typical scenario where such data could apply would be a set of IRC logs in a given month, annotated in RDF, where one wants to get an overview over the activity on a channel over the month. This example shows how XQuery features can be used to perform aggregation of data, which is not possible with pure SPARQL. In our current, implementation the naive rewriting generated leaves some room for improvement and we expect future XSPARQL engines to optimize such queries.

XSPARQL query:

prefix foaf: <http://xmlns.com/foaf/0.1/>
prefix dct:  <http://purl.org/dc/terms/>

let $results :=
  for $entry $date
  from <sample_distribution_data.nt>
  where {$entry dct:created $date}
  return <entry date="{$date}"/>
return
    let $days := for $day in data($results/@date)
             return day-from-dateTime(xs:dateTime($day))
    for $day in distinct-values($days)
    order by $day
    return <day d="{$day}">{count($results[day-from-dateTime(xs:dateTime(@date)) = $day])}</day>

Observe that assuming and optimal sorting algorithm for evaluating the order by clause (i.e., O(n.log(n) ), this query runs in O(n2.log(n)), where n is the number of dated entries in the input data.

Rewritten query:

import module namespace _xsparql = "http://xsparql.deri.org/XSPARQLer/xsparql.xquery"
at "http://xsparql.deri.org/XSPARQLer/xsparql.xquery";

declare namespace _sparql_result = "http://www.w3.org/2005/sparql-results#";

declare namespace foaf = "http://xmlns.com/foaf/0.1/";
declare namespace dct = "http://purl.org/dc/terms/";


declare variable $_NS1 := "prefix foaf: <http://xmlns.com/foaf/0.1/>";
declare variable $_NS2 := "prefix dct: <http://purl.org/dc/terms/>";

let $results :=
let $_aux1 := _xsparql:_serialize(("http://example.org/sparql?query=", fn:encode-for-uri( _xsparql:_serialize(( $_NS1, $_NS2, "
select $entry $date from <sample_distribution_data.nt> where { $entry dct:created $date . } ")))))
for $_aux_result1 at $_aux_result1_Pos in doc($_aux1)//_sparql_result:result
let $_entry_Node := ($_aux_result1/_sparql_result:binding[@name = "entry"])
let $entry := data($_entry_Node/*)
let $_entry_NodeType := name($_entry_Node/*)
let $_entry_NodeDatatype := string($_entry_Node/*/@datatype)
let $_entry_NodeLang := string($_entry_Node/*/@lang)
let $_entry_RDFTerm := _xsparql:_rdf_term($_entry_NodeType, $entry )
let $_date_Node := ($_aux_result1/_sparql_result:binding[@name = "date"])
let $date := data($_date_Node/*)
let $_date_NodeType := name($_date_Node/*)
let $_date_NodeDatatype := string($_date_Node/*/@datatype)
let $_date_NodeLang := string($_date_Node/*/@lang)
let $_date_RDFTerm := _xsparql:_rdf_term($_date_NodeType, $date )
return <entry date = "{$date}"/>
return
let $days := for $day at $_day_Pos in data($results/@date ) return day-from-dateTime(xs:dateTime($day ) )
for $day at $_day_Pos in distinct-values($days ) 
order by $day return <day d = "{$day}">{ count($results[day-from-dateTime(xs:dateTime(@date ) ) = $day ] ) }</day>

Expected output:

<day d="12">41</day>
<day d="13">22</day>
<day d="14">166</day>
<day d="15">252</day>

3.8 distribution.xsparql

This query is similar to the previous one, i.e. it performs the same task, except that a custom function is used to improve the complexity of the algorithm. This example shall show that there is obviously a lot of room for query optimizers for XSPARQL.

XSPARQL query:

prefix foaf: <http://xmlns.com/foaf/0.1/>
prefix dct:  <http://purl.org/dc/terms/>

declare function local:_distribution_count($s, $i, $c) {
  let $x :=
    if ($i > count($s)) then
      ()
    else if ($s[$i] eq $s[$i + 1]) then
      local:_distribution_count($s, $i + 1, $c + 1)
    else
      fn:concat( fn:concat($s[$i], ", ", $c) , "
", local:_distribution_count($s, $i + 1, 1) )
  return $x
};

let $days  := 
  for $entry $date
  from <sample_distribution_data.nt>
  where {$entry dct:created $date}
  let $day := day-from-dateTime(xs:dateTime($date))
  order by $day
  return $day

return local:_distribution_count($days, 1, 1)

Observe here that again the sorting in the first let takes O(n.log(n) steps for an optimal engine. The recursive custom function for counting is then called upon the messages sorted per day. Since this function boils down to a simple iteration over the sorted days, it runs in O(n) and the overall complexity thus stays within O(n.log(n) which is an improvement over the previous query. An intelligent query optimizer could possibly catch such cases.

Rewritten query:

import module namespace _xsparql = "http://xsparql.deri.org/XSPARQLer/xsparql.xquery"
at "http://xsparql.deri.org/XSPARQLer/xsparql.xquery";

declare namespace _sparql_result = "http://www.w3.org/2005/sparql-results#";

declare namespace foaf = "http://xmlns.com/foaf/0.1/";
declare namespace dct = "http://purl.org/dc/terms/";


declare function local:_distribution_count ( $s , $i , $c ) {
let $x := if ( $i > count($s ) ) then () else if ( $s[$i ] eq $s[$i+1 ] ) 
then local:_distribution_count($s , $i+1 , $c+1 ) else fn:concat(fn:concat($s[$i ] , ", " , $c ) , "
" , local:_distribution_count($s , $i+1 , 1 ) )
return $x } ;
declare variable $_NS1 := "prefix foaf: <http://xmlns.com/foaf/0.1/>";
declare variable $_NS2 := "prefix dct: <http://purl.org/dc/terms/>";

let $days :=
let $_aux1 := _xsparql:_serialize(("http://example.org/sparql?query=", fn:encode-for-uri( _xsparql:_serialize(( $_NS1, $_NS2, "
select $entry $date from <sample_distribution_data.nt> where { $entry dct:created $date . } ")))))
for $_aux_result1 at $_aux_result1_Pos in doc($_aux1)//_sparql_result:result
let $_entry_Node := ($_aux_result1/_sparql_result:binding[@name = "entry"])
let $entry := data($_entry_Node/*)
let $_entry_NodeType := name($_entry_Node/*)
let $_entry_NodeDatatype := string($_entry_Node/*/@datatype)
let $_entry_NodeLang := string($_entry_Node/*/@lang)
let $_entry_RDFTerm := _xsparql:_rdf_term($_entry_NodeType, $entry )
let $_date_Node := ($_aux_result1/_sparql_result:binding[@name = "date"])
let $date := data($_date_Node/*)
let $_date_NodeType := name($_date_Node/*)
let $_date_NodeDatatype := string($_date_Node/*/@datatype)
let $_date_NodeLang := string($_date_Node/*/@lang)
let $_date_RDFTerm := _xsparql:_rdf_term($_date_NodeType, $date )

let $day := day-from-dateTime(xs:dateTime($date ) )
order by $day return $day
return local:_distribution_count($days , 1 , 1 ) 

Expected output:

12, 41
13, 22
14, 166
15, 252

4. References

[SPARQLPROTOCOL]
Kendall Grant Clark, Lee Feigenbaum, and Elias Torres. SPARQL Protocol for RDF, November 2007. W3C Proposed Recommendation, available at http://www.w3.org/TR/2007/PR-rdf-sparql-protocol-20071112/.
[SPARQLRESULT]
Dave Beckett and Jeen Broekstra. SPARQL Query Results XML Format, November 2007. W3C Proposed Recommendation, available at http://www.w3.org/TR/2007/PR-rdf-sparql-XMLres-20071112/.
[TURTLE]
David Beckett and Tim Berners-Lee. Turtle - Terse RDF Triple Language, W3C Team Submission, 14 January 2008, Available at http://www.w3.org/TeamSubmission/turtle/.
[XQUERYSEMANTICS]
Denise Draper, Peter Fankhauser, Mary Fernández, Ashok Malhotra, Kristoffer Rose, Michael Rys, Jérôme Siméon, and Philip Wadler. XQuery 1.0 and XPath 2.0 Formal Semantics. W3c recommendation, W3C, January 2007. W3C Recommendation, available at http://www.w3.org/TR/xquery-semantics/.
[XSPARQLLANGUAGE]
XSPARQL Language Specification. Document included in the present specification.
[XSPARQLSEMANTICS]
XSPARQL: Semantics. Document included in the present specification.

XSPARQL: Use cases

Authors:
Alexandre Passant - DERI, NUI Galway
Jacek Kopecký - University of Innsbruck
Stéphane Corlosquet - DERI, NUI Galway
Diego Berrueta - Fundación CTIC
Davide Palmisano - Asemantics S.R.L.
Axel Polleres - DERI, NUI Galway

This work is supported by Science Foundation Ireland under grants number SFI/02/CE1/I131 and SFI/08/CE/I1380 and under the European Commission European FP6 project inContext (IST-034718).


Abstract

XSPARQL is a query language combining XQuery and SPARQL for transformations between RDF and XML. This document contains an overview of XSPARQL use cases within various scenarios.


Table of Contents


1. Introduction

This document introduces a set of use cases showing the potential of the XSPARQL language. Especially, it focuses on how XSPARQL can be used to run tasks that usually need a complex process pipeline, such as running a query using either XQuery or SPARQL (depending on the data source) and then extending the result data or rewriting it using a particular scripting language.

2. Use cases

2.1 KML mapping: people's geolocation on a map

This use case demonstrates the capacity of XSPARQL to output OGC KML (formerly Keyhole Markup Language) format XML data. KML is a XML-based format designed for geolocation purposes. Creating KML data using XSPARQL provides a straightforward way to build geolocation interfaces and mash-ups based on RDF data. This use case was originally motivated by the needs of the EU project inContext to locate a set of people on a map from their coordinates recorded in an RDF store.

The following query example creates a KML file regarding a set of foaf:Person instances from the FOAF ontology [FOAF] stored in a particular RDF file with their related location, using the WGS84 vocabulary [WGS84].


Figure 1: XSPARQL query: Creating KML Data from RDF

prefix foaf: <http://xmlns.com/foaf/0.1/>
prefix geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>

<kml xmlns="http://www.opengis.net/kml/2.2">
{
for $person $fn $ln $long $lat
from <sample_person_geodata.rdf>
where {
  $person a foaf:Person; foaf:firstName $fn;foaf:lastName $ln;
          foaf:based_near [ a geo:Point; geo:long $long; geo:lat $lat ] 
}
return <Placemark>
         <name>{fn:concat("Location of ", $fn, " ", $ln)}</name>
         <Point><coordinates>{fn:concat($long, ",", $lat, ",0")}</coordinates></Point>
       </Placemark>
}
</kml>

Ideally, combined with an XSPARQL engine providing a REST interface that supports HTTP GET, one can directly pass the URL encoding of this query to an application that handles a KML input in order to get a rendering of the map. For instance, Google maps supports KML as input format. The following URI shows the encoded query to an XSPARQL server running at http://www.example.org/xsparqlrest/, which extracts geo data from an RDF file at sample_person_geodata.rdf, used as input data for Google maps:

http://maps.google.com/maps?f=q&hl=en&geocode=&q=http:%2F%2www.example.org%2Fxsparqlrest%2F%3Fquery%3Dprefix%2Bfoaf%2B%253A%2B%253Chttp%253A%252F%252Fxmlns.com%252Ffoaf%252F0.1%252F%253E%250D%250Aprefix%2Brdf%2B%253A%2B%253Chttp%253A%252F%252Fwww.w3.org%252F1999%252F02%252F22-rdf-syntax-ns%2523%253E%250D%250Aprefix%2Bgeo%253A%2B%253Chttp%253A%252F%252Fwww.w3.org%252F2003%252F01%252Fgeo%252Fwgs84_pos%2523%253E%250D%250A%250D%250A%253Ckml%2Bxmlns%253D%2522http%253A%252F%252Fwww.opengis.net%252Fkml%252F2.2%2522%253E%250D%250A%7B%250D%250Afor%2B%2524person%2B%2524fn%2B%2524ln%2B%2524long%2B%2524lat%250D%250Afrom%2B%253Csample_person_geodata.rdf%253E%250D%250Awhere%2B%7B%250D%250A%2B%2B%2524person%2Brdf%253Atype%2Bfoaf%253APerson.%250D%250A%2B%2B%2524person%2Bfoaf%253Abased_near%2B%2524gps.%250D%250A%2B%2B%2524gps%2Brdf%253Atype%2Bgeo%253APoint.%250D%250A%2B%2B%2524gps%2Bgeo%253Along%2B%2524long.%250D%250A%2B%2B%2524gps%2Bgeo%253Alat%2B%2524lat.%250D%250A%2B%2B%2524person%2Bfoaf%253AfirstName%2B%2524fn.%250D%250A%2B%2B%2524person%2Bfoaf%253AlastName%2B%2524ln.%250D%250A%7D%250D%250Areturn%2B%253CPlacemark%253E%250D%250A%2B%2B%2B%2B%2B%2B%2B%2B%2B%253Cname%253E%7Bfn%253Aconcat(%2522Location%2Bof%2B%2522%252C%2B%2524fn%252C%2B%2522%2B%2522%252C%2B%2524ln)%7D%253C%252Fname%253E%250D%250A%2B%2B%2B%2B%2B%2B%2B%2B%2B%253CPoint%253E%253Ccoordinates%253E%7Bfn%253Aconcat(%2524long%252C%2B%2522%252C%2522%252C%2B%2524lat%252C%2B%2522%252C0%2522)%7D%253C%252Fcoordinates%253E%253C%252FPoint%253E%250D%250A%2B%2B%2B%2B%2B%2B%2B%2B%253C%252FPlacemark%253E%250D%250A%7D%250D%250A%253C%252Fkml%253E&ie=UTF8&ll=50.736455,9.140625&spn=13.252978,31.113281&t=h&z=5

This URI will not work within your browser, unless you replace http://www.example.org/xsparqlrest/ with the URI of an actual running XSPARQL server. See a working example. For details of an existing implementation, we refer to [XSPARQLIMPLEMENTATION].

2.2 Creating an RSS feed from distributed SIOC data

SIOC [SIOC] is an RDF vocabulary designed to describe activities of online communities such as blog posts, wiki pages, comments, etc, now used in more than 50 applications, from various Web 2.0 exporters to neuromedicine projects.

While SIOC provides a complete way to browse social data from a community, some tools may require a simple RSS feed. The following example shows how RSS data can be created from distributed SIOC data, by using rdfs:seeAlso links to fetch related documents from a given entry point, in that case a weblog index.


Figure 2: XSPARQL query: Translating distributed SIOC data to RSS

declare namespace foaf = "http://xmlns.com/foaf/0.1/";
declare namespace sioc = "http://rdfs.org/sioc/ns#";
declare namespace rdfs = "http://www.w3.org/2000/01/rdf-schema#";
declare namespace dcterms = "http://purl.org/dc/terms/";
declare namespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";

<rss version="2.0">
  <channel>
    <title>{"RSS feed for blog @ johnbreslin.org"}</title>
    <link>{"http://johnbreslin.com/blog"}</link>
    <description>{"RSS feed for example.org Weblog"}</description>
    <generator>{"XSPARQL - xsparqler 0.1"}</generator>
	{ for $blog $post $seeAlso from <http://www.johnbreslin.com/blog/index.php?sioc_type=site>
	  where { $blog sioc:container_of $post . 
	          $post rdf:type sioc:Post ; rdfs:seeAlso $seeAlso . 
	        }
	  return <item><link>{$post}{$seeAlso}</link>
	         { for $date from $seeAlso
	           where { $post dcterms:created $date. }
	           return <date>{ $date }</date>
	         }
	</item>
	}
	  </channel>
</rss>

In the previous query, both sioc:container_of and rdfs:seeAlso are used to retrieve the list of related SIOC Post from the weblog homepage. Yet, in a case where each blog post would be content-negotiated or annotated with RDFa, the sioc:container_of property will be enough to retrieve the related sioc:Post instances, thus reducing the complexity of the XSPARQL query.


Figure 3: SIOC to RSS


RDF data lifting and lowering for WS communication

2.3 Creating an RSS feed from graphs stored in a triple-store

In order to build Semantic Web applications, developers generally rely on a backend system, known as a triple store, to store their RDF data. Most of the available stores on the market supports the use of named graphs [NAMEDGRAPHS], so that each triple is contained in a particular graph, which has a given URI. Since this URI can be used as a subject for any RDF triple, this is an efficient way to deal with information about those graphs, as provenance, retrieving date, etc, in the store itself. Yet, there is no standard way at the moment to get an overview of the latest added / updated graph for a particular triple store. Creating an RSS feed from a store would be the straightforward option, but cannot be easily achieved.

While using SPARQL CONSTRUCT can be used to create a RSS 1.0 feed from graphs contained in a triple store, it does not offer the ability to control the RDF/XML serialization output. Hence, while the produced feed will be valid from a semantic point of view, it will not be readable by generic RSS aggregators, that unfortunately check the feed syntax only.

XSPARQL solves that problem by letting the user define its own RDF/XML serialization, as show by the following query that creates an RSS 2.0 feed from data contained in a triple store. Then, any produced feed can be read through an RSS reader, so that triple store maintainers follow the activity of upcoming graphs in the storage system.


Figure 4: XSPARQL query: Creating an RSS feed from graphs contained in a triple store

declare namespace dct = "http://purl.org/dc/terms/";
<rss version="2.0">
  <channel>
  <title>{"RDF Graphs @ example.org"}</title>
  <link>{"http://example.org/sparql"}</link>
  <description>{"RSS feed for RDF Graphs contained in example.org triple store"}</description>
  <generator>{"XSPARQL - xsparqler 0.1"}</generator>
  { for $graph $date
    where { graph $graph { } . $graph dct:created $date }
    return <item><link>{$graph}</link><date>{$date}</date></item>
  }
  </channel>
</rss>

Figure 5: Named graphs to RSS


RDF data lifting and lowering for WS communication

2.4 XHTML and XML document to RDF data

While some Web applications expose RDF data natively, others use different ways to structure data contained in webpages. For instance, some providers use microformats, while other simply use XHTML templates to define a common structure for different pages of the same website.

The next example provides a way to define a set of personal relationships using the foaf:knows property from the FOAF vocabulary, thanks to a single XSPARQL query applied to an XHTML document from a microblogging platform. This use case also shows how XSPARQL could be used to provide GRDDL [GRDDL] transformations.


Figure 6: XSPARQL query: XHTML data to RDF (FOAF)

declare namespace foaf="http://xmlns.com/foaf/0.1/";
declare default element namespace "http://www.w3.org/1999/xhtml";

let $doc := doc("http://cgi.w3.org/cgi-bin/tidy?docAddr=http%3A%2F%2Ftwitter.com%2Fterraces")
let $friends:= $doc//*[@id="friends"]/span[@class="vcard"]
for $f in $friends
let $friendsAccount := $f/*/@href
let $hp := "http://apassant.net/alex"

construct { <{$hp}> foaf:knows [ foaf:userAccount <{$friendsAccount}> ] }

Figure 7: (X)HTML to FOAF


XHTML data to RDF using XSPARQL

While the previous example focus on creating RDF representation of a social network from an XHTML document, a similar approach can be used to convert XML data. The example query that follows then provides RDF data (using the FOAF vocabulary and some OpenSocial RDF mappings) from an XML file described using the Portable Contact schema.


Figure 8: XSPARQL query: XML data to RDF (FOAF and OpenSocial RDF mappings)

declare namespace foaf="http://xmlns.com/foaf/0.1/";
declare namespace opensocial="http://danbri.org/2008/opensocial/_latest";
declare namespace dc="http://purl.org/dc/elements/1.1/";
let $doc := doc("http://asemantics.dyndns.org/downloads/dp_contacts.xml")
let $entries := $doc//entry
return
  for $entry in $entries
    let $id := $entry/id
    let $givenName := $entry/name/givenName
    let $familyName := $entry/name/familyName
    let $gender := $entry/gender
    let $nickname := $entry/nickname
    let $birthday := $entry/birthday
    let $drinker := $entry/drinker
    let $tags := $entry/tags
    let $relationshipStatus := $entry/relationshipStatus
    let $emails := $entry/emails
    let $phones := $entry/phoneNumbers
    let $ims := $entry/ims
    let $accounts := $entry/accounts
    let $organizations := $entry/organizations

    construct 
    { _:b{data($id)} a foaf:Person; 
      foaf:firstname {data($givenName)};
      foaf:surname {data($familyName)};
      foaf:gender {data($gender)};
      foaf:nick {data($nickname)};
      foaf:birthday {data($birthday)};
      opensocial:drinker {data($drinker)}; 
      opensocial:relationshipStatus {data($relationshipStatus)};
      opensocial:tags {data($tags)}.
      {
        for $email in $emails
        let $address := $email/value
        construct 
        { _:b{data($id)} foaf:mbox {data($address)};
        }
      }.
      {
        for $phoneNumber in $phones
        let $phone := $phoneNumber/value
        construct 
        { _:b{data($id)} foaf:phone {data($phone)};
        }
      }.
      {
        let $counter := 0
        for $im in $ims
        let $imAccount := $im/value
        let $imType := $im/type
        let $counter := $counter + 1
        construct
        { _:im{data($counter)} a foaf:OnlineChatAccount;
            foaf:accountName {fn:concat(data($imType),":",data($imAccount))}.
          _:b{data($id)} foaf:holdsAccount _:im{data($counter)};
        }
      }.
      {
        let $counter := 0
        for $account in $accounts
        let $accountDomain := $account/domain
        let $accountUserId := $account/userid
        let $counter := $counter + 1
        construct
        { _:acc{data($counter)} a foaf:OnlineAccount;
            foaf:accountName {data($accountUserId)};
            foaf:accountServiceHomepage {data($accountDomain)}.
          _:b{data($id)} foaf:holdsAccount _:acc{data($counter)};
	    }
	  }.
	  {
	    let $counter := 0
	    for $organization in $organizations
	    let $orgName := $organization/name
	    let $counter := $counter + 1
	    construct
	    { _:org{data($counter)} a foaf:Organization;
	      dc:title {data($orgName)}.
	    }
      }.
   }

2.5 Generating ontology specification

Understanding and using an ontology is easier if comprehensive reference documentation is available. Almost every popular vocabulary is extensively documented, otherwise, its value as element of shared understanding would be compromised.

For large ontologies, keeping the ontology and its documentation in sync is simpler if the former is generated by an automated process that exploits the logical structure of the ontology and the content of the annotation properties. There are some tools for this task. Some of them are plugins for the Protégé ontology editor, such as OWLDoc and ProtegeDocgen, and thus, closely tied to the use of that editor. Another tool in use by some popular ontologies is SpecGen, which is a Python script that uses an ontology API to programmatically create a HTML report.

XSPARQL offers new opportunities to produce the reference documentation of a self-documented ontology, i.e., an ontology that contains per-term documentation as annotation properties. With XSPARQL, no scripting language is required, and the report can be obtained independently of the application used to edit the ontology. Moreover, the XSPARQL-based solution is highly flexible and can be easily modified to produce different kinds of reports.

The example query that follows produces a very simple HTML report with a listing of all the classes and properties of an OWL ontology. The annotation properties provide the name and description of each term, and the name is also used to sort the terms. The structure of the ontology is queried to show the names of the super-classes for each class.


Figure 9: XSPARQL query: HTML documentation from ontology

declare namespace foaf = "http://xmlns.com/foaf/0.1/";
declare namespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
declare namespace rdfs = "http://www.w3.org/2000/01/rdf-schema#";
declare namespace owl = "http://www.w3.org/2002/07/owl#";
declare namespace xhtml = "http://www.w3.org/1999/xhtml";
declare default element namespace "http://www.w3.org/1999/xhtml";
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml">
<head>
<title>{"Reference documentation"}</title>
</head>
<body>
<h1>{"Reference documentation"}</h1>

<h2>{"Classes"}</h2>
{ for $Entity $Label $Desc
  from <http://rdfs.org/sioc/ns#>
  where { $Entity rdf:type owl:Class .
          $Entity rdfs:label $Label .
	      $Entity rdfs:comment $Desc }
  order by $Label
  return
    <xhtml:div>
    <h3>{"Class: "} {$Label} {" ("} {$Entity} {")"}</h3>
    <p><strong>{"Description: "}</strong> {$Desc}</p>
    <p><strong>{"Superclasses: "}</strong>
    { for $Super $SuperLabel
      from <http://rdfs.org/sioc/ns#>
      where { $Entity rdfs:subClassOf $Super .
              $Super rdfs:label $SuperLabel }
      order by $SuperLabel
      return <span>{$SuperLabel} {", "}</span>
    }
    </p>
    </xhtml:div>
}

<h2>{"Properties"}</h2>
{ for $Entity2 $Label2 $Desc2
  from <http://rdfs.org/sioc/ns#>
  where { $Entity2 rdfs:label $Label2 .
          $Entity2 rdfs:comment $Desc2 .
          { { $Entity2 rdf:type owl:DatatypeProperty }
            union
            { $Entity2 rdf:type owl:ObjectProperty } }
          }
  order by $Label2
  return
    <xhtml:div>
    <h3>{"Property: "} {$Label2} {" ("} {$Entity2} {")"}</h3>
    <p><strong>{"Description: "}</strong> {$Desc2}</p>
    </xhtml:div>
  }

</body>
</html>

2.6 Creating an SVG chart from RDF data

SPARQL is amazingly powerful when it comes to collecting, aggregating and querying data from the (semantic) web. However, it cannot produce nice-looking reports to present the results. A solution to this limitation is to use a XSLT stylesheet on the outcome of the SPARQL query.

Fortunately, XSPARQL provides a more elegant and powerful solution in just one step, removing the need for an intermediate XML serialization of the query results. Some of the most popular formats for documents and images, such as XHTML and SVG respectively, are based on XML, and therefore, can be produced by a XSPARQL query.

In Figure 11, we show a dispersion (X-Y) graph that depicts the source of the energy produced by rich countries. The X axis represents the percentage of the electricity produced at nuclear power plants. The Y axis does the same for electricity produced by burning fossil fuels. Thus, countries that rely on greener "alternative" energies are close to the origin of coordinates (bottom-left corner). Countries near the top of the diagram are large carbon dioxide producers due to their dependency on fossil fuels.

In order to produce this graph with XSPARQL, the data provided by the CIA Factbook is used. The query in Figure 10 can be used with the public SPARQL endpoint of the Factbook RDF dataset to get a list of the richer countries of the world (countries with a Gross Domestic Product per capita larger than $25,000), and the information about their electrical power sources. The "return" part of the query plots each country in the diagram using simple SVG drawing primitives.


Figure 10: XSPARQL query: RDF data to SVG

declare namespace factbook = "http://www4.wiwiss.fu-berlin.de/factbook/ns#";
declare namespace rdfs = "http://www.w3.org/2000/01/rdf-schema#";
declare namespace svg = "http://www.w3.org/2000/svg";
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" width="560px" height="560px">
  <g transform="translate(30,30)">
    <rect width="500" height="500" x="0" y="0" style="fill:none;stroke-width:1px;stroke:#000000" />
{ ordered {
  for $Name $RatioNuc $RatioFos
  where {
    $Country a factbook:Country .
    $Country rdfs:label $Name .
    $Country factbook:electricity_productionbysource_nuclear $RatioNuc .
    $Country factbook:electricity_productionbysource_fossilfuel $RatioFos .
    $Country factbook:GDP_percapita_PPP $GDP .
    filter ($GDP > 25000)
  }
  return
    <g>
      <path d="M {$RatioNuc * 5 - 3} {500 - $RatioFos * 5 + 3} L {$RatioNuc * 5 + 3} {500 - $RatioFos * 5 - 3}"
            style="fill:none;stroke-width:1px;stroke:#000000"/>
      <path d="M {$RatioNuc * 5 - 3} {500 - $RatioFos * 5 - 3} L {$RatioNuc * 5 + 3} {500 - $RatioFos * 5 + 3}"
            style="fill:none;stroke-width:1px;stroke:#000000"/>
      <svg:text x="{$RatioNuc * 5 + 4}" y="{500 - $RatioFos * 5 + 4}"
            style="font-size:12px;fill:#3030F0">{$Name}</svg:text>
    </g>
} }
    <svg:text x="200" y="{500 + 12}" style="font-size:12px;fill:#F03030">{"% Nuclear"}</svg:text>
    <svg:text x="-250" y="-6" transform="matrix(0,-1,1,0,0,0)" style="font-size:12px;fill:#F03030">{"% Fossil"}</svg:text>
  </g>
</svg>

Figure 11: SVG chart from RDF data using XSPARQL



SVG chart from RDF data using XSPARQL

Browse this example in SVG (only with an SVG-compliant browser).

2.7 Semantic Web Services Lifting and Lowering

In Semantic Web Service applications, such as those employing SAWSDL (Semantic Annotations for WSDL and XML Schema), there is a strong need for translations in both directions between XML to RDF.

Web services are software components remotely accessible using XML and Web protocols. The stack of Web services specifications is built on SOAP and WSDL. SOAP is an XML-based protocol, and WSDL is an XML language for capturing the machine-readable descriptions of the interfaces and communication specifics of Web services.

On top of WSDL, SAWSDL is the first standardized specification for semantic description of Web services. Semantic Web Services (SWS) technologies aim to automate tasks involved in the use of Web services, such as service discovery, composition and invocation. A SWS client works on the semantic level, with its data represented in RDF. In contrast, Web services usually expect (and emit) messages in XML. In order to enable the semantic client to communicate with actual Web services, its semantic data must be lowered into the expected input messages, and the data coming from the service in its output messages must be lifted back up to the semantic level, as illustrated in next figure.


Figure 12: RDF data lifting and lowering for Web service communication


RDF data lifting and lowering for WS communication

SAWSDL provides a fine-grained mechanism for "semantic adornments" of XML Schemas. In WSDL, schemas are used to describe the input and output messages of Web service operations, and SAWSDL can annotate messages or parts of them with pointers to relevant semantic concepts plus links to lifting and lowering transformations. These links are captured using the sawsdl:liftingSchemaMapping and sawsdl:loweringSchemaMapping attributes which reference the transformations from within the schema components (xs:element, xs:attribute, etc.) that describe the respective message parts.

SAWSDL's schema annotations for lifting and lowering are not only useful for communication with web services from an RDF-based client, but for service data mediation in general. For example, given two services S1 and S2, if the output of S1 uses a different message format than S2 expects as input, but the meaning of the data is compatible, then the data can be transformed automatically if services S1 and S2 provide the respective lifting and lowering schema mappings as shown in the following figure. The transformations would presumably map from/to the same ontology, or, alternatively, to two different ontologies that can be aligned via ontology mediation techniques.


Figure 13: RDF data lifting and lowering for data mediation


RDF data lifting and lowering for data mediation

To illustrate XSPARQL lifting and lowering transformations, we use an example travel (train ticket) reservation service with its message schemas shown in Figure 14 and the respective data ontology shown in Figure 15. We need a lowering transformation for the booking request element so that a semantic client can transform the data of the user goal (booking a train trip) into the appropriate XML/SOAP message. Further, we need a lifting transformation for the reservation response element.

For the purpose of SWS data lowering, we define <input.rdf> to be a special graph that contains the input RDF data that the semantic client intends to send to the service. When a semantic client performs lowering, it calls the XSPARQL engine and makes sure that the relevant data is available as <input.rdf>. This special graph should also be available when doing lifting on the response message that comes from the service, so that the lifting transformation can correlate the incoming data with the appropriate knowledge of the client. Both the lifting and the lowering transformations described below use the <input.rdf> graph.

The lowering transformation, shown in Figure 16, takes parts of the request data (starting after the function declaration in the lower half) and mostly puts it into the resulting XML structure in a straightforward way. For the start and destination locations, which may be a train station or a city, the function locationLowering puts the respective place names in the corresponding XML structure.

The lifting transformation, shown in Figure 17, is even simpler, and it uses the input data together with the incoming message and constructs the reservation graph in RDF.


Figure 14: XML Schema for the example service messages
<xs:schema targetNamespace="http://example.com/bookTicket.xsd"              
      xmlns="http://example.com/bookTicket.xsd"                                
      xmlns:sawsdl="http://www.w3.org/ns/sawsdl#"                              
      xmlns:xs="http://www.w3.org/2001/XMLSchema" >                         
  <xs:element name="BookingRequest"                                         
      sawsdl:modelReference="http://example.org/bookTicket#ReservationRequest" 
      sawsdl:loweringSchemaMapping="http://example.org/BookingRequestLowering.xsp" >
    <xs:complexType>                                                     
       <xs:all>                                                          
         <xs:element name="start" type="location"/>                      
         <xs:element name="destination" type="location"/>                
         <xs:element name="dateTime" type="xs:dateTime"/>                
         <xs:element name="passengerCount" type="xs:short"/>             
       </xs:all>                                                         
    </xs:complexType>                                                    
  </xs:element>                                                          
                                                                               
  <xs:complexType name="location">                                       
    <xs:all>                                                             
       <xs:element name="country" type="xs:string"/>                     
       <xs:element name="city" type="xs:string"/>                        
       <xs:element name="station" type="xs:string" minOccurs="0"/>       
    </xs:all>                                                            
  </xs:complexType>                                                      
                                                                               
  <xs:element name="Reservation"                                            
      sawsdl:modelReference="http://example.org/bookTicket#Reservation"        
      sawsdl:liftingSchemaMapping="http://example.org/ReservationLifting.xsp" >
    <xs:all>                                                             
       <xs:element name="confirmationID" type="xs:string"/>              
       <xs:element name="description" type="xs:string"/>                 
    </xs:all>                                                            
  </xs:element>                                                          
</xs:schema>                                                             

Figure 15: Ontology for the example service message data
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .                        
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .      
@prefix xs: <http://www.w3.org/2001/XMLSchema#> .            
@prefix bkrdf: <http://example.org/bookTicket#> .            
                                                                   
bkrdf:Reservation a rdfs:Class .                                   
bkrdf:ReservationRequest a rdfs:Class .                            
                                                                   
bkrdf:time a rdf:Property .                                        
  # domain: either a reservation request or reservation            
  # range:  xs:dateTime                                             
bkrdf:from a rdf:Property .                                        
  # domain: either a reservation request or reservation            
  # range:  either city or a train station                          
bkrdf:to a rdf:Property .                                          
  # domain: either a reservation request or reservation            
  # range:  either city or a train station                          
bkrdf:passengerCount a rdf:Property .                              
  # domain: either a reservation request or reservation            
  # range:  xs:short                                                
bkrdf:requestedBy a rdf:Property ;
  rdfs:domain bkrdf:Reservation ;                                 
  rdfs:range  bkrdf:ReservationRequest .                                          
                                                                   
bkrdf:description a rdf:Property ;                                 
  rdfs:subPropertyOf rdfs:comment .                                
bkrdf:confirmationID a rdf:Property .                              
  # range:  xs:string                                               
                                                                   
bkrdf:TrainStation a rdfs:Class .                                  
bkrdf:isInCity a rdf:Property ;                                    
  rdfs:domain bkrdf:TrainStation ;                                 
  rdfs:range  bkrdf:City .                                          
bkrdf:City a rdfs:Class .                                          
bkrdf:isInCountry a rdf:Property ;                                 
  rdfs:domain bkrdf:City ;                                         
  rdfs:range  bkrdf:Country .                                       
bkrdf:Country a rdfs:Class .                                       
bkrdf:name a rdf:Property .                                        
  # domain: train station, city, country                           
  # range:  xs:string                                               

Figure 16: XSPARQL example: lowering transformation
declare namespace bkrdf="http://example.org/bookTicket#";                    
declare namespace bkxml="http://example.com/bookTicket.xsd";                 
declare namespace tool="http://example.com/tools";
                                                                             
declare function tool:locationLowering ($node, $name) {                           
  for $city $country $station from <input.rdf>                         
     where { optional { $node a bkrdf:TrainStation ;                         
                               bkrdf:name $station ;                               
                               bkrdf:isInCity $cityNode .                          
                           $cityNode bkrdf:name $city ;                            
                               bkrdf:isInCountry $countryNode .                    
                           $countryNode bkrdf:name $country . }                    
               optional { $node a bkrdf:City ;                               
                               bkrdf:name $city ;                                  
                               bkrdf:isInCountry $countryNode .                    
                           $countryNode bkrdf:name $country . } }                  
     return                                                                  
       element { $name } {
         <bkrdf:location>                                                    
         <bkxml:country>{$country}</bkxml:country>             
         <bkxml:city>{$city}</bkxml:city>                        
         { if ($station) then <bkxml:station>{$station}</bkxml:station> else () }   
        </bkrdf:location>
      }                                                                      
};
                                                                             
for $date $count $from $to from <input.rdf>                            
where { $req a bkrdf:ReservationRequest ;                                      
           bkrdf:time $date ;                                             
           bkrdf:from $from ;                                             
           bkrdf:to $to ;                                                 
           bkrdf:passengerCount $count . }                                
return                                                                       
  <bkxml:BookingRequest>                                               
     <bkxml:dateTime>{$date}</bkxml:dateTime>                    
     <bkxml:passengerCount>{$count}</bkxml:passengerCount>       
     { tool:locationLowering($from, bkxml:start) }                                
     { tool:locationLowering($to, bkxml:destination) }                            
  </bkxml:BookingRequest>

Figure 17: XSPARQL example: lifting transformation
declare namespace bkrdf="http://example.org/bookTicket#";     
declare namespace bkxml="http://example.com/bookTicket.xsd";  
                                                              
let $reservation := /bkxml:Reservation                        
for $req $date $count $from $to from <input.rdf>             
where { $req a bkrdf:ReservationRequest ;                                           
             bkrdf:time $date ;                                                    
             bkrdf:from $from ;                                                    
             bkrdf:to $to ;                                                        
             bkrdf:passengerCount $count . }                                       
construct {                                                   
    _:a a bkrdf:Reservation ;                                  
        bkrdf:requestedBy $req ;
        bkrdf:time $date ;                                     
        bkrdf:from $from ;                                     
        bkrdf:to $to;                                          
        bkrdf:passengerCount $count ;                      
        bkrdf:description { $reservation/description } ;       
        bkrdf:confirmationID { $reservation/confirmationID } . 
}                                                             

3. References

[FOAF]
The Friend of a Friend (FOAF) Project. See http://www.foaf-project.org/.
[GRDDL]
Gleaning Resource Descriptions from Dialects of Languages (GRDDL). W3C Recommendation 11 September 2007. See http://www.w3.org/TR/grddl/.
[NAMEDGRAPHS]
Named Graphs, Semantic Web Interest Group. See http://www.w3.org/2004/03/trix/.
[SIOC]
Semantically-Interlinked Online Communities - W3C Member Submission 12 June 2007. See http://www.w3.org/Submission/2007/02/.
[XSPARQLIMPLEMENTATION]
XSPARQL: Implementation and Test-cases. Document included in the present specification.
[WGS84]
The Basic Geo (WGS84 lat/long) Vocabulary. See http://www.w3.org/2003/01/geo/.