跳转到内容

XQuery/XML 到 RDF

来自维基教科书,开放世界中的开放书籍

对于 Emp-DEPT 案例研究,必须从底层 XML 文件生成 RDF。XQuery 脚本生成 RDF。它使用配置文件来定义如何将表的列映射到 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

[编辑 | 编辑源代码]

此外,每个资源都作为 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>

待办事项

[编辑 | 编辑源代码]
  • 复合主键
  • 转换函数,例如将字符串的大小写转换、重新格式化日期
  • 添加的资源和关系 - 这里有公司实体以及从部门到公司的链接
华夏公益教科书