XQuery/XML 到 RDF
外观
< XQuery
对于 Emp-DEPT 案例研究,必须从底层 XML 文件生成 RDF。XQuery 脚本生成 RDF。它使用配置文件来定义如何将表的列映射到 RDF 以及要使用的命名空间。这种映射需要更多工作才能允许复合键并允许用户定义的转换。用于创建此映射的交互式工具将很有用。
在网络上发布链接数据的首要指南是 如何在网络上发布链接数据。这项与维基教科书条目相关的工作在于逐步应用其中阐述的原则。
这种转换说明了本地数据集(无论是 SQL 还是 XML)与旨在融入全球数据库的数据集之间的一些差异。一些决定仍然不清楚。
- 表隐式地处于组织上下文之中。此上下文必须通过为本地属性和标识符创建命名空间来添加到 RDF 中
- 查询的范围隐式地处于组织边界内,但在 RDF 中,此范围需要明确。在 SQL 查询中 select * from emp; emp 模糊地既是员工类,也是公司中的员工集。在 RDF 中,这需要明确,因此需要添加两种类型的元组
- 元组将员工类型与公司的员工定义联系起来
- 元组将员工与公司联系起来(待添加)
- 与全球数据库的链接需要两种类型的链接
- 本地属性需要映射到全局谓词。这里,员工姓名映射到 foaf:surname(但案例可能需要更改)。或者,可以定义本地谓词 f:name,它与 foaf 谓词使用 owl:samePropertyAs 等效。
- 资源的本地标识符需要替换为全局 URI。这里,位置映射到 dbpedia 资源 URI。或者,本地 URI f:location/Dallas 可以与 dbPedia 资源使用 owl:sameAs 等效。(在哪里?为什么延迟这一点?)
- 外键被替换为完整的 URI,直接指向链接的资源。此属性的名称不再是外键的名称(例如 MgrNo,而是相关资源的名称(Manager)。但是,外键本身也可能需要替换。
- 主键也被替换为 URI,但本地主键值(例如员工编号)将需要作为文字保留,如果它不是纯粹的代理键。这可能应该映射到 rdf:label。
- 数据类型在数据中最好是显式的,以避免在查询中进行转换,尽管这会增加 RDF 图的大小。
- 命名空间已在 RDF 属性值中出现时完全展开。另一种方法是在 DTD 前言中定义实体作为这些命名空间的简写,但并非所有 RDF 处理器都会进行展开。xml:base 可用于默认一个命名空间。
[这里做出的选择是初学者的选择,欢迎评论。]
一些尚未解决的问题
- 关于整个数据集的元数据 - 它的来源、转换时间和方式 - 这些可以是文档的 DC 属性,每个实体都作为文档的一部分与其绑定?
- 另一种映射方法是从本体开始,向其添加映射信息,而不是从临时配置文件中生成映射信息。
为了便于从 XML 转换为 RDF,定义了一个单独的配置文件。这是 emp-dept 数据的配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<XML-to-RDF>
<namespaces>
<namespace prefix="f" uri="http://www.cems.uwe.ac.uk/empdept/concept/" />
<namespace prefix="ft" uri="http://www.cems.uwe.ac.uk/empdept/"/>
<namespace prefix="rdf" uri="http://www.w3.org/1999/02/22-rdf-syntax-ns#" />
<namespace prefix="rdfs" uri="http://www.w3.org/2000/01/rdf-schema#" />
<namespace prefix="foaf" uri="http://xmlns.com/foaf/0.1/" />
<namespace prefix="xs" uri="http://www.w3.org/2001/XMLSchema#" />
</namespaces>
<map type="emp" prefix="f">
<syntaxhighlight file="/db/Wiki/empdept/emp.xml" path="//Emp"/>
<col name="EmpNo" pk="true" uribase="ft:emp" type="xs:string"/>
<col name="Ename" prefix="rdfs" tag="label"/>
<col name="Sal" type="xs:integer"/>
<col name="Comm" type="xs:integer"/>
<col name="HireDate" type="xs:date"/>
<col name="MgrNo" tag="Mgr" uribase="ft:emp"/>
<col name="MgrNo"/>
<col name="DeptNo" tag="Dept" uribase="ft:dept"/>
<col name="Ename" prefix="foaf" tag="surname"/>
<col name="Job"/>
</map>
<map type="dept" prefix="f">
<syntaxhighlight file="/db/Wiki/empdept/dept.xml" path="//Dept"/>
<col name="Dname" prefix="rdfs" tag="label"/>
<col name="Dname"/>
<col name="Location" uribase="http://dbpedia.org/resource"/>
<col name="DeptNo" pk="true" uribase="ft:dept" type="xs:string"/>
</map>
<map type="salgrade" prefix="f">
<syntaxhighlight file="/db/Wiki/empdept/salgrade.xml" path="//SalGrade"/>
<col name="HiSal" type="xs:integer"/>
<col name="LoSal" type="xs:integer"/>
<col name="Grade" pk="true" uribase="ft:grade" type="xs:integer"/>
<col name="Grade" prefix="rdfs" tag="label"/>
</map>
</XML-to-RDF>
一个函数 row-to-rdf 生成表的行的 RDF,另一个函数 map-to-schema 生成表中使用的谓词的 RDFS 描述。
module namespace fr= "http://www.cems.uwe.ac.uk/wiki/fr";
import module namespace util = "http://exist-db.org/xquery/util";
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 function fr:declare-namespaces($config) {
for $ns in $config//namespace[@declare="yes"]
return util:declare-namespace($ns/@prefix,xs:anyURI($ns/@uri))
};
declare function fr:expand($qname as xs:string?, $map ) as xs:string ?{
let $namespace := $map/..//namespace
return
if ($qname)
then if (contains($qname,":"))
then let $qs := tokenize($qname,":")
let $prefix := $qs[1]
let $name := $qs[2]
let $uri := $namespace[@prefix=$prefix]/@uri
return concat($uri,$name)
else if ($namespace[@prefix = $qname])
then
$namespace[@prefix = $qname]/@uri
else
$qname
else ()
};
declare function fr:row-to-rdf($row as element() , $map as element() ) as element(rdf:Description) * {
let $pk := $map/col[@pk="true"]
let $pkv := string($row/*[name()=$pk/@name])
let $pkuri := fr:expand($pk/@uribase, $map)
return
<rdf:Description>
{attribute rdf:about {concat($pkuri,"/",$pkv)}}
{ if ($map/@type)
then
let $typeuri := fr:expand(concat($map/@prefix,":",$map/@type),$map)
return <rdf:type rdf:resource="{$typeuri}"/>
else ()
}
{for $col in $map/col
let $name := $col/@name
let $data := string($row/*[name(.)=$name])
return
if ($data !="")
then
element { concat(($col/@prefix,$map/@prefix)[1], ":", ($col/@tag,$name)[1])}
{
if ($col/@type)
then (attribute rdf:datatype
{ fr:expand($col/@type,$map)} ,
$data)
else if ( $col/@uribase )
then attribute rdf:resource
{concat(fr:expand($col/@uribase,$map), "/",replace($data," ","_"))}
else $data
}
else ()
}
</rdf:Description>
};
declare function fr:map-to-schema ($map as element()) as element(rdf:Description) * {
let $typeuri := fr:expand(concat($map/@prefix,":",$map/@type),$map)
for $col in $map/col[@type]
let $prop := concat( fr:expand(($col/@prefix,$map/@prefix)[1],$map ), ($col/@tag,$col/@name)[1])
let $rangeuri := ( fr:expand($col/@type,$map), fr:expand($col/@uribase,$map),"http://www.w3.org/2000/01/rdf-schema#literal")[1]
return
<rdf:Description rdf:about="{$prop}">
<rdf:type rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
<rdfs:domain rdf:resource="{$typeuri}"/>
<rdfs:range rdf:resource="{$rangeuri}"/>
<rdf:label>{string($col/@name)}</rdf:label>
</rdf:Description>
};
用于生成完整数据库的 RDF 的脚本
import module namespace fr="http://www.cems.uwe.ac.uk/wiki/fr" at "fr.xqm";
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 variable $config := doc(request:get-parameter("config",()));
declare variable $x := fr:declare-namespaces($config);
<rdf:RDF>
{
for $map in $config//map
let $xml := doc($map/source/@file)
let $source := util:eval(concat("$xml",$map/source/@path))
return
(for $row in $source return fr:row-to-rdf($row,$map),
fr:map-to-schema($map)
)
}
</rdf:RDF>
此外,每个资源都作为 RDF 检索。在此简单示例中,对像这样的资源 URI 的请求
http://www.cems.uwe.ac.uk/empdept/emp/7839
由 Apache 重写为
http://www.cems.uwe.ac.uk/xmlwiki/RDF/empdeptrdf.xq?emp=7839
并且脚本直接从 RDF 文件中检索所选资源的 RDF:Description。
这种机制不符合区分信息资源(如关于员工 7839 的信息)和所表示的现实世界实体的推荐做法。目前,资源 URI 直接取消引用到 RDF,而不是使用推荐的 303 机制进行间接取消引用。
declare namespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
declare variable $rdf := doc("/db/Wiki/RDF/empdept.rdf");
declare option exist:serialize "media-type=application/rdf+xml";
(: better to just parse the uri itself :)
let $param := request:get-parameter-names()
let $type := $param[1]
return
if ($type="all")
then
$rdf
else
let $key := request:get-parameter($type,())
let $resourceuri := concat("http://www.cems.uwe.ac.uk/empdept/",$type,"/",$key)
return
<rdf:RDF>
{$rdf//rdf:Description[@rdf:about=$resourceuri]}
</rdf:RDF>
- 复合主键
- 转换函数,例如将字符串的大小写转换、重新格式化日期
- 添加的资源和关系 - 这里有公司实体以及从部门到公司的链接