甜RDF/quick-rdf-io

与甜RDF/rdfInterface 兼容的解析器和序列化器集合

1.1.3 2024-09-14 19:44 UTC

README

Latest Stable Version Build status Coverage Status License

RDF解析器和序列化器集合,实现https://github.com/sweetrdf/rdfInterface 接口。

最初为 quickRdf 库开发。

支持的格式

[1] 流式解析器/序列化器不会在内存中完全实现整个数据集,这保证了恒定(且低)的内存占用。(此功能仅适用于解析器/序列化器 - 请参阅下面的内存使用部分)
[2] 使用 jsonld-stream 作为 \quickRdfIo\Util::serialize()$format 参数值来使用此序列化器。
[3] 仅以极度扁平化的Json-LD格式输出数据,但以流式模式运行。

安装

  • 获取 Composer
  • 运行 composer require sweetrdf/quick-rdf-io

自动生成的文档

https://sweetrdf.github.io/quickRdfIo/namespaces/quickrdfio.html

它非常不完整,但没有比什么都没有好。
包括 RdfInterfaceml/json-ld 文档。

使用方法

注意 - 示例中调用了两个其他库 sweetrdf/quick-rdfsweetrdf/term-templates。您可以使用 composer require sweetrdf/quick-rdfcomposer require sweetrdf/term-templates 来安装它们。

基本解析

只需使用 \quickRdfIo\Util::parse($input, $dataFactory, $format, $baseUri),其中

  • $input 可以是(几乎)包含RDF的“任何东西”(一个RDF字符串,文件的路径,URL,打开的资源(fopen()的结果),PSR-7响应或PSR-7 StreamInterface对象)。
  • $dataFactory 是实现 \rdfInterface\DataFactory 接口的对象,例如 new \quickRdf\DataFactory()
  • $format 是一个 可选的 显式RDF格式指示,用于处理无法自动检测格式的罕见情况。请参阅 src/quickRdfIo/Util.php::getParser() 源代码,查看所有接受的 $format 值的列表。
  • $baseUri 是一个 可选的 baseURI值(对于某些类型的 $input 值,它可以自动检测)。
include 'vendor/autoload.php';

// create a DataFactory - it's needed by all parsers
// (DataFactory implementation comes from other package, here sweetrdf/quick-rdf)
$dataFactory = new \quickRdf\DataFactory();

// parse a file
$iterator = \quickRdfIo\Util::parse('tests/files/quadsPositive.nq', $dataFactory);
foreach ($iterator as $i) echo "$i\n";
// parse a remote file (with format autodetection as github wrongly reports text/html)
$url = 'https://github.com/sweetrdf/quickRdfIo/raw/master/tests/files/spec2.10.rdf';
$iterator = \quickRdfIo\Util::parse($url, $dataFactory);
foreach ($iterator as $i) echo "$i\n";
// parse a PSR-7 response (format recognized from the response content-type header)
$url = 'https://www.w3.org/2000/10/rdf-tests/RDF-Model-Syntax_1.0/ms_7.2_1.rdf';
$client = new \GuzzleHttp\Client();
$request = new \GuzzleHttp\Psr7\Request('GET', $url);
$response = $client->send($request);
$iterator = \quickRdfIo\Util::parse($response, $dataFactory);
foreach ($iterator as $i) echo "$i\n";
// parse a string containing RDF with format autodetection
$rdf = file_get_contents('https://www.w3.org/2000/10/rdf-tests/RDF-Model-Syntax_1.0/ms_7.2_1.rdf');
$iterator = \quickRdfIo\Util::parse($rdf, $dataFactory);
foreach ($iterator as $i) echo "$i\n";
// parse an PHP stream
$stream = fopen('tests/files/quadsPositive.nq', 'r');
$iterator = \quickRdfIo\Util::parse($stream, $dataFactory);
fclose($stream);

// in most cases you will populate a Dataset with parsed triples/quads
// (note that a Dataset implementation comes from other package, e.g. sweetrdf/quick-rdf)
$dataset = new \quickRdf\Dataset();
$url = 'https://github.com/sweetrdf/quickRdfIo/raw/master/tests/files/spec2.10.rdf';
$dataset->add(\quickRdfIo\Util::parse($url, $dataFactory));
echo $dataset;

基本序列化

只需使用 \quickRdfIo\Util::serialize($data, $format, $output, $nmsp),其中

  • $data 是实现 \rdfInterface\QuadIterator 接口的对象,例如一个Dataset或解析器返回的迭代器。
  • $format 指定一个RDF序列化格式,例如 turtlentriples。请参阅 src/quickRdfIo/Util.php::getSeriazlier() 源代码,查看所有接受的 $format 值的列表。
  • $output 是一个 可选的 参数,描述输出应写入的位置。如果它缺失或为null,则输出作为字符串返回。如果它是一个字符串,则将其视为使用 fopen($output, 'wb') 打开的路径。如果它是一个流资源或PSR-8 StreamInterface 实例,则输出直接写入其中。
  • $nmsp 是一个 可选 参数,用于向序列化器传递期望的 RDF 命名空间别名。注意,某些格式(如 n-triples 和 n-quads)不支持命名空间别名,而在其他格式(例如 turtle)中使用它们则非常常见。
include 'vendor/autoload.php';

$iterator = ...some \rdfInterface\QuadIterator, e.g. one from parsing examples...

// serialize to file in text/turtle format
\quickRdfIo\Util::serialize($iterator, 'turtle', 'myFile.ttl');
// serialize to string
echo \quickRdfIo\Util::serialize($iterator, 'turtle');

// use given namespace aliases when serializing to turtle
$nmsp = new \quickRdf\RdfNamespace();
$nmsp->add('http://purl.org/dc/terms/', 'dc');
$nmsp->add('http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf');
echo \quickRdfIo\Util::serialize($iterator, 'turtle', null, $nmsp);

基本转换

include 'vendor/autoload.php';
// create a DataFactory - it's needed by all parsers
// (note that DataFactory implementation comes from other package, e.g. sweetrdf/quick-rdf)
$dataFactory = new \quickRdf\DataFactory();

// or any other example from the "Basic parsing" section above
$iterator = \quickRdfIo\Util::parse('tests/files/puzzle4d_100k.nt', $dataFactory);
// or any other example from the "Basic serialization" section above
\quickRdfIo\Util::serialize($iterator, 'rdf', 'output.rdf');

不带数据集的基本过滤

值得注意的是,基本的三元组/四元组过滤可以通过不使用数据集实现以内存高效的方式进行。

假设我们想要将 https://vocabs.acdh.oeaw.ac.at/schema#hasIdentifier 谓词的所有三元组从 test/files/puzzle4d_100k.nt n-triples 文件复制到 ids.ttl turtle 文件中。

典型的方法是将数据加载到数据集中,在那里过滤它们,最后序列化数据集

include 'vendor/autoload.php';
$dataFactory = new \quickRdf\DataFactory();
$t = microtime(true);

// parse input into a Dataset
$iterator = \quickRdfIo\Util::parse('tests/files/puzzle4d_100k.nt', $dataFactory);
$dataset = new \quickRdf\Dataset();
$dataset->add($iterator);

// filter out non-matching triples
$template = new \termTemplates\QuadTemplate(null, $dataFactory->namedNode('https://vocabs.acdh.oeaw.ac.at/schema#hasIdentifier'), null);
$dataset->deleteExcept($template);

// serialize
\quickRdfIo\Util::serialize($dataset, 'turtle', 'ids.ttl');

print_r([
    'time [s]' => microtime(true) - $t,
    'memory [MB]' => (int) (memory_get_peak_usage(true) / 1024 / 1024),
]);
// 4.4s, 125 MB of RAM

但也可以通过使用“过滤生成器”而不是数据集来完成。这种方法可以避免在内存中实例化整个数据集,这应该既会减少内存占用,也会略微提高速度

include 'vendor/autoload.php';
$dataFactory = new \quickRdf\DataFactory();
$t = microtime(true);

// prepare input generator
$iterator = \quickRdfIo\Util::parse('tests/files/puzzle4d_100k.nt', $dataFactory);

// create a generator performing the filtering
$template = new \termTemplates\QuadTemplate(null, $dataFactory->namedNode('https://vocabs.acdh.oeaw.ac.at/schema#hasIdentifier'), null);
$filter = function($iter, $tmpl) {
    foreach ($iter as $quad) {
        if ($tmpl->equals($quad)) {
            yield $quad;
        }
    }
};
// wrap it into something implementing \rdfInterface\QuadIterator for types compatibility
$wrapper = new \rdfHelpers\GenericQuadIterator($filter($iterator, $template));

// serialize our filtering generator
\quickRdfIo\Util::serialize($wrapper, 'turtle', 'ids.ttl');

print_r([
    'time [s]' => microtime(true) - $t,
    'memory [MB]' => (int) (memory_get_peak_usage(true) / 1024 / 1024),
]);
// 2.7s, 51 MB of RAM

结果更好,但内存占用仍然出奇的高。这是因为我们使用的 DataFactory 实现,以及它所应用的性能优化(诚实地讲,在我们的场景中这只会减慢速度)。我们可以通过使用尽可能简单的 DataFactory 实现来进一步优化(为此我们需要另一个包 - sweetrdf/simple-rdf

include 'vendor/autoload.php';
$dataFactory = new \simpleRdf\DataFactory();
$t = microtime(true);

// prepare input generator
$iterator = \quickRdfIo\Util::parse('tests/files/puzzle4d_100k.nt', $dataFactory);

// create a generator performing the filtering
$template = new \termTemplates\QuadTemplate(null, $dataFactory->namedNode('https://vocabs.acdh.oeaw.ac.at/schema#hasIdentifier'), null);
$filter = function($iter, $tmpl) {
    foreach ($iter as $quad) {
        if ($tmpl->equals($quad)) {
            yield $quad;
        }
    }
};
// wrap it into something implementing \rdfInterface\QuadIterator for types compatibility
$wrapper = new \rdfHelpers\GenericQuadIterator($filter($iterator, $template));

// serialize our filtering generator
\quickRdfIo\Util::serialize($wrapper, 'turtle', 'ids.ttl');

print_r([
    'time [s]' => microtime(true) - $t,
    'memory [MB]' => (int) (memory_get_peak_usage(true) / 1024 / 1024),
]);
// 1.9s, 2 MB of RAM

正如我们所见,优化后的实现比基于数据集的实现快2.3倍,内存占用降低了60倍。

注意

  • 请查阅 sweetrdf/term-templates 库,以获取更多允许轻松匹配满足给定条件的三元组/四元组的类。
  • 这种方法不仅限于过滤。也可以以类似的方式应用简单的三元组/四元组修改(只需调整“过滤生成器” foreach 循环体)。

手动解析器/序列化器实例化

当然,可以显式实例化特定的解析器/序列化器。

这是调整解析器/序列化器配置的唯一选项,例如。

  • 创建一个严格的 n-triples 解析器
    $parser = new \quickRdfIo\NQuadsParser($dataFactory, true, \quickRdfIo\NQuadsParser::MODE_TRIPLES);
  • 创建一个 JsonLD 序列化器,使用从给定文件读取的上下文进行压缩,并生成格式化的 JSON
    $serializer = new \quickRdfIo\JsonLdSerializer(
        'http://baseUri', 
        \quickRdfIo\JsonLdSerializer::TRANSFORM_COMPACT, 
        JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT,
        'context.jsonld'
    );

请注意,使用手动创建的解析器/序列化器实例进行解析/序列化需要更多的代码。

比较

include 'vendor/autoload.php';
$data = ...data read from somewhere...

// using \quickRdfIo\Util::serialize()
\quickRdfIo\Util::serialize($data, 'jsonld', 'output.jsonld');

// using manually instantiated serializer
$serializer = new \quickRdfIo\JsonLdSerializer();
$output = fopen('output.jsonld', 'w');
$serializer->serialize($data, $output);
fclose($output);