65.9K
CodeProject 正在变化。 阅读更多。
Home

SimpleRDFElement 类使处理 RDF XML 更容易

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (2投票s)

2011 年 7 月 6 日

GPL3

8分钟阅读

viewsIcon

19224

downloadIcon

339

PHP 内置的 SimpleXML 对象对命名空间或 RDF 文档处理不佳。此扩展类提供了帮助。

引言

资源描述框架 (RDF) 是一种以易于序列化为 XML 的方式表示知识“三元组”(主语-谓语-宾语格式的陈述)的方法。RDF 中的不同“术语”由人们在线提供的不同词汇表在 RDF Schema 文档中定义,该系统专门设计用于让人们相互借鉴词汇表定义:您可以在 RDF XML 文档中将不同的词汇表指定为命名空间,并使用它们定义的术语。因此,典型的 RDF 文档包含大量命名空间中的元素。由于 RDF 信息在 XML 中的表示方式,通常每个单独的元素标签都由命名空间前缀限定,而每个父元素都拥有来自多个命名空间的子元素。

在 PHP 中使用其 SimpleXML 模块解析 RDF XML 时,这会带来问题。它提供了一个 SimpleXMLElement 类,该类易于使用且有趣,只要您不处理命名空间即可。在命名空间处理非常简单的情况下,使用它可以说是足够了:例如,当一个元素的所有子元素都属于同一个命名空间时。但没有简单的方法可以获取特定元素的命名空间前缀或部分,并且很难处理包含来自多个命名空间的子元素的元素。这使得本应非常简单的代码——将 RDF XML 转换为 XML 所表示的“三元组”(主语、谓语、宾语)的表示——变得极其复杂。

因此,我提出了 SimpleRDFElement 类:一个扩展了 PHP 内置 SimpleXMLElement 类的类,并包含一些额外的​​方法,旨在使其在处理 RDF XML 时更易于使用。

(注意:本文的其余部分假设您熟悉 RDF、XML 和 PHP 的基础知识,并且知道“三元组”、“命名空间”和“对象方法”等术语的含义及其表示方式。)

背景

作为我正在进行的一个项目的一部分,我需要能够将 RDF/XML 文本字符串转换为表示 XML 中每个标签(或“节点”)的对象,然后确定这些 XML 元素及其子树代表哪些 RDF 三元组。我想利用 PHP 中 SimpleXML 模块的内置功能,但当我尝试时,我遇到了许多问题。以下是我尝试使用 SimpleXMLElement 类来表示 RDF/XML 时遇到的一些问题的简要列表。

  • 由于根元素的所有子元素都由命名空间前缀限定,因此无法使用 -> 运算符将它们作为对象属性访问。
  • 由于子节点数组的创建方式,使用 print_r() 等方法也无法查看限定元素。
  • 由于 children() 方法在不带参数调用时,只返回未限定(即没有命名空间前缀)的元素,因此它返回零,无法迭代。
  • 结果,对象似乎完全为空;即使您尝试将其用作布尔值(例如,在赋值后添加“or die()”子句),它也会被评估为“false”。
  • 当您命名空间参数调用 children() 方法时,它只会检索具有该命名空间前缀的子元素(及其子元素)。
  • 因此,如果您期望元素的子元素来自多个命名空间中的任何一个,您必须迭代所有命名空间。

(如果您好奇,我在这里有一篇关于我一些失败尝试和遇到的问题的详细博文:http://talkingowlproject.blogspot.com/2011/06/simplexml-and-namespace-quirks.html。)

经过大量的 Google 搜索来解决这个问题,我没有找到任何适合我需求的东西。要么我可以下载需要安装十几个甚至更多 PHP 类文件的广泛的 RDF“框架”(……但我只想将 RDF 字符串解析为三元组!我不需要那些!),要么我可以遵循一些“黑客”的建议,这些建议实际上是行不通的。(例如,有人建议我直接将 RDF 字符串中的“:”字符替换为“_”来完全摆脱命名空间。这不起作用,因为 XML 文档中的命名空间前缀是任意的,仅用作文档开头定义的更长 URI 的“快捷方式”。不同的人可以使用不同的前缀来表示同一个命名空间 URI,这不应该有区别。)

所以我决定创建一个自己的解决方案,作为一个“轻量级”的替代方案。它实际上是一个文件,包含一个主类(SimpleRDFElement 类)和一个辅助类(SimpleRDFTriple 类)。它所做的就是向 PHP 的内置 SimpleXMLElement 类添加一些辅助方法。但当您处理 RDF XML 时,这些方法会产生巨大的差异。

由于此解决方案简短且简单,因此有很多功能是缺失的。这是故意的:它不打算做太多事情。它是一个简单问题的简单解决方案。它允许您将 RDF 文档解析为对象,并允许您访问命名空间信息。它还提供了一个方法,可以从对象表示的顶级元素及其直接子元素中提取三元组。(此方法不是递归的,因此您需要自己进行任何递归。)

我不能保证它绝对能处理所有有效的 RDF/XML 文档。但是,我愿意进行(一些)添加和改进,并修复您发现的任何错误。请通过您的评论、建议和抱怨与我联系。

Using the Code

此代码是一个包含两个 PHP 类定义的单个文件。

第一个类仅仅是一个辅助类,SimpleRDFTriple,它实际上是一个没有方法的对象,只有三个属性:tripleSubjecttriplePredicatetripleObject。这个类之所以存在,只是为了让 SimpleRDFElement 类有一个 getTriples() 方法,该方法返回该类型对象的数组。

第二个类,SimpleRDFElement,继承了 PHP 中 SimpleXML 库内置的 SimpleXMLElement 类。

由于该类继承了 SimpleXMLElement,您可以使用内置函数 simplexml_load_string() 从包含 RDF/XML 文本的字符串变量创建新的 SimpleRDFElement

$xmlobj = simplexml_load_string($xmltext,'SimpleRDFElement');

第一个参数是包含您要解析的 RDF/XML 文本的变量,第二个参数是一个字符串:我们扩展类的名称,SimpleRDFElement。这将返回一个 SimpleRDFElement 类型的对象,这意味着它可以像 SimpleXMLElement 对象一样进行操作,但您也可以使用扩展类提供的新元素。

SimpleRDFClass 提供的新方法是

$xmlobj->getPrefix()

根据 XML 文本定义的命名空间定义,返回对象的根元素的命名空间前缀。

$xmlobj->getNamespace()

根据 XML 文本定义的命名空间定义,返回对象的根元素的命名空间完整 URI。

$xmlobj->getFullName()

返回根元素的完全限定名称,使用前缀:标签名格式,例如 rdfs:Class

$xmlobj->getFullURI()

返回根元素的完整 URI,使用命名空间的扩展 URI 后跟元素标签名,例如http://www.w3.org/2000/01/rdf-schema#Class

$xmlobj->getChildNodes()

返回当前顶级元素的所有子元素(作为 SimpleRDFElement 对象)的数组。与内置的 children() 方法不同,此方法返回所有子元素,无论命名空间如何。

$xmlobj->getAttributes()

返回当前顶级元素的所有属性(作为单个 SimpleRDFElement 对象)的数组。与内置的 attributes() 方法不同,此方法返回所有属性,无论命名空间如何。

$xmlobj->getTriples()

返回 SimpleRDFTriple 对象的数组。这是一个简单的辅助类,定义了一个具有三个属性的对象:tripleSubjecttriplePredicatetripleObject。此方法解析顶级元素,并根据该元素、其属性及其直接子元素构建三元组。它不是递归的。

大多数方法都很简单,如果您熟悉 RDF、XML 和命名空间,其用法是不言自明的。

唯一复杂的方法是 getTriples(),它返回基于 $xmlobject 表示的根元素而生成的 SimpleRDFTriple 对象数组。

您应该记住,getTriples() 不是递归的,因此它会假定根节点代表一个 RDF 元素,该元素包含三元组主语的信息,而直接子元素(和属性)则是该主语的谓语和宾语信息。这意味着,如果您最初是从完整的 RDF/XML 文档创建了 $xmlobj,使得根元素是 RDF 元素,那么您将需要遍历子元素来提取三元组。

例如,以下代码提供了一个非常简单的 RDF/XML 字符串,并将演示如何提取其所有三元组。

$xmltext = 
'<rdf:rdf 
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<rdf:Description rdf:id="#someperson">
<rdfs:label>Bob</rdfs:label>
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person" />
</rdf:Description>
</rdf:rdf>';

$xmlobj = simplexml_load_string($xmltext,'SimpleRDFElement');

foreach ($xmlobj->getChildNodes() as $child)
{
     foreach ($child->getTriples() as $trip)
     {
        print_r( $trip );
     }
}

这将产生以下输出文本:

SimpleRDFTriple Object
(
    [tripleSubject] => #someperson
    [triplePredicate] => http://www.w3.org/2000/01/rdf-schema#label
    [tripleObject] => Bob
)
SimpleRDFTriple Object
(
    [tripleSubject] => #someperson
    [triplePredicate] => http://www.w3.org/1999/02/22-rdf-syntax-ns#type
    [tripleObject] => http://xmlns.com/foaf/0.1/Person
)

关注点

源代码文件中的代码故意保持非常简单,这样您就可以确切地看到它是如何完成的,而不是简单地将其用作某种“黑盒子”,并且(如果您愿意)可以进行修改。

如果您想出特别巧妙的扩展或附加方法,请告诉我,我会将其(以及您的名字,并注明出处)添加到上面链接的源代码中。

历史

此类或与其相关的任何更新将出现在博客上:http://talkingowlproject.blogspot.com/

© . All rights reserved.