RDF-aREF-0.27000755001750001750 012703377652 11423 5ustar00vojvoj000000000000README100644001750001750 615212703377652 12370 0ustar00vojvoj000000000000RDF-aREF-0.27NAME RDF::aREF - Another RDF Encoding Form SYNOPSIS use RDF::aREF; my $rdf = { _id => 'http://example.com/people#alice', foaf_name => 'Alice Smith', foaf_age => '42^xsd_integer', foaf_homepage => [ { _id => 'http://personal.example.org/', dct_modified => '2010-05-29^xsd_date', }, 'http://work.example.com/asmith/', ], foaf_knows => { dct_description => 'a nice guy@en', }, }; decode_aref( $rdf, callback => sub { my ($subject, $predicate, $object, $language, $datatype) = @_; ... } ); my @lastmod = aref_query $rdf, 'foaf_homepage.dct_modified^'; my $model = RDF::Trine::Model->new; decode_aref( $rdf, callback => $model ); print RDF::Trine::Serializer->new('Turtle')->serialize_model_to_string($model); my $model = RDF::Trine::Model->new; RDF::Trine::Parser->parse_url_into_model($url, $model); my $aref = encode_aref $model; DESCRIPTION aREF (another RDF Encoding Form ) is an encoding of RDF graphs in form of arrays, hashes, and Unicode strings. This module provides methods for decoding from aREF data to RDF triples (RDF::aREF::Decoder), for encoding RDF data in aREF (RDF::aREF::Encoder), and for querying parts of an RDF graph (RDF::aREF::Query). EXPORTED FUNCTIONS The following functions are exported by default. decode_aref $aref [, %options ] Decodes an aREF document given as hash reference with RDF::aREF::Decoder. Equivalent to "RDF::aREF::Decoder->new(%options)->decode($aref)". encode_aref $graph [, %options ] Construct an aREF subject mapfrom an RDF graph. The RDF::aREF::Encoder for possible options. The $graph can be supplied as: * instance of RDF::Trine::Model * instance of RDF::Trine::Model::Iterator * an URL or a filename (only if RDF::Trine is installed) * instance of Attean::API::TripleIterator (experimental) * instance of Attean::API::TripleStore (experimental) * hash reference with RDF/JSON format (as returned by method "as_hashref" in RDF::Trine::Model) aref_query $graph, [ $origin ], @queries Query parts of an aREF data structure by aREF query expressions and return a list. See RDF::aREF::Query for details. aref_query_map( $graph, [ $origin ], $query_map ) Map parts of an aREF data structure to a flat key-value structure. SEE ALSO * aREF is specified at . * See Catmandu::RDF for an application of this module. * Usee RDF::Trine for more elaborated handling of RDF data in Perl. * See RDF::YAML for a similar (outdated) RDF encoding in YAML. COPYRIGHT AND LICENSE Copyright Jakob Voss, 2014- This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Changes100644001750001750 544012703377652 13002 0ustar00vojvoj000000000000RDF-aREF-0.27# Changelog for RDF-aREF 0.27 2016-04-13 10:13:27 CEST - Fix test failure caused by RDF::NS (#20) 0.26 2016-03-01 10:14:22 CET - encode literal with datatype xsd:string as plain literal (#19) 0.25 2015-03-03 12:33:40 CET - support aref_query on property maps - support aREF query expression with multiple items - support encoding RDF from file - support Unicode normalization 0.23 2014-12-11 16:40:30 CET - new decoder option keep_bnode_map 0.22 2014-12-11 14:57:01 CET - modified encoder, added subject and triple method - remove or hide some exerimental methods - avoid malformed bnode identifiers 0.21 2014-10-28 14:08:40 CET - allow 'a' in aREF query expressions - experimental support to encode from URL - support multiple queries in aref_query 0.20 2014-10-16 12:09:05 CEST - support empty queries - fix bug for literal nodes without language tags in encoder - new encoder method add_hashref 0.19 2014-10-15 14:56:18 CEST - added encoding methods - removed internal method aref_to_trine_statement (issue #6) 0.18 2014-10-14 10:35:56 CEST - implement aref_query_map (issue #7) - adjust subjects to aREF spec (more strict) 0.17 2014-10-07 13:30:47 CEST - fix failure on Perl 5.10 - more cleanup and tests 0.16 2014-10-07 10:50:16 CEST - cleanup - only require Perl 5.10 (issue #5) 0.15 2014-10-05 15:16:09 CEST - better support RDF::Trine in Encoder - introduced RDF::aREF::Query (experimental) 0.14 2014-10-02 14:14:09 CEST - adjust with aREF 0.19 - added aref_query (experimental) 0.13 2014-10-01 16:09:44 CEST - improveded Encoder 0.12 2014-10-01 14:45:09 CEST - migrated to Dist::Zilla - added methods aref_get_literal and aref_get_resource - disallow ':' in predicates - added partial Encoder 0.11 2014-04-09T12:56:37 - don't die on malformed IRIs 0.10 2014-01-22T11:11:50 - fixed bnode collision bug - new option: bnode_prefix - show how to serialize with RDF::Trine - more test cases 0.09 2014-01-21T13:11:32 - better error message when expecting an URI (close #3) - Avoid numeric blank node identifiers. Although it's legal, some parsers don't like them. - Show how to decode into RDF::Trine::Model 0.08 2014-01-15T11:08:31 - facilitate use of this module together with RDF::Trine 0.07 2013-12-18T08:21:41 - increased error detection - fixed test_requires 0.06 2013-12-14T20:15:38 - extended test suite - test-cases with errors 0.05 2013-12-04T20:01:53 - improved handling of undefined/null - use warnings; list provides 0.04 2013-12-03T08:51:00 - improved error handling, fixed bug in subject-maps 0.03 2013-12-02T13:47:26 - release - avoid lexical $_ - added error test - run coverage test with RDF::Trine - fixed links - improved unit tests - rewrote aREF decoder - split RDF::aREF from Catmandu-RDF t000755001750001750 012703377652 11607 5ustar00vojvoj000000000000RDF-aREF-0.27nfc.t100644001750001750 47612703377652 12671 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use warnings; use Test::More; use RDF::aREF; my $rdf = "t/nfc.ttl"; my $aref = encode_aref $rdf, NFC => 1; is $aref->{foaf_given}, $aref->{foaf_surname}, "Unicode Normalization NFC"; $aref = encode_aref $rdf; isnt $aref->{foaf_given}, $aref->{foaf_surname}, "Unicode Normalization NFC"; done_testing; LICENSE100644001750001750 4361712703377652 12544 0ustar00vojvoj000000000000RDF-aREF-0.27This software is copyright (c) 2014- by -. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2014- by -. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2014- by -. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End cpanfile100644001750001750 34312703377652 13170 0ustar00vojvoj000000000000RDF-aREF-0.27requires 'perl', '5.10.0'; requires 'RDF::NS', '20140908'; suggests 'RDF::Trine', '1.010'; suggests 'Unicode::Normalize'; on test => sub { requires 'JSON'; requires 'RDF::Trine'; requires 'Unicode::Normalize'; }; dist.ini100644001750001750 3312703377652 13104 0ustar00vojvoj000000000000RDF-aREF-0.27author=Jakob Voß [@Milla] null.t100644001750001750 205212703377652 13105 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use warnings; use Test::More; use RDF::aREF; sub decode_ok(@) { my $aref = shift; my $count = shift; my $triples = 0; RDF::aREF::Decoder ->new( callback => sub { $triples++ }, @_ ) ->decode($aref); local $Test::Builder::Level = $Test::Builder::Level + 1; is $triples, $count; } # ignore undef decode_ok { _id => undef, a => 'ex:ample' }, 0; decode_ok { _id => 'an:id', a => 'ex:ample' }, 1; decode_ok { _id => 'an:id', a => undef }, 0; decode_ok { _id => 'an:id', a => [undef,'ex:ample'] }, 1; # ignore undef and '0' decode_ok { _id => '0', a => 'ex:ample' }, 0, null => 0; decode_ok { _id => 'an:id', a => 'ex:ample' }, 1, null => 0; decode_ok { _id => 'an:id', a => '0' }, 0, null => 0; decode_ok { _id => 'an:id', a => ['0','ex:ample'] }, 1, null => 0; decode_ok { _id => 'an:id', a => [undef,'ex:ample'] }, 1, null => undef; decode_ok { 'an:id' => { a => 'ex:ample' }, '0' => { a => 'ex:ample' } }, 1, null => 0; # ignore as subject decode_ok { '' => { a => 'ex:ample' } }, 0, null => ''; done_testing; Build.PL100644001750001750 25512703377652 12762 0ustar00vojvoj000000000000RDF-aREF-0.27# This Build.PL for RDF-aREF was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.014. use strict; use warnings; use v5.10.0; use Module::Build::Tiny 0.034; Build_PL(); META.yml100644001750001750 151512703377652 12757 0ustar00vojvoj000000000000RDF-aREF-0.27--- abstract: 'Another RDF Encoding Form' author: - 'Jakob Voß' build_requires: JSON: '0' RDF::Trine: '0' Unicode::Normalize: '0' configure_requires: Module::Build::Tiny: '0.034' dynamic_config: 0 generated_by: 'Dist::Zilla version 5.035, Dist::Milla version v1.0.8, CPAN::Meta::Converter version 2.150001' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: RDF-aREF no_index: directory: - t - xt - inc - share - eg - examples requires: RDF::NS: '20140908' perl: v5.10.0 resources: bugtracker: https://github.com/nichtich/RDF-aREF/issues homepage: https://github.com/nichtich/RDF-aREF repository: https://github.com/nichtich/RDF-aREF.git version: '0.27' x_contributors: - 'Jakob Voss ' - 'Jakob Voss ' MANIFEST100644001750001750 431012703377652 12633 0ustar00vojvoj000000000000RDF-aREF-0.27# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.035. Build.PL Changes LICENSE MANIFEST META.json META.yml README cpanfile dist.ini lib/RDF/aREF.pm lib/RDF/aREF/Decoder.pm lib/RDF/aREF/Encoder.pm lib/RDF/aREF/Query.pm t/aref-query.t t/decode-errors.t t/decoder.t t/doi-example.json t/encode_aref.t t/encoder.t t/example.json t/nfc.t t/nfc.ttl t/null.t t/objects.t t/release-pod-syntax.t t/suite.t t/suite/README.md t/suite/blanks.json t/suite/blanks.nt t/suite/invalid-datatype-01.err t/suite/invalid-datatype-01.json t/suite/invalid-object-01.err t/suite/invalid-object-01.json t/suite/invalid-object-02.err t/suite/invalid-object-02.json t/suite/invalid-object-03.err t/suite/invalid-object-03.json t/suite/invalid-predicate-01.err t/suite/invalid-predicate-01.json t/suite/invalid-predicate-02.err t/suite/invalid-predicate-02.json t/suite/invalid-predicate-03.err t/suite/invalid-predicate-03.json t/suite/invalid-subject-01.err t/suite/invalid-subject-01.json t/suite/invalid-subject-02.err t/suite/invalid-subject-02.json t/suite/invalid-subject-03.err t/suite/invalid-subject-03.json t/suite/invalid-subject-04.err t/suite/invalid-subject-04.json t/suite/invalid-subject-05.err t/suite/invalid-subject-05.json t/suite/invalid-subject-06.err t/suite/invalid-subject-06.json t/suite/invalid-subject-07.err t/suite/invalid-subject-07.json t/suite/invalid-subject-08.err t/suite/invalid-subject-08.json t/suite/invalid-subject-09.err t/suite/invalid-subject-09.json t/suite/invalid-subject-10.err t/suite/invalid-subject-10.json t/suite/invalid-subject-10.nt t/suite/json-ld-14.json t/suite/json-ld-14.nt t/suite/json-ld-23.json t/suite/json-ld-23.nt t/suite/rdf-trine-model.json t/suite/rdf-trine-model.nt t/suite/strict-invalid-subject-11.err t/suite/strict-invalid-subject-11.json t/suite/strict-invalid-subject-12.err t/suite/strict-invalid-subject-12.json t/suite/strict-invalid-subject-13.err t/suite/strict-invalid-subject-13.json t/suite/strict-null-object-1.err t/suite/strict-null-object-1.json t/suite/strict-null-object-2.err t/suite/strict-null-object-2.json t/suite/strict-null-object-2.nt t/suite/uri-object-01.json t/suite/uri-object-01.nt t/suite/uri-object-02.json t/suite/uri-object-02.nt t/trine.t nfc.ttl100644001750001750 20612703377652 13220 0ustar00vojvoj000000000000RDF-aREF-0.27/t "\u0045\u0304\u0300\uAC00\u11A8" ; "\u1E14\uAC01" . suite.t100644001750001750 314012703377652 13263 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use Test::More; use File::Find; BEGIN { eval { require RDF::Trine::Model; require RDF::Trine::Serializer::NTriples::Canonical; require JSON; 1; } or do { plan skip_all => "RDF::Trine or JSON required"; }; } sub slurp { local (@ARGV, $/) = @_; my $data = -e $ARGV[0] ? <> : ""; $data =~ s/\n$//; $data; } use RDF::aREF qw(decode_aref); use RDF::aREF::Decoder; sub trine_statement { # TODO: remove/modify this method RDF::aREF::Decoder::trine_statement(@_); } foreach my $file (sort ) { $file =~ s/\.json$//; my $name = $file; $name =~ s{^t/suite/}{}; my $aref = JSON::from_json(slurp("$file.json")); my $nt = slurp("$file.nt"); my $err = slurp("$file.err"); my @errors; my $model = RDF::Trine::Model->new; # TODO: use iterator instead # a (->next returns a trine-statement) $model->begin_bulk_ops; my $decoder = RDF::aREF::Decoder->new( callback => sub { $model->add_statement( trine_statement( @_ ) ) }, complain => 2, strict => ($file =~ /strict/ ? 1 : 0), ); eval { $decoder->decode($aref) }; if ($err) { my $got = $@; $got =~ s{ at t/suite\.t line \d+[.]?\n}{}; is $got, $err, "$name.err"; } else { $model->end_bulk_ops; my $got = RDF::Trine::Serializer::NTriples::Canonical->new( onfail => 'truncate', )->serialize_model_to_string($model); $got =~ s/\r|\n$//g; $file =~ s{.*/}{}; is $got, $nt, $file; } } done_testing; trine.t100644001750001750 476612703377652 13272 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use Test::More; use RDF::aREF; use RDF::aREF::Encoder; BEGIN { eval { require RDF::Trine; RDF::Trine->import(qw(statement iri literal)); 1; } or do { plan skip_all => "RDF::Trine required"; }; } my $model = RDF::Trine::Model->new; decode_aref( { _id => 'http://example.org/alice', a => 'foaf_Person', foaf_knows => 'http://example.org/bob' }, callback => $model, ); is $model->size, 2, 'added two statements'; my $aref = encode_aref $model, ns => '20150725'; is_deeply $aref, { _id => 'http://example.org/alice', a => 'foaf_Person', foaf_knows => '' }, 'encode_aref from RDF::Trine::Model'; decode_aref({ _id => 'http://example.org/alice', a => 'foaf_Person' }, callback => $model); decode_aref({ _id => 'http://example.org/bob', a => 'foaf_Person' }, callback => $model); is $model->size, 3, 'added another statement'; is_deeply( encode_aref($model, ns => '20150725'), { 'http://example.org/alice' => { 'a' => 'foaf_Person', 'foaf_knows' => '' }, 'http://example.org/bob' => { 'a' => 'foaf_Person' } }, 'converted predicate map to subject map'); # bnodes $model = RDF::Trine::Model->new; my $decoder = RDF::aREF::Decoder->new( callback => $model ); my $aref = { _id => '', foaf_knows => { foaf_name => 'alice' } }; $decoder->decode( $aref ); $decoder->decode( $aref ); is $model->size, 4, 'no bnode collision'; is $decoder->bnode_count, 2; $decoder->bnode_count(1); $decoder->decode( $aref ); is $model->size, 4, 'bnode collision'; # errors my $warning; local $SIG{__WARN__} = sub { $warning = shift }; decode_aref( { _id => 'isbn:123', rdfs_seeAlso => [ 'isbn:456 x', # looks like an IRI to aREF but rejected by Trine 'isbn:789' ] }, callback => $model, ); ok $warning, "bad IRI"; is $model->size, 5, 'ignored illformed URI'; my $encoder = RDF::aREF::Encoder->new(ns => '20140910'); is_deeply $encoder->triple( iri('http://example.org/'), iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), iri('http://xmlns.com/foaf/0.1/Agent'), ), { _id => 'http://example.org/', a => 'foaf_Agent' }, 'triple'; is_deeply $encoder->triple( iri('http://example.org/'), iri('http://xmlns.com/foaf/0.1/name'), literal('Anne','de'), ), { _id => 'http://example.org/', foaf_name => 'Anne@de' }, 'triple'; # TODO encode_aref $model / $iterator done_testing; META.json100644001750001750 330512703377652 13126 0ustar00vojvoj000000000000RDF-aREF-0.27{ "abstract" : "Another RDF Encoding Form", "author" : [ "Jakob Voß" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 5.035, Dist::Milla version v1.0.8, CPAN::Meta::Converter version 2.150001", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "RDF-aREF", "no_index" : { "directory" : [ "t", "xt", "inc", "share", "eg", "examples" ] }, "prereqs" : { "configure" : { "requires" : { "Module::Build::Tiny" : "0.034" } }, "develop" : { "requires" : { "Dist::Milla" : "v1.0.8", "Test::Pod" : "1.41" } }, "runtime" : { "requires" : { "RDF::NS" : "20140908", "perl" : "v5.10.0" }, "suggests" : { "RDF::Trine" : "1.010", "Unicode::Normalize" : "0" } }, "test" : { "requires" : { "JSON" : "0", "RDF::Trine" : "0", "Unicode::Normalize" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/nichtich/RDF-aREF/issues" }, "homepage" : "https://github.com/nichtich/RDF-aREF", "repository" : { "type" : "git", "url" : "https://github.com/nichtich/RDF-aREF.git", "web" : "https://github.com/nichtich/RDF-aREF" } }, "version" : "0.27", "x_contributors" : [ "Jakob Voss ", "Jakob Voss " ] } decoder.t100644001750001750 766012703377652 13552 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use warnings; use Test::More; use RDF::aREF::Decoder; my $_rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; my $_foaf = "http://xmlns.com/foaf/0.1/"; my $_xsd = 'http://www.w3.org/2001/XMLSchema#'; my $_ex = "http://example.org/"; my $alice = "http://example.org/alice"; sub decode { my @triples; my ($aref, %options) = ref $_[0] eq 'HASH' ? ($_[0]) : (@{$_[0]}); RDF::aREF::Decoder->new( callback => sub { push @triples, join " ", map { (ref $_ ? '?'.$$_ : $_) // '' } @_; }, %options )->decode( $aref ); join "\n", sort @triples; } sub test_decode(@) { ## no critic local $Test::Builder::Level = $Test::Builder::Level + 1; is decode($_[0]), $_[1]; } # many ways to encode the same simple triple test_decode $_, "$alice ${_rdf}type ${_foaf}Person" for # predicate map { _id => $alice, a => "foaf_Person" }, { _id => $alice, a => "${_foaf}Person" }, { _id => $alice, a => "<${_foaf}Person>" }, { _id => $alice, rdf_type => "foaf_Person" }, { _id => $alice, "${_rdf}type" => "foaf_Person" }, { _id => $alice, "<${_rdf}type>" => "foaf_Person" }, # subject map { $alice => { a => "foaf_Person" } }, { $alice => { a => "${_foaf}Person" } }, { $alice => { a => "<${_foaf}Person>" } }, { $alice => { rdf_type => "foaf_Person" } }, { $alice => { "${_rdf}type" => "foaf_Person" } }, { $alice => { "<${_rdf}type>" => "foaf_Person" } }, { _ns => { x => $_ex }, x_alice => { a => "foaf_Person" } }, ; # simple literals test_decode $_, "$alice ${_foaf}name Alice " for { $alice => { foaf_name => "Alice" } }, { $alice => { foaf_name => "Alice@" } }, { $alice => { foaf_name => "Alice^<${_xsd}string>" } }, { $alice => { foaf_name => "Alice^xsd_string" } }, ; # datatypes test_decode $_, "$alice ${_foaf}age 42 ${_xsd}integer" for { $alice => { foaf_age => "42^xsd_integer" } }, { $alice => { foaf_age => "42^<${_xsd}integer>" } }, ; # language tags test_decode { $alice => { foaf_name => "Alice\@$_" } }, "$alice ${_foaf}name Alice ".lc($_) for qw(en en-US abcdefgh-x-12345678); # blank nodes test_decode $_, "$alice ${_foaf}knows _:b1" for { $alice => { foaf_knows => { } } }, { $alice => { foaf_knows => { _id => '_:b1' } } }, ; test_decode [ $_, bnode_prefix => 'x' ], "$alice ${_foaf}knows _:x1" for { $alice => { foaf_knows => { } } }, { $alice => { foaf_knows => { _id => '_:b1' } } }, ; test_decode $_, "_:b1 ${_rdf}type ${_foaf}Person\n$alice ${_foaf}knows _:b1" for # { $alice => { foaf_knows => { a => 'foaf_Person' } } }, # { $alice => { foaf_knows => { _id => '_:b1', a => 'foaf_Person' } } }, { $alice => { foaf_knows => '_:b1' }, '_:b1' => { a => 'foaf_Person' } }, ; # TODO: more blank nodes =cut # valid decode_aref { '' => { a => undef } }, complain => 2; decode_aref { '' => { a => 'foaf_Person' } }, complain => 2, null => ''; ok !$error, 'not strict by default'; my $rdf = decode_aref { '' => { a => '' } }, complain => 2, null => ''; ok !$rdf, 'empty string as null'; decode_aref { '' => { a => '' } }, %handler, strict => 1; ok !$error && $rdf, 'empty string not null by default'; =cut =cut my @looks_like_error = ( { '0' => { a => 'foaf_Person' }, _ns => 'x:' }, { _id => '0', a => 'foaf_Person', _ns => 'x:' }, ); my $rdf; my $decoder = RDF::aREF::Decoder->new( callback => sub { $rdf++ } ); foreach (@looks_like_error) { $decoder->decode($_); ok $rdf, 'triple'; $rdf = 0; } =cut note '->plain_literal'; my %tests = ( "a@" => "a", "0^" => "0", "http://example.org/" => undef, "http://example.org/@" => 'http://example.org/', "" => undef, "<>" => "<>", # (sic!) ); use RDF::aREF::Decoder; my $decoder = RDF::aREF::Decoder->new; while (my ($aref, $literal) = each %tests) { my $got = $decoder->plain_literal($aref); is $got, $literal, $aref; } done_testing; encoder.t100644001750001750 727712703377652 13570 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use warnings; use Test::More; use RDF::aREF::Encoder; sub test_encoder(@) { my ($encoder, $method, @tests) = @_; note "RDF::aREF::Encoder::$method"; while (@tests) { my $input = shift @tests; my $expect = shift @tests; local $Test::Builder::Level = $Test::Builder::Level + 1; if ( ref $expect ) { is_deeply $encoder->$method($input), $expect, $expect; } else { is $encoder->$method($input), $expect, $expect; } } } my $encoder = RDF::aREF::Encoder->new( ns => '20140910' ); test_encoder $encoder => 'subject', # RDF/JSON { type => 'uri', value => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' } => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', { type => 'bnode', value => '_:foo' } => '_:foo', # RDF::Trine ['URI','http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', ['BLANK', 0 ] => '_:0', # else 'http://example.org/' => undef, ; test_encoder $encoder => 'predicate', # RDF/JSON { type => 'uri', value => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' } => 'a', { type => 'uri', value => 'http://purl.org/dc/terms/title' } => 'dct_title', # RDF::Trine ['URI','http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] => 'a', ['URI','http://undefinednamespace.foo'] => 'http://undefinednamespace.foo', ['BLANK', 0 ] => undef, # else 'http://example.org/' => undef, ; test_encoder $encoder => 'object', # RDF/JSON { type => 'uri', value => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' } => 'rdf_type', { type => 'uri', value => 'http://www.w3.org/2006/vcard/ns#street-address', } => '', { type => 'literal', value => 'hello, world!', lang => 'en' } => 'hello, world!@en', { type => 'literal', value => '12', datatype => 'http://www.w3.org/2001/XMLSchema#integer' } => '12^xs_integer', { type => 'bnode', value => '_:12', } => '_:12', # RDF::Trine ['URI','http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] => 'rdf_type', ['URI','http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] => 'rdf_type', ['BLANK', 0 ] => '_:0', ['hello, world!', 'en', undef ] => 'hello, world!@en', ['hello, world!' ] => 'hello, world!@', [42, undef, 'http://www.w3.org/2001/XMLSchema#integer'] => '42^xs_integer', # else 'http://example.org/' => undef, ; # RDF::aREF::Encoder::uri( ... ) test_encoder $encoder => 'uri', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => 'rdf_type', 'http://undefinednamespace.foo' => '' ; # RDF::aREF::Encoder::literal( ... ) test_encoder $encoder => 'literal', '' => '@' ; # RDF::aREF::Encoder::bnode( ... ) test_encoder $encoder => 'bnode', abc => '_:abc', 0 => '_:0', '_:0' => undef, ; # RDF::aREF::Encoder::qname( ... ) test_encoder $encoder => 'qname', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => 'rdf_type', 'http://schema.org/Review' => 'schema_Review', 'http://www.w3.org/2006/vcard/ns#street-address' => undef, ; # encoder methods with ns => 0 $encoder = RDF::aREF::Encoder->new( ns => 0 ); is $encoder->literal( 42, undef, 'http://www.w3.org/2001/XMLSchema#integer'), '42^xsd_integer', 'literal (ns=0)'; is $encoder->qname('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), 'rdf_type', 'qname (ns=0)'; is $encoder->predicate('http://purl.org/dc/terms/title'), undef, 'predicate (ns=0)'; done_testing; objects.t100644001750001750 232712703377652 13571 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use warnings; use Test::More; use RDF::aREF qw(decode_aref); my @tests = ( '@' => [ '', undef ], '' => [ '', undef ], '^xsd_string' => [ '', undef ], '^' => [ '', undef ], '@^xsd_string' => [ '@', undef ], '@@' => [ '@', undef ], 'alice@' => [ 'alice', undef ], 'alice@en' => [ 'alice', 'en' ], 'alice@example.com' => [ 'alice@example.com', undef ], '123' => [ '123', undef ], '123^xsd_integer' => [ '123', undef, "http://www.w3.org/2001/XMLSchema#integer" ], '123^' => [ '123', undef, "xsd:integer" ], '忍者@ja' => [ '忍者', 'ja' ], 'Ninja@en@' => [ 'Ninja@en', undef ], 'rdf_type' => [ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' ], '' => [ 'rdf:type' ], 'geo:48.2010,16.3695,183' => [ 'geo:48.2010,16.3695,183' ], 'geo_Point' => [ 'http://www.w3.org/2003/01/geo/wgs84_pos#Point' ], ); while (defined (my $input = shift @tests)) { my ($expect, $object, $error) = shift @tests; decode_aref { 'x:subject' => { '' => $input } }, callback => sub { shift; shift; $object = \@_; }; is_deeply $object, $expect, "\"$input\""; } done_testing; aref-query.t100644001750001750 761012703377652 14220 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use warnings; use Test::More; use RDF::aREF qw(aref_query aref_query_map); use RDF::aREF::Query; use Scalar::Util qw(reftype); BEGIN { eval { require JSON; 1; } or plan skip_all => "test requires JSON"; } my $rdf = JSON::from_json(do { local (@ARGV, $/) = "t/doi-example.json"; <> }); my $uri = "http://dx.doi.org/10.2474/trol.7.147"; my @res = aref_query($rdf, $uri, '.'); is reftype $res[0], 'HASH'; # FIXME: # is_deeply [ aref_query($rdf, $uri, '@') ], [ ], 'empty query (@)'; is_deeply [ aref_query($rdf, $uri, '') ], [ $uri ], 'empty query'; is_deeply [ aref_query($rdf, $uri, 'dct_title') ], ['Frictional Coefficient under Banana Skin'], 'dct_title'; is_deeply [ aref_query($rdf, $uri, 'dct_title@') ], ['Frictional Coefficient under Banana Skin'], 'dct_title@'; is_deeply [ aref_query($rdf, $uri, 'dct_title^xsd_string') ], ['Frictional Coefficient under Banana Skin'], 'dct_title^xsd_string'; is_deeply [ aref_query($rdf, $uri, 'dct_title@en') ], [ ], 'dct_title@'; is_deeply [ sort(aref_query($rdf->{$uri}, 'dct_publisher')) ], [ 'Japanese Society of Tribologists', 'http://d-nb.info/gnd/5027072-2', ], 'dct_publisher'; is_deeply [ aref_query($rdf->{$uri}, 'dct_publisher.') ], [ 'http://d-nb.info/gnd/5027072-2', ], 'dct_publisher.'; is_deeply [ aref_query($rdf->{$uri}, 'dct_date') ], ["2012"], 'dct_date'; is_deeply [ aref_query($rdf->{$uri}, 'dct_date^xsd_gYear') ], ["2012"], 'dct_date^xsd_gYear'; is_deeply [ aref_query($rdf->{$uri}, 'dct_date^xsd_foo') ], [], 'dct_date^xsd_foo'; is_deeply [ aref_query($rdf, $uri, 'dct_creator.a') ], [ map { 'http://xmlns.com/foaf/0.1/Person' } 1..4 ], 'a is a valid property'; foreach my $query (qw(dct_creator dct_creator.)) { is_deeply [ sort (aref_query($rdf, $uri, $query)) ], [ "http://id.crossref.org/contributor/daichi-uchijima-y2ol1uygjx72", "http://id.crossref.org/contributor/kensei-tanaka-y2ol1uygjx72", "http://id.crossref.org/contributor/kiyoshi-mabuchi-y2ol1uygjx72", "http://id.crossref.org/contributor/rina-sakai-y2ol1uygjx72", ], $query; } is join(' ',sort(aref_query($rdf,$uri,'dct_creator.foaf_familyName'))), "Mabuchi Sakai Tanaka Uchijima", 'dct_creator.foaf_familyName'; my %names = ( 'dct_creator.foaf_name' => 4, 'dct_creator.foaf_name@' => 4, 'dct_creator.foaf_name@en' => 4, 'dct_creator.foaf_name@ja' => 0, ); while ( my ($query, $count) = each %names ) { my @names = aref_query( $rdf, $uri, $query ); is scalar @names, $count, $query; } is scalar @{[ aref_query( $rdf, $uri, qw(dct_creator. schema_author. dct_publisher.)) ]}, 5, 'multiple queries'; is_deeply [ aref_query( $rdf, $uri, 'bibo_pageStart|bibo_unknown|bibo_pageEnd' ) ], [qw(147 151)], 'multiple items'; foreach my $query ( "dct_title@#", "dct_date^_" ) { eval { RDF::Query->new($query) }; ok $@, 'error in aREF query'; } { my $rdf = { 'http://example.org/book' => { dct_creator => [ 'http://example.org/alice', "Bob" ] }, 'http://example.org/alice' => { foaf_name => "Alice" }, }; my $uri = 'http://example.org/book'; is_deeply [ sort(aref_query($rdf, $uri, 'dct_creator')) ], [qw(Bob http://example.org/alice)], 'literal and URI'; my $record = aref_query_map( $rdf, $uri, { 'dct_creator@' => 'creator', 'dct_creator.foaf_name' => 'creator', }); is_deeply [ sort @{$record->{creator}} ], [qw(Alice Bob)], 'aref_query_map'; } { my $url = "http://example.org/"; my $aref = { dct_title => "Hello@", _id => $url, }; is_deeply [ aref_query($aref, $url, 'dct_title@de','dct_title@') ], ["Hello"], "query from property map"; is_deeply [ aref_query($aref, undef, 'dct_title@de','dct_title@') ], ["Hello"], "query from property map"; } done_testing; example.json100644001750001750 75412703377652 14263 0ustar00vojvoj000000000000RDF-aREF-0.27/t{ "_ns": { "dct": "http://purl.org/dc/terms/", "foaf": "http://xmlns.com/foaf/0.1/" }, "_id": "http://example.com/people#alice", "a": "foaf:Person", "foaf_name": "Alice Smisth", "foaf_age": "42^xsd_integer", "foaf_homepage": [ "http://personal.example.org/~alice/", "http://work.example.com/asmith/" ], "foaf_knows": { "_id": "_:1", "foaf_name": "John", "dct_description": "a nice guy@en" } } RDF000755001750001750 012703377652 12525 5ustar00vojvoj000000000000RDF-aREF-0.27/libaREF.pm100644001750001750 1455112703377652 14026 0ustar00vojvoj000000000000RDF-aREF-0.27/lib/RDFpackage RDF::aREF; use strict; use warnings; use v5.10; our $VERSION = '0.27'; use RDF::aREF::Query; use RDF::aREF::Decoder; use RDF::aREF::Encoder; use Scalar::Util qw(blessed reftype); use Carp qw(croak); use parent 'Exporter'; our @EXPORT = qw(decode_aref encode_aref aref_query aref_query_map); our %EXPORT_TAGS = (all => [@EXPORT]); our @CARP_NOT = qw(RDF::aREF::Query RDF::aREF::Decoder RDF::aREF::Encoder); sub decode_aref(@) { ## no critic my ($aref, %options) = @_; RDF::aREF::Decoder->new(%options)->decode($aref); } sub encode_aref(@) { ## no critic my ($source, %options) = @_; my $encoder = RDF::aREF::Encoder->new(%options); my $aref = $options{to} // {}; if (blessed $source and $source->isa('RDF::Trine::Iterator')) { $encoder->add_iterator( $source, $aref ); } elsif (blessed $source and $source->DOES('Attean::API::TripleIterator')) { $encoder->add_iterator( $source, $aref ); } elsif (blessed $source and $source->isa('RDF::Trine::Model')) { $encoder->add_iterator( $source->as_stream, $aref ); } elsif (blessed $source and $source->DOES('Attean::API::TripleStore')) { $encoder->add_iterator( $source->get_triples, $aref ); } elsif (ref $source and reftype $source eq 'HASH') { $encoder->add_hashref( $source, $aref ); } elsif (!ref $source) { eval { require RDF::Trine::Model; require RDF::Trine::Parser }; croak "RDF::Trine missing: encoding aREF from URL or file not supported!" if $@; my $model = RDF::Trine::Model->new; # TODO: directly use iterator if ($source =~ qr{^https?://}) { RDF::Trine::Parser->parse_url_into_model($source, $model); } elsif (-f $source) { my $parser = RDF::Trine::Parser->guess_parser_by_filename($source); $parser->parse_file_into_model("file://$source", $source, $model) } else { croak 'invalid RDF graph, given as string'; } $encoder->add_iterator( $model->as_stream, $aref ); } return $aref; } sub aref_query(@) { ## no critic my ($graph, $origin, @queries) = @_ < 3 ? ($_[0], undef, $_[1]) : @_; RDF::aREF::Query->new( query => join '|', @queries )->apply($graph, $origin); } sub aref_query_map(@) { ## no critic my ($graph, $origin, $map) = @_ < 3 ? ($_[0], undef, $_[1]) : @_; my %record; while (my ($query, $field) = each %$map) { my @values = aref_query( $origin ? ($graph, $origin, $query) : ($graph, $query) ); if (@values) { if ($record{$field}) { if (ref $record{$field}) { push @{$record{$field}}, @values; } else { $record{$field} = [ $record{$field}, @values ]; } } else { $record{$field} = @values > 1 ? \@values : $values[0]; } } } \%record; } 1; __END__ =head1 NAME RDF::aREF - Another RDF Encoding Form =begin markdown # STATUS [![Build Status](https://travis-ci.org/nichtich/RDF-aREF.png)](https://travis-ci.org/nichtich/RDF-aREF) [![Coverage Status](https://coveralls.io/repos/nichtich/RDF-aREF/badge.png)](https://coveralls.io/r/nichtich/RDF-aREF) [![Kwalitee Score](http://cpants.cpanauthors.org/dist/RDF-aREF.png)](http://cpants.cpanauthors.org/dist/RDF-aREF) =end markdown =head1 SYNOPSIS use RDF::aREF; my $rdf = { _id => 'http://example.com/people#alice', foaf_name => 'Alice Smith', foaf_age => '42^xsd_integer', foaf_homepage => [ { _id => 'http://personal.example.org/', dct_modified => '2010-05-29^xsd_date', }, 'http://work.example.com/asmith/', ], foaf_knows => { dct_description => 'a nice guy@en', }, }; decode_aref( $rdf, callback => sub { my ($subject, $predicate, $object, $language, $datatype) = @_; ... } ); my @lastmod = aref_query $rdf, 'foaf_homepage.dct_modified^'; my $model = RDF::Trine::Model->new; decode_aref( $rdf, callback => $model ); print RDF::Trine::Serializer->new('Turtle')->serialize_model_to_string($model); my $model = RDF::Trine::Model->new; RDF::Trine::Parser->parse_url_into_model($url, $model); my $aref = encode_aref $model; =head1 DESCRIPTION B (L) is an encoding of RDF graphs in form of arrays, hashes, and Unicode strings. This module provides methods for decoding from aREF data to RDF triples (L), for encoding RDF data in aREF (L), and for querying parts of an RDF graph (L). =head1 EXPORTED FUNCTIONS The following functions are exported by default. =head2 decode_aref $aref [, %options ] Decodes an aREF document given as hash reference with L. Equivalent to C<< RDF::aREF::Decoder->new(%options)->decode($aref) >>. =head2 encode_aref $graph [, %options ] Construct an aREF subject mapfrom an RDF graph. The L for possible options. The C<$graph> can be supplied as: =over =item instance of L =item instance of L =item an URL or a filename (only if L is installed) =item instance of L (experimental) =item instance of L (experimental) =item hash reference with L format (as returned by method C in L) =back =head2 aref_query $graph, [ $origin ], @queries Query parts of an aREF data structure by L and return a list. See L for details. =head2 aref_query_map( $graph, [ $origin ], $query_map ) Map parts of an aREF data structure to a flat key-value structure. =head1 SEE ALSO =over =item aREF is specified at L. =item See L for an application of this module. =item Usee L for more elaborated handling of RDF data in Perl. =item See L for a similar (outdated) RDF encoding in YAML. =back =head1 COPYRIGHT AND LICENSE Copyright Jakob Voss, 2014- This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut encode_aref.t100644001750001750 271112703377652 14367 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use warnings; use Test::More; use RDF::aREF; use RDF::aREF::Encoder; my $example = { # RDF/JSON "http://example.org/about" => { "http://purl.org/dc/terms/title" => [ { value => "Anna's Homepage", type => "literal", lang => "en" } ] } }; my $aref = encode_aref($example, ns => '20140910'); is_deeply $aref, { _id => "http://example.org/about", dct_title => "Anna's Homepage\@en" }, 'add_hashref'; encode_aref( { "http://example.org/about" => { "x:y" => [{ "type" => "uri", value => "x:z" }] } }, subject_map => 1, to => $aref ); is_deeply $aref, { 'http://example.org/about' => { dct_title => "Anna's Homepage\@en", "x:y" => "", } }, 'encode_aref( $rdfjson, subject_map => 1, to => $aref )'; # TODO: normalize/avoid duplicate triples is_deeply encode_aref($example, ns => 0), { _id => "http://example.org/about", "http://purl.org/dc/terms/title" => "Anna's Homepage\@en" }, 'encode_aref( $rdfjson, ns => 0)'; is_deeply encode_aref($example, ns => 0, subject_map => 1), { "http://example.org/about" => { "http://purl.org/dc/terms/title" => "Anna's Homepage\@en" } }, 'encode_aref( $rdfjson, ns => 0, subject_map => 1)'; { my $encoder = RDF::aREF::Encoder->new( ns => '20140910' ); is $encoder->literal('x', '', 'http://www.w3.org/2001/XMLSchema#string'), 'x@', 'omit xsd_string'; } done_testing; decode-errors.t100644001750001750 355512703377652 14701 0ustar00vojvoj000000000000RDF-aREF-0.27/tuse strict; use warnings; use Test::More; use RDF::aREF::Decoder; use Scalar::Util qw(reftype); sub check_errors(@) { my $errors = pop; my %options = @_; my $msg = delete $options{msg}; my $decoder = RDF::aREF::Decoder->new( complain => 2, %options ); local $Test::Builder::Level = $Test::Builder::Level + 1; while (@$errors) { my $aref = shift @$errors; eval { $decoder->decode( $aref ) }; if (@$errors and reftype $errors->[0] eq reftype qr//) { my $expect = shift @$errors; like($@, $expect, ($msg // $expect)); } else { ok $@, $msg // ''; } } } check_errors [ { _ns => [] }, => qr{^namespace map must be map or string}, # invalid subjects { [] => { a => 'foaf:Person' } } => qr{^invalid subject: ARRAY\(}, { '' => { a => 'foaf:Person' } } => qr{^invalid subject}, # invalid predicates { 'x:subject' => { \"" => "" } } => qr{^invalid predicate IRI SCALAR\(}, # TODO: check different forms of same IRI # invalid objects { 'x:subject' => { a => \"" } } => qr{^object must not be reference to SCALAR}, { 'x:subject' => { a => [ \"" ] } } => qr{^object must not be reference to SCALAR}, { _ns => { 1 => 'http://example.org/' }, 'x:subject' => { a => 'foaf:Person' } } => qr{^invalid prefix: 1}, { _ns => { x => 'foo' }, 'x:subject' => { a => 'foaf:Person' } } => qr{^invalid namespace: foo} ]; check_errors strict => 1, msg => 'strict makes undef error', [ { '' => { a => undef } } => qr{.} ]; check_errors strict => 1, null => '', msg => 'strict makes null value error', [ { '' => { a => 'foaf_Person' } } ]; check_errors strict => 1, msg => 'empty string not null by default', [ { '' => { a => 'foaf_Person' } } ]; done_testing; suite000755001750001750 012703377652 12740 5ustar00vojvoj000000000000RDF-aREF-0.27/tREADME.md100644001750001750 46412703377652 14343 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteThis directory contains a test suite with test cases for aREF decoding. Each example consists of an aREF document in JSON (with extension `.json`) and an expected result, as RDF graph in form of canonical N-Triples (with extension `.nt`) and/or sorted list of expected error message (with extension `.err`). blanks.nt100644001750001750 7612703377652 14660 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite_:g2 _:g1 . doi-example.json100644001750001750 543712703377652 15057 0ustar00vojvoj000000000000RDF-aREF-0.27/t{ "http://id.crossref.org/contributor/kiyoshi-mabuchi-y2ol1uygjx72" : { "foaf_name" : [ "Kiyoshi Mabuchi@en" ], "a" : [ "foaf_Person" ], "foaf_familyName" : [ "Mabuchi@" ], "foaf_givenName" : [ "Kiyoshi@" ] }, "http://id.crossref.org/contributor/daichi-uchijima-y2ol1uygjx72" : { "foaf_givenName" : [ "Daichi@" ], "foaf_familyName" : [ "Uchijima@" ], "a" : [ "foaf_Person" ], "foaf_name" : [ "Daichi Uchijima@en" ] }, "http://dx.doi.org/10.2474/trol.7.147" : { "prism21_volume" : [ "7@" ], "owl_sameAs" : [ "doi:10.2474/trol.7.147", "info:doi/10.2474/trol.7.147" ], "prism21_doi" : [ "10.2474/trol.7.147@" ], "bibo_doi" : [ "10.2474/trol.7.147@" ], "dct_date" : [ "2012^xsd_gYear" ], "prism21_startingPage" : [ "147@" ], "bibo_pageStart" : [ "147@" ], "bibo_volume" : [ "7@" ], "dct_title" : [ "Frictional Coefficient under Banana Skin@" ], "dct_creator" : [ "", "", "http://id.crossref.org/contributor/kiyoshi-mabuchi-y2ol1uygjx72", "http://id.crossref.org/contributor/rina-sakai-y2ol1uygjx72" ], "dct_publisher" : [ "Japanese Society of Tribologists@", "http://d-nb.info/gnd/5027072-2" ], "dct_isPartOf" : [ "" ], "prism21_endingPage" : [ "151@" ], "bibo_pageEnd" : [ "151@" ], "dct_identifier" : [ "10.2474/trol.7.147@" ] }, "http://id.crossref.org/contributor/rina-sakai-y2ol1uygjx72" : { "foaf_familyName" : [ "Sakai@" ], "foaf_givenName" : [ "Rina@" ], "foaf_name" : [ "Rina Sakai@en" ], "a" : [ "foaf_Person" ] }, "http://id.crossref.org/contributor/kensei-tanaka-y2ol1uygjx72" : { "a" : [ "foaf_Person" ], "foaf_name" : [ "Kensei Tanaka@en" ], "foaf_givenName" : [ "Kensei@" ], "foaf_familyName" : [ "Tanaka@" ] }, "http://id.crossref.org/issn/1881-2198" : { "bibo_issn" : [ "1881-2198@" ], "a" : [ "bibo_Journal" ], "prism21_issn" : [ "1881-2198@" ], "dct_title" : [ "Tribology Online@" ], "owl_sameAs" : [ "urn:issn:1881-2198" ] } } blanks.json100644001750001750 2512703377652 15202 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"_:x":{"a":"_:y"}} aREF000755001750001750 012703377652 13302 5ustar00vojvoj000000000000RDF-aREF-0.27/lib/RDFQuery.pm100644001750001750 1404712703377652 15133 0ustar00vojvoj000000000000RDF-aREF-0.27/lib/RDF/aREFpackage RDF::aREF::Query; use strict; use warnings; use v5.10; our $VERSION = '0.27'; use RDF::aREF::Decoder qw(qName languageTag); use Carp qw(croak); use RDF::NS; sub new { my ($class, %options) = @_; my $expression = $options{query} // croak "query required"; my $ns = $options{ns} // RDF::NS->new; my $decoder = $options{decoder} // RDF::aREF::Decoder->new( ns => $ns ); my $self = bless { items => [], decoder => $decoder }, $class; my @items = split /\s*\|\s*/, $expression; foreach my $expr ( @items ? @items : '' ) { my $type = 'any'; my ($language, $datatype); if ($expr =~ /^(.*)\.$/) { $type = 'resource'; $expr = $1; } elsif ( $expr =~ /^([^@]*)@([^@]*)$/ ) { ($expr, $language) = ($1, $2); if ( $language eq '' or $language =~ languageTag ) { $type = 'literal'; } else { croak 'invalid languageTag in aREF query'; } } elsif ( $expr =~ /^([^^]*)\^([^^]*)$/ ) { # TODO: support explicit IRI ($expr, $datatype) = ($1, $2); if ( $datatype =~ qName ) { $type = 'literal'; $datatype = $decoder->prefixed_name( split '_', $datatype ); $datatype = undef if $datatype eq $decoder->prefixed_name('xsd','string'); } else { croak 'invalid datatype qName in aREF query'; } } my @path = split /\./, $expr; foreach (@path) { croak "invalid aref path expression: $_" if $_ !~ qName and $_ ne 'a'; } push @{$self->{items}}, { path => \@path, type => $type, language => $language, datatype => $datatype, }; } $self; } sub query { my ($self) = @_; join '|', map { my $q = join '.', @{$_->{path}}; if ($_->{type} eq 'literal') { if ($_->{datatype}) { $q .= '^' . $_->{datatype}; } else { $q .= '@' . ($_->{language} // ''); } } elsif ($_->{type} eq 'resource') { $q .= '.'; } $q; } @{$self->{items}} } sub apply { my ($self, $rdf, $subject) = @_; map { $self->_apply_item($_, $rdf, $subject) } @{$self->{items}}; } sub _apply_item { my ($self, $item, $rdf, $subject) = @_; my $decoder = $self->{decoder}; # TODO: Support RDF::Trine::Model # TODO: try abbreviated *and* full URI? my @current = $rdf; if ($subject) { if ($rdf->{_id}) { return if $rdf->{_id} ne $subject; } else { @current = ($rdf->{$subject}); } } my @path = @{$item->{path}}; if (!@path and $item->{type} ne 'resource') { if ($item->{type} eq 'any') { return ($subject ? $subject : $rdf->{_id}); } } while (my $field = shift @path) { # get objects in aREF @current = grep { defined } map { (ref $_ and ref $_ eq 'ARRAY') ? @$_ : $_ } map { $_->{$field} } @current; return if !@current; if (@path or $item->{type} eq 'resource') { # get resources @current = grep { defined } map { $decoder->resource($_) } @current; if (@path) { # TODO: only if RDF given as predicate map! @current = grep { defined } map { $rdf->{$_} } @current; } } } # last path element @current = grep { defined } map { $decoder->object($_) } @current; if ($item->{type} eq 'literal') { @current = grep { @$_ > 1 } @current; if ($item->{language}) { # TODO: use language tag substring @current = grep { $_->[1] and $_->[1] eq $item->{language} } @current; } elsif ($item->{datatype}) { # TODO: support qName and explicit IRI @current = grep { $_->[2] and $_->[2] eq $item->{datatype} } @current; } } map { $_->[0] } @current; # IRI or string value } 1; __END__ =head1 NAME RDF::aREF::Query - aREF query expression =head1 SYNOPSIS my $rdf = { 'http://example.org/book' => { dct_creator => [ 'http://example.org/alice', 'http://example.org/bob' ] }, 'http://example.org/alice' => { foaf_name => "Alice" }, 'http://example.org/bob' => { foaf_name => "Bob" } }; my $getnames = RDF::aREF::Query->new( query => 'dct_creator.foaf_name' ); my @names = $getnames->apply( $rdf, 'http://example.org/boo' ); $getnames->query; # 'dct_creator.foaf_name' use RDF::aREF qw(aref_query_map); my $record = aref_query_map( $rdf, $publication, { 'dct_creator@' => 'creator', 'dct_creator.foaf_name' => 'creator', }); =head1 DESCRIPTION Implements L, a query language to access strings and nodes from agiven RDF graph. See also functions C and C in L for convenient application. =head1 CONFIGURATION The constructor expects the following options: =over =item query L expression =item decoder Instance of L to map qNames to URIs. A new instance is created unless given. =item ns Optional namespace map (L), passed to the constructor of L if no decoder is given. =back =head1 METHODS =head1 apply( $graph [, $origin ] ) Perform the query on a given RDF graph. The graph can be given as aREF structure (subject map or predicate map) or as instance of L. An origin subject node must be provided unless the RDF graph is provided as L. =head1 query Returns the aREF query expression =head1 SEE ALSO Use SPARQL for more complex queries, e.g. with L. =cut json-ld-14.nt100644001750001750 34712703377652 15217 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite . . json-ld-23.nt100644001750001750 43712703377652 15217 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite "2010-05-29T14:17:39+02:00"^^ . . release-pod-syntax.t100644001750001750 45612703377652 15645 0ustar00vojvoj000000000000RDF-aREF-0.27/t#!perl BEGIN { unless ($ENV{RELEASE_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for release candidate testing'); } } # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use Test::More; use Test::Pod 1.41; all_pod_files_ok(); Decoder.pm100644001750001750 3344612703377652 15377 0ustar00vojvoj000000000000RDF-aREF-0.27/lib/RDF/aREFpackage RDF::aREF::Decoder; use strict; use warnings; use v5.10; our $VERSION = '0.27'; use RDF::NS; use Carp qw(croak carp); use Scalar::Util qw(refaddr reftype blessed); use parent 'Exporter'; our @EXPORT_OK = qw(prefix localName qName blankNode blankNodeIdentifier IRIlike languageString languageTag datatypeString); our ($PREFIX, $NAME); BEGIN { my $nameChar = 'A-Z_a-z\N{U+00C0}-\N{U+00D6}\N{U+00D8}-\N{U+00F6}\N{U+00F8}-\N{U+02FF}\N{U+0370}-\N{U+037D}\N{U+037F}-\N{U+1FFF}\N{U+200C}-\N{U+200D}\N{U+2070}-\N{U+218F}\N{U+2C00}-\N{U+2FEF}\N{U+3001}-\N{U+D7FF}\N{U+F900}-\N{U+FDCF}\N{U+FDF0}-\N{U+FFFD}\N{U+10000}-\N{U+EFFFF}'; my $nameStartChar = $nameChar.'0-9\N{U+00B7}\N{U+0300}\N{U+036F}\N{U+203F}-\N{U+2040}-'; our $PREFIX = '[a-z][a-z0-9]*'; our $NAME = "[$nameStartChar][$nameChar]*"; } use constant localName => qr/^$NAME$/; use constant prefix => qr/^$PREFIX$/; use constant qName => qr/^($PREFIX)_($NAME)$/; use constant blankNodeIdentifier => qr/^([a-zA-Z0-9]+)$/; use constant blankNode => qr/^_:([a-zA-Z0-9]+)$/; use constant IRIlike => qr/^[a-z][a-z0-9+.-]*:/; use constant languageString => qr/^(.*)@([a-z]{2,8}(-[a-z0-9]{1,8})*)$/i; use constant languageTag => qr/^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i; use constant datatypeString => qr/^(.*?)[\^] ((($PREFIX)?_($NAME))|<([a-z][a-z0-9+.-]*:.*)>)$/x; use constant explicitIRIlike => qr/^<(.+)>$/; use constant xsd_string => 'http://www.w3.org/2001/XMLSchema#string'; sub new { my ($class, %options) = @_; my $self = bless { ns => $options{ns}, complain => $options{complain} // 1, strict => $options{strict} // 0, null => $options{null}, # undef by default bnode_prefix => $options{bnode_prefix} || 'b', bnode_count => $options{bnode_count} || 0, bnode_map => { }, }, $class; # facilitate use of this module together with RDF::Trine my $callback = $options{callback} // sub { }; if (blessed $callback and $callback->isa('RDF::Trine::Model')) { require RDF::Trine::Statement; my $model = $callback; $callback = sub { eval { $model->add_statement( trine_statement(@_) ) }; $self->error($@) if $@; }; } $self->{callback} = $callback; return $self; } sub namespace_map { # sets the local namespace map my ($self, $map) = @_; # TODO: copy on write because this is expensive! # copy default namespace map # TODO: respect '_' and default map! my $ns = ref $self->{ns} ? bless { %{$self->{ns}} }, 'RDF::NS' : RDF::NS->new($self->{ns}); if (ref $map) { if (ref $map eq 'HASH') { while (my ($prefix,$namespace) = each %$map) { $prefix = '' if $prefix eq '_'; if ($prefix !~ prefix) { $self->error("invalid prefix: $prefix"); } elsif ($namespace !~ IRIlike) { $self->error("invalid namespace: $namespace"); } else { $ns->{$prefix} = $namespace; } } } else { $self->error("namespace map must be map or string"); } } $self->{ns} = $ns; } sub decode { my ($self, $map, %options) = @_; unless ($options{keep_bnode_map}) { $self->{bnode_map} = { }; } $self->{visited} = { }; $self->namespace_map( $map->{"_ns"} ); if (exists $map->{_id}) { # predicate map my $id = $map->{_id}; return if $self->is_null($id,'_id'); my $subject = $id ne '' ? $self->expect_resource($id,'subject') : undef; if (defined $subject and $subject ne '') { $self->predicate_map( $subject, $map ); } elsif ($self->{strict}) { $self->error("invalid subject", $id); } } else { # 3.4.1 subject maps foreach my $key (grep { $_ ne '_' and $_ !~ /^_[^:]/ } keys %$map) { next if $self->is_null($key,'subject'); my $subject = $self->subject($key); if (!$subject) { $self->error("invalid subject", $key); next; } my $predicates = $map->{$key}; if (exists $predicates->{_id} and ($self->resource($predicates->{_id}) // '') ne $subject) { $self->error("subject _id must be <$subject>"); } else { $self->predicate_map( $subject, $predicates ); } } } } sub predicate_map { my ($self, $subject, $map) = @_; $self->{visited}{refaddr $map} = 1; for (keys %$map) { next if $_ eq '_id' or $_ eq '_ns'; my $predicate = do { if ($_ eq 'a') { 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; } elsif ( $_ =~ /^<(.+)>$/ ) { $self->iri($1); } elsif ( $_ =~ qName ) { $self->prefixed_name($1,$2); } elsif ( $_ =~ IRIlike ) { $self->iri($_); } else { $self->error("invalid predicate IRI $_") if $_ ne '' or $self->{strict}; next; } } or next; my $value = $map->{$_}; # encoded_object foreach my $o (ref $value eq 'ARRAY' ? @$value : $value) { if ($self->is_null($o,'object')) { next; } elsif (!ref $o) { if (my $object = $self->object($o)) { $self->triple( $subject, $predicate, $object ); } next; } elsif (ref $o eq 'HASH') { my $object = exists $o->{_id} ? ($self->expect_resource($o->{_id},'object _id') // next) : $self->blank_identifier(); $self->triple( $subject, $predicate, [$object] ); unless( ref $object and $self->{visited}{refaddr $object} ) { $self->predicate_map( $object, $o ); } } else { $self->error('object must not be reference to '.ref $o); } } } } sub is_null { my ($self, $value, $check) = @_; if ( !defined $value or (defined $self->{null} and $value eq $self->{null} ) ) { if ($check and $self->{strict}) { $self->error("$check must not be null") } 1; } else { 0; } } sub expect_resource { my ($self, $r, $expect) = @_; if (my $resource = $self->resource($r)) { return $resource; } else { if (!$self->is_null($r, $expect)) { $expect .= ": " . (ref $r ? reftype $r : $r); $self->error("invalid $expect"); } return; } } sub resource { my ($self, $r) = @_; return unless defined $r; if ( $r =~ explicitIRIlike ) { $self->iri($1); } elsif ( $r =~ blankNode ) { $self->blank_identifier($1); } elsif ( $r =~ qName ) { $self->prefixed_name($1,$2); } elsif ( $r =~ IRIlike ) { $self->iri($r); } else { undef } } sub subject { # plain IRI, qName, or blank node my ($self, $s) = @_; return unless defined $s; if ( $s =~ IRIlike ) { $self->iri($s); } elsif ( $s =~ qName ) { $self->prefixed_name($1,$2); } elsif ( $s =~ blankNode ) { $self->blank_identifier($1); } else { undef } } sub object { my ($self, $o) = @_; if (!defined $o) { undef; } elsif ( $o =~ explicitIRIlike ) { [$self->iri($1)]; } elsif ( $o =~ blankNode ) { [$self->blank_identifier($1)]; } elsif ( $o =~ qName ) { [$self->prefixed_name($1,$2)]; } elsif ( $o =~ languageString ) { [$1, lc($2)]; } elsif ( $o =~ /^(.*)[@]$/ ) { [$1, undef]; } elsif ( $o =~ datatypeString ) { if ($6) { my $datatype = $self->iri($6) // return; if ($datatype eq xsd_string) { [$1,undef]; } else { [$1,undef,$datatype]; } } else { my $datatype = $self->prefixed_name($4,$5) // return; if ($datatype eq xsd_string) { [$1,undef]; } else { [$1,undef,$datatype]; } } } elsif ( $o =~ IRIlike ) { [$self->iri($o)]; } else { [$o, undef]; } } sub plain_literal { my ($self, $object) = @_; my $obj = $self->object($object); return if @$obj == 1; # resource or blank return $obj->[0]; } sub iri { my ($self, $iri) = @_; # TODO: check full RFC form of IRIs if ( $iri !~ IRIlike ) { return $self->error("invalid IRI $iri"); } else { return $iri; } } sub prefixed_name { my ($self, $prefix, $name) = @_; my $base = $self->{ns}{$prefix // ''} // return $self->error( $prefix // '' ne '' ? "unknown prefix: $prefix" : "not an URI: $name"); $self->iri($base.$name); } sub triple { my $self = shift; my $subject = ref $_[0] ? '_:'.${$_[0]} : $_[0]; my $predicate = $_[1]; my @object = @{$_[2]}; $object[0] = '_:'.${$object[0]} if ref $object[0]; $self->{callback}->($subject, $predicate, @object); } sub error { my ($self, $message, $value, $context) = @_; # TODO: include $context (bless $message, 'RDF::aREF::Error') if (defined $value) { $message .= ': ' . (ref $value ? reftype $value : $value); } if (!$self->{complain}) { return; } elsif ($self->{complain} == 1) { carp $message; } else { croak $message; } } sub bnode_count { $_[0]->{bnode_count} = $_[1] if @_ > 1; $_[0]->{bnode_count}; } # TODO: test this sub blank_identifier { my ($self, $id) = @_; my $bnode; if ( defined $id ) { $bnode = ($self->{bnode_map}{$id} //= $self->{bnode_prefix} . ++$self->{bnode_count}); } else { $bnode = $self->{bnode_prefix} . ++$self->{bnode_count}; } return \$bnode; } sub clean_bnodes { my ($self) = @_; $self->{bnode_count} = 0; $self->{bnode_map} = {}; } # TODO: test this sub trine_statement { RDF::Trine::Statement->new( # subject (substr($_[0],0,2) eq '_:' ? RDF::Trine::Node::Blank->new(substr $_[0], 2) : RDF::Trine::Node::Resource->new($_[0])), # predicate RDF::Trine::Node::Resource->new($_[1]), # object do { if (@_ == 3) { if (substr($_[2],0,2) eq '_:') { RDF::Trine::Node::Blank->new(substr $_[2], 2); } else { RDF::Trine::Node::Resource->new($_[2]); } } else { RDF::Trine::Node::Literal->new($_[2],$_[3],$_[4]); } } ); } 1; __END__ =head1 NAME RDF::aREF::Decoder - decode another RDF Encoding Form (to RDF triples) =head1 SYNOPSIS use RDF::aREF::Decoder; RDF::aREF::Decoder->new( %options )->decode( $aref ); =head1 DESCRIPTION This module implements a decoder from another RDF Encoding Form (aREF), given as in form of Perl arrays, hashes, and Unicode strings, to RDF triples. =head1 OPTIONS =head2 ns A default namespace map, given either as hash reference or as version string of module L. Set to the most recent version of RDF::NS by default, but relying on a default value is not recommended! =head2 callback A code reference that is called for each triple with a list of three to five elements: =over =item subject The subject IRI or subject blank node as string. Blank nodes always start with C<_:>. =item predicate The predicate IRI. =item object The object IRI or object blank node or literal object as string. Blank nodes always start with C<_:> and literal objects can be detected by existence of the (possibly empty) language or datatype element. =item language The language tag (possible the empty string) for literal objects. =item datatype The object's datatype if object is a literal and datatype is not C. =back For convenience an instance of L can also be used as callback. =head2 complain What to do on errors. Set to 1 be default (warn). Set to 0 to ignore. Other values will die on errors. =head2 strict Enables errors on undefined subjects, predicates, and objects. By default the Perl value C in any part of an encoded RDF triple will silently ignore the triple, so aREF structures can easily be used as templates with optional values. =head2 null A null object that is treated equivalent to C if found as object. For instance setting this to the empty string will ignore all triples with the empty string as literal value. =head2 bnode_prefix A prefix for blank node identifiers. Defaults to "b", so blank node identifiers will be "b1", "b2", "b3" etc. =head2 bnode_count An integer to start creating blank node identifiers with. The default value "0" results in blank node identifiers starting from "b1". This option can be useful to avoid collision of blank node identifiers when merging multiple aREF instances. The current counter value is accessible as accessor. =head1 METHODS =head2 decode( $aref [, keep_bnode_map => 1 ] ) Encode RDF data given in aREF. Resets all blank node identifier mappings unless option c is set. =head2 clean_bnodes Delete blank node identifier mapping and reset bnode_count. =head1 EXPORTABLE CONSTANTS On request this module exports the following regular expressions, as defined in the L: =over =item qName =item blankNode =item IRIlike =item languageString =item languageTag =item datatypeString =back =head1 SEE ALSO L =cut Encoder.pm100644001750001750 2465312703377652 15411 0ustar00vojvoj000000000000RDF-aREF-0.27/lib/RDF/aREFpackage RDF::aREF::Encoder; use strict; use warnings; use v5.10; our $VERSION = '0.27'; use RDF::NS; use RDF::aREF::Decoder qw(localName blankNodeIdentifier); use Scalar::Util qw(blessed reftype); use Carp qw(croak); sub new { my ($class, %options) = @_; if (!defined $options{ns}) { $options{ns} = RDF::NS->new; } elsif (!$options{ns}) { $options{ns} = bless { rdf => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', rdfs => 'http://www.w3.org/2000/01/rdf-schema#', owl => 'http://www.w3.org/2002/07/owl#', xsd => 'http://www.w3.org/2001/XMLSchema#', }, 'RDF::NS'; } elsif ( !blessed $options{ns} or !$options{ns}->isa('RDF::NS') ) { $options{ns} = RDF::NS->new($options{ns}); } $options{sn} = $options{ns}->REVERSE; $options{subject_map} = !!$options{subject_map}; if ($options{NFC}) { eval { require Unicode::Normalize }; croak "Missing Unicode::Normalize: NFC normalization disabled!\n" if $@; } bless \%options, $class; } sub qname { my ($self, $uri) = @_; return unless $self->{sn}; my @qname = $self->{sn}->qname($uri); return $qname[0] if @qname == 1; return join('_',@qname) if @qname and $qname[1] =~ localName; return; } sub uri { my ($self, $uri) = @_; if ( my $qname = $self->qname($uri) ) { return $qname; } else { return "<$uri>"; } } sub subject { my ($self, $subject) = @_; return do { if (!reftype $subject) { undef # RDF/JSON } elsif (reftype $subject eq 'HASH') { if ($subject->{type} eq 'uri' or $subject->{type} eq 'bnode') { $subject->{value} } # RDF::Trine::Node } elsif (reftype $subject eq 'ARRAY') { if (@$subject == 2 ) { if ($subject->[0] eq 'URI') { "".$subject->[1]; } elsif ($subject->[0] eq 'BLANK') { $self->bnode($subject->[1]) } } } }; } sub predicate { my ($self, $predicate) = @_; $predicate = do { if (!reftype $predicate) { undef # RDF/JSON } elsif (reftype $predicate eq 'HASH' and $predicate->{type} eq 'uri') { $predicate->{value} # RDF::Trine::Node } elsif (reftype $predicate eq 'ARRAY') { (@$predicate == 2 and $predicate->[0] eq 'URI') ? "".$predicate->[1] : undef; } }; return do { if ( !defined $predicate ) { undef } elsif ( $predicate eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' ) { 'a' } elsif ( my $qname = $self->qname($predicate) ) { $qname } else { $predicate } }; } sub object { my ($self, $object) = @_; return do { if (!reftype $object) { undef # RDF/JSON } elsif (reftype $object eq 'HASH') { if ($object->{type} eq 'literal') { $self->literal( $object->{value}, $object->{lang}, $object->{datatype} ) } elsif ($object->{type} eq 'bnode') { $object->{value} } else { $self->uri($object->{value}) } # RDF::Trine::Node } elsif (reftype $object eq 'ARRAY') { if (@$object != 2 ) { $self->literal(@$object) } elsif ($object->[0] eq 'URI') { $self->uri("".$object->[1]) } elsif ($object->[0] eq 'BLANK') { $self->bnode($object->[1]) } } }; } sub literal { my ($self, $value, $language, $datatype) = @_; if ($self->{NFC}) { $value = Unicode::Normalize::NFC($value); } if ($language) { $value.'@'.$language } elsif ($datatype and $datatype ne 'http://www.w3.org/2001/XMLSchema#string') { $value.'^'.$self->uri($datatype) } else { $value.'@' } } sub bnode { $_[1] =~ blankNodeIdentifier ? '_:'.$_[1] : undef; } sub triple { my ($self, $subject, $predicate, $object, $aref) = @_; $subject = $self->subject($subject) // return; $predicate = $self->predicate($predicate) // return; $object = $self->object($object) // return; $aref //= { }; # empty if ( !keys %$aref and !$self->{subject_map} ) { $aref->{_id} = $subject; $aref->{$predicate} = $object; # predicate map } elsif ( $aref->{_id} ) { if ( $aref->{_id} eq $subject and !$self->{subject_map} ) { $self->_add_object_to_predicate_map( $aref, $predicate, $object ); } else { # convert predicate map to subject map my $s = delete $aref->{_id}; my $pm = { }; foreach (keys %$aref) { $pm->{$_} = delete $aref->{$_}; } if ($s eq $subject) { $self->_add_object_to_predicate_map( $pm, $predicate, $object ); } else { $aref->{$subject} = { $predicate => $object }; } $aref->{$s} = $pm; } } else { # subject map if ( $aref->{$subject} ) { $self->_add_object_to_predicate_map( $aref->{$subject}, $predicate, $object ); } else { $aref->{$subject} = { $predicate => $object }; } } return $aref; } sub _add_object_to_predicate_map { my ($self, $map, $predicate, $object) = @_; if (ref $map->{$predicate}) { push @{$map->{$predicate}}, $object; } elsif (defined $map->{$predicate}) { $map->{$predicate} = [ $map->{$predicate}, $object ]; } else { $map->{$predicate} = $object; } } sub add_iterator { my ($self, $iterator, $aref) = @_; while (my $s = $iterator->next) { $self->triple($s->subject, $s->predicate, $s->object, $aref); } } sub add_hashref { my ($self, $hashref, $aref) = @_; while (my ($s,$ps) = each %$hashref) { my $subject = $s =~ /^_:/ ? ['BLANK',substr($s, 2)] : ['URI',$s]; foreach my $p (keys %$ps) { my $predicate = ['URI',$p]; foreach my $object (@{ $hashref->{$s}->{$p} }) { $self->triple($subject, $predicate, $object, $aref); } } } } 1; __END__ =head1 NAME RDF::aREF::Encoder - encode RDF to another RDF Encoding Form =head1 SYNOPSIS use RDF::aREF::Encoder; my $encoder = RDF::aREF::Encoder->new; # encode parts of aREF my $qname = $encoder->qname('http://schema.org/Review'); # 'schema_Review' my $predicate = $encoder->predicate({ type => 'uri', value => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' }); # 'a' my $object = $encoder->object({ type => 'literal', value => 'hello, world!', lang => 'en' }); # 'hello, world!@en' # method also accepts RDF::Trine::Node instances my $object = $encoder->object( RDF::Trine::Resource->new($iri) ); # encode RDF graphs (see also function 'encode_aref' in RDF::aREF) use RDF::Trine::Parser; my $aref = { }; RDF::Trine::Parser->parse_file ( $base_uri, $fh, sub { my $s = shift; $encoder->triple( $s->subject, $s->predicate, $s->object, $aref ); } ); =head1 DESCRIPTION This module provides methods to encode RDF data in another RDF Encoding Form (aREF). As aREF was designed to facilitate creation of RDF data, it may be easier to create aREF "by hand" instead of using this module! =head1 OPTIONS =head2 ns A default namespace map, given as version string of module L for stable qNames or as instance of L. The most recent installed version of L is used by default. The value C<0> can be used to only use required namespace mappings (rdf, rdfs, owl and xsd). =head2 subject_map By default RDF graphs with common subject are encoded as aREF predicate map: { _id => $subject, $predicate => $object } Enable this option to always encode as aREF subject map: { $subject => { $predicate => $object } } =head1 METHODS Note that no syntax checking is applied, e.g. whether a given URI is a valid URI or whether a given language is a valid language tag! =head2 qname( $uri ) Abbreviate an URI as qName or return C. For instance C is abbreviated to "C". =head2 uri( $uri ) Abbreviate an URI or as qName or enclose it in angular brackets. =head2 literal( $value, $language_tag, $datatype_uri ) Encode a literal RDF node by either appending "C<@>" and an optional language tag, or "C<^>" and an datatype URI. =head2 bnode( $identifier ) Encode a blank node by prepending "C<_:>" to its identifier. =head2 subject( $subject ) =head2 predicate( $predicate ) =head2 object( $object ) Encode an RDF subject, predicate, or object respectively. The argument must either be given as hash reference, as defined in L format (see also method C of L), or as array reference as internally used by L. A hash reference is expected to have the following fields: =over =item type one of C, C or C (required) =item value the URI of the object, its lexical value or a blank node label depending on whether the object is a uri, literal or bnode =item lang the language of a literal value (optional but if supplied it must not be empty) =item datatype the datatype URI of the literal value (optional) =back An array reference is expected to consists of =over =item three elements (value, language tag, and datatype uri) for literal nodes, =item two elements "C" and the URI for URI nodes, =item two elements "C" and the blank node identifier for blank nodes. =back =head2 triple( $subject, $predicate, $object, [, $aref ] ) Encode an RDF triple, its elements given as explained for method C, C, and C. If an aREF data structure is given as fourth argument, the triple is added to this structure, possibly changing an aREF predicate map to an aRef subject map. Returns C on failure. =head2 add_hashref( $aref, $rdf ) Add RDF given in L format (as returned by method C in L). =head2 add_iterator( $aref, $iterator ) Add a L to an aREF subject map. I =head1 SEE ALSO L, L =cut json-ld-14.json100644001750001750 14312703377652 15541 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{ "_id": "http://me.markus-lanthaler.com/", "a": ["schema_Restaurant","schema_Brewery"] } json-ld-23.json100644001750001750 24612703377652 15545 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{ "_id": "http://example.org/posts#TripToWestVirginia", "a": "http://schema.org/BlogPosting", "dct_modified": "2010-05-29T14:17:39+02:00^xsd_dateTime" } uri-object-01.nt100644001750001750 11612703377652 15702 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite . uri-object-02.nt100644001750001750 11512703377652 15702 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite "x:object1" . rdf-trine-model.nt100644001750001750 33312703377652 16412 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite "Bar"^^ . "Foo"@en . _:g2 _:g1 . uri-object-01.json100644001750001750 4412703377652 16212 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject@":{"a":""}} uri-object-02.json100644001750001750 4212703377652 16211 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject@":{"a":"x:object1@"}} rdf-trine-model.json100644001750001750 55612703377652 16751 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{ "http://example.com/subject1": { "http://example.com/predicate1": [ "Foo@en", "Bar^" ] }, "_:bnode1": { "http://example.com/predicate2": [ "http://example.com/object2" ], "http://example.com/predicate2": [ "_:bnode3" ] } } invalid-object-01.err100644001750001750 2212703377652 16654 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteunknown prefix: x invalid-object-02.err100644001750001750 3012703377652 16654 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid object _id: "x" invalid-object-03.err100644001750001750 3212703377652 16657 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid object _id: ARRAY invalid-subject-10.nt100644001750001750 012703377652 16652 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid-object-01.json100644001750001750 4412703377652 17041 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject":{"a":{"_id":"x_bar"}}} invalid-object-02.json100644001750001750 4412703377652 17042 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject":{"a":{"_id":"\"x\""}}} invalid-object-03.json100644001750001750 4112703377652 17040 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject":{"a":{"_id":[[]]}}} invalid-subject-01.err100644001750001750 2212703377652 17045 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteunknown prefix: x invalid-subject-02.err100644001750001750 2212703377652 17046 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteunknown prefix: x invalid-subject-03.err100644001750001750 2512703377652 17052 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid subject: "x" invalid-subject-04.err100644001750001750 2512703377652 17053 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid subject: "x" invalid-subject-05.err100644001750001750 2312703377652 17052 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid subject: 0 invalid-subject-06.err100644001750001750 2312703377652 17053 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid subject: 0 invalid-subject-07.err100644001750001750 2712703377652 17060 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid subject: ARRAY invalid-subject-08.err100644001750001750 3212703377652 17055 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suitesubject _id must be invalid-subject-09.err100644001750001750 3212703377652 17056 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suitesubject _id must be invalid-subject-10.err100644001750001750 3212703377652 17046 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suitesubject _id must be invalid-datatype-01.err100644001750001750 2212703377652 17221 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteunknown prefix: x invalid-subject-01.json100644001750001750 3612703377652 17233 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x_bar":{"a":"foaf_Person"}} invalid-subject-02.json100644001750001750 4212703377652 17231 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"a":"foaf_Person","_id":"x_bar"} invalid-subject-03.json100644001750001750 3512703377652 17234 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"\"x\"":{"a":"foaf:Person"}}invalid-subject-04.json100644001750001750 4112703377652 17232 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"a":"foaf:Person","_id":"\"x\""}invalid-subject-05.json100644001750001750 3112703377652 17232 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"0":{"a":"foaf:Person"}}invalid-subject-06.json100644001750001750 3512703377652 17237 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"a":"foaf:Person","_id":"0"}invalid-subject-07.json100644001750001750 3412703377652 17237 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"a":"foaf:Person","_id":[]}invalid-subject-08.json100644001750001750 5212703377652 17240 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:a":{"a":"foaf:Person","_id":""}} invalid-subject-09.json100644001750001750 4712703377652 17245 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:a":{"a":"foaf:Person","_id":null}} invalid-subject-10.json100644001750001750 6512703377652 17235 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{ "x:a" : { "_id" : "", "a" : "foaf:Person" } } strict-null-object-2.nt100644001750001750 14312703377652 17304 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite . invalid-datatype-01.json100644001750001750 5212703377652 17405 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{ "x:subject":{"dct_extent":"123^x_bar"}} invalid-predicate-01.err100644001750001750 2412703377652 17350 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteunknown prefix: bar invalid-predicate-02.err100644001750001750 3212703377652 17350 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid predicate IRI _:1 invalid-predicate-03.err100644001750001750 1612703377652 17353 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid IRI x strict-null-object-1.err100644001750001750 3012703377652 17425 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteobject must not be null strict-null-object-2.err100644001750001750 3012703377652 17426 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteobject must not be null invalid-predicate-01.json100644001750001750 3612703377652 17534 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject":{"bar_y":"123"}} invalid-predicate-02.json100644001750001750 3112703377652 17530 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject":{"_:1":""}} invalid-predicate-03.json100644001750001750 3112703377652 17531 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject":{"":""}} strict-null-object-1.json100644001750001750 3112703377652 17607 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject":{"a":null}} strict-null-object-2.json100644001750001750 5112703377652 17612 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"x:subject":{"a":[null,"foaf_Person"]}} strict-invalid-subject-11.err100644001750001750 2212703377652 20354 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid subject: strict-invalid-subject-12.err100644001750001750 2212703377652 20355 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suiteinvalid subject: strict-invalid-subject-13.err100644001750001750 2512703377652 20361 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite_id must not be null strict-invalid-subject-11.json100644001750001750 3112703377652 20535 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"":{"a":"foaf:Person"}} strict-invalid-subject-12.json100644001750001750 3512703377652 20542 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"_id":"","a":"foaf:Person"} strict-invalid-subject-13.json100644001750001750 3712703377652 20545 0ustar00vojvoj000000000000RDF-aREF-0.27/t/suite{"_id":null,"a":"foaf:Person"}