Add general RDF.Serialization.read_* and write_* functions
This commit is contained in:
parent
54d9eff014
commit
24aabc389b
5 changed files with 439 additions and 3 deletions
|
@ -18,7 +18,13 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
|
|||
### Added
|
||||
|
||||
- `RDF.Serialization.Format`s define a `name` atom
|
||||
- The following functions to access available `RDF.Serialization.Format`s:
|
||||
- all `RDF.Serialization.Reader` and `RDF.Serialization.Writer` functions are now
|
||||
available on the `RDF.Serialization` module (or aliased on the top-level `RDF`
|
||||
module) and the format can be specified instead of a `RDF.Serialization.Format`
|
||||
argument, via the `format` or `media_type` option or in case of `*_file`
|
||||
functions, without explicit specification of the format, but inferred from file
|
||||
name extension instead; see the updated README section about RDF serializations
|
||||
- the following functions to access available `RDF.Serialization.Format`s:
|
||||
- `RDF.Serialization.formats/0`
|
||||
- `RDF.Serialization.available_formats/0`
|
||||
- `RDF.Serialization.format/1`
|
||||
|
|
15
lib/rdf.ex
15
lib/rdf.ex
|
@ -20,8 +20,9 @@ defmodule RDF do
|
|||
- `RDF.Dataset`
|
||||
- `RDF.Data`
|
||||
- `RDF.List`
|
||||
- the foundations for the definition of RDF serialization formats
|
||||
- `RDF.Serialization`
|
||||
- functions for working with RDF serializations: `RDF.Serialization`
|
||||
- behaviours for the definition of RDF serialization formats
|
||||
- `RDF.Serialization.Format`
|
||||
- `RDF.Serialization.Decoder`
|
||||
- `RDF.Serialization.Encoder`
|
||||
- and the implementation of various RDF serialization formats
|
||||
|
@ -38,6 +39,16 @@ defmodule RDF do
|
|||
alias RDF.{IRI, Namespace, Literal, BlankNode, Triple, Quad,
|
||||
Description, Graph, Dataset}
|
||||
|
||||
defdelegate read_string(content, opts), to: RDF.Serialization
|
||||
defdelegate read_string!(content, opts), to: RDF.Serialization
|
||||
defdelegate read_file(filename, opts \\ []), to: RDF.Serialization
|
||||
defdelegate read_file!(filename, opts \\ []), to: RDF.Serialization
|
||||
defdelegate write_string(content, opts), to: RDF.Serialization
|
||||
defdelegate write_string!(content, opts), to: RDF.Serialization
|
||||
defdelegate write_file(filename, opts \\ []), to: RDF.Serialization
|
||||
defdelegate write_file!(filename, opts \\ []), to: RDF.Serialization
|
||||
|
||||
|
||||
@doc """
|
||||
Checks if the given value is a RDF resource.
|
||||
|
||||
|
|
|
@ -90,9 +90,15 @@ defmodule RDF.Serialization do
|
|||
|
||||
iex> RDF.Serialization.format_by_extension("ttl")
|
||||
RDF.Turtle
|
||||
iex> RDF.Serialization.format_by_extension(".ttl")
|
||||
RDF.Turtle
|
||||
iex> RDF.Serialization.format_by_extension("jsonld")
|
||||
nil # unless json_ld is defined as a dependency of the application
|
||||
"""
|
||||
def format_by_extension(extension)
|
||||
|
||||
def format_by_extension("." <> extension), do: format_by_extension(extension)
|
||||
|
||||
def format_by_extension(extension) do
|
||||
format_where(fn format -> format.extension == extension end)
|
||||
end
|
||||
|
@ -102,4 +108,169 @@ defmodule RDF.Serialization do
|
|||
|> Stream.filter(&Code.ensure_loaded?/1)
|
||||
|> Enum.find(fun)
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
Reads and decodes a serialized graph or dataset from a string.
|
||||
|
||||
The format must be specified with the `format` option and a format name or the
|
||||
`media_type` option and the media type of the format.
|
||||
|
||||
It returns an `{:ok, data}` tuple, with `data` being the deserialized graph or
|
||||
dataset, or `{:error, reason}` if an error occurs.
|
||||
"""
|
||||
def read_string(content, opts) do
|
||||
with {:ok, format} <- string_format(opts) do
|
||||
format.read_string(content, opts)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Reads and decodes a serialized graph or dataset from a string.
|
||||
|
||||
The format must be specified with the `format` option and a format name or the
|
||||
`media_type` option and the media type of the format.
|
||||
|
||||
As opposed to `read_string`, it raises an exception if an error occurs.
|
||||
"""
|
||||
def read_string!(content, opts) do
|
||||
with {:ok, format} <- string_format(opts) do
|
||||
format.read_string!(content, opts)
|
||||
else
|
||||
{:error, error} -> raise error
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Reads and decodes a serialized graph or dataset from a file.
|
||||
|
||||
The format can be specified with the `format` option and a format name or the
|
||||
`media_type` option and the media type of the format. If none of these are
|
||||
given, the format gets inferred from the extension of the given file name.
|
||||
|
||||
It returns an `{:ok, data}` tuple, with `data` being the deserialized graph or
|
||||
dataset, or `{:error, reason}` if an error occurs.
|
||||
"""
|
||||
def read_file(file, opts \\ []) do
|
||||
with {:ok, format} <- file_format(file, opts) do
|
||||
format.read_file(file, opts)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Reads and decodes a serialized graph or dataset from a file.
|
||||
|
||||
The format can be specified with the `format` option and a format name or the
|
||||
`media_type` option and the media type of the format. If none of these are
|
||||
given, the format gets inferred from the extension of the given file name.
|
||||
|
||||
As opposed to `read_file`, it raises an exception if an error occurs.
|
||||
"""
|
||||
def read_file!(file, opts \\ []) do
|
||||
with {:ok, format} <- file_format(file, opts) do
|
||||
format.read_file!(file, opts)
|
||||
else
|
||||
{:error, error} -> raise error
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Encodes and writes a graph or dataset to a string.
|
||||
|
||||
The format must be specified with the `format` option and a format name or the
|
||||
`media_type` option and the media type of the format.
|
||||
|
||||
It returns an `{:ok, string}` tuple, with `string` being the serialized graph or
|
||||
dataset, or `{:error, reason}` if an error occurs.
|
||||
"""
|
||||
def write_string(data, opts) do
|
||||
with {:ok, format} <- string_format(opts) do
|
||||
format.write_string(data, opts)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Encodes and writes a graph or dataset to a string.
|
||||
|
||||
The format must be specified with the `format` option and a format name or the
|
||||
`media_type` option and the media type of the format.
|
||||
|
||||
As opposed to `write_string`, it raises an exception if an error occurs.
|
||||
"""
|
||||
def write_string!(data, opts) do
|
||||
with {:ok, format} <- string_format(opts) do
|
||||
format.write_string!(data, opts)
|
||||
else
|
||||
{:error, error} -> raise error
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Encodes and writes a graph or dataset to a file.
|
||||
|
||||
The format can be specified with the `format` option and a format name or the
|
||||
`media_type` option and the media type of the format. If none of these are
|
||||
given, the format gets inferred from the extension of the given file name.
|
||||
|
||||
Other available serialization-independent options:
|
||||
|
||||
- `:force` - If not set to `true`, an error is raised when the given file
|
||||
already exists (default: `false`)
|
||||
- `:file_mode` - A list with the Elixir `File.open` modes to be used fior writing
|
||||
(default: `[:utf8, :write]`)
|
||||
|
||||
It returns `:ok` if successfull or `{:error, reason}` if an error occurs.
|
||||
"""
|
||||
def write_file(data, path, opts \\ []) do
|
||||
with {:ok, format} <- file_format(path, opts) do
|
||||
format.write_file(data, path, opts)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Encodes and writes a graph or dataset to a file.
|
||||
|
||||
The format can be specified with the `format` option and a format name or the
|
||||
`media_type` option and the media type of the format. If none of these are
|
||||
given, the format gets inferred from the extension of the given file name.
|
||||
|
||||
See `write_file` for a list of other available options.
|
||||
|
||||
As opposed to `write_file`, it raises an exception if an error occurs.
|
||||
"""
|
||||
def write_file!(data, path, opts \\ []) do
|
||||
with {:ok, format} <- file_format(path, opts) do
|
||||
format.write_file!(data, path, opts)
|
||||
else
|
||||
{:error, error} -> raise error
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
defp string_format(opts) do
|
||||
if format =
|
||||
(opts |> Keyword.get(:format) |> format()) ||
|
||||
(opts |> Keyword.get(:media_type) |> format_by_media_type())
|
||||
do
|
||||
{:ok, format}
|
||||
else
|
||||
{:error, "unable to detect serialization format"}
|
||||
end
|
||||
end
|
||||
|
||||
defp file_format(filename, opts) do
|
||||
case string_format(opts) do
|
||||
{:ok, format} -> {:ok, format}
|
||||
_ -> format_by_file_name(filename)
|
||||
end
|
||||
end
|
||||
|
||||
defp format_by_file_name(filename) do
|
||||
if format = filename |> Path.extname() |> format_by_extension() do
|
||||
{:ok, format}
|
||||
else
|
||||
{:error, "unable to detect serialization format"}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
47
test/data/cbd.ttl
Normal file
47
test/data/cbd.ttl
Normal file
|
@ -0,0 +1,47 @@
|
|||
@prefix dc: <http://purl.org/dc/terms/> .
|
||||
@prefix dc11: <http://purl.org/dc/elements/1.1/> .
|
||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
||||
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
||||
|
||||
<http://example.com/aBookCritic> <http://example.com/dislikes> <http://example.com/anotherGreatBook>;
|
||||
<http://example.com/likes> <http://example.com/aReallyGreatBook> .
|
||||
<http://example.com/john.jpg> dc11:extent "1234";
|
||||
dc11:format "image/jpeg";
|
||||
a foaf:Image .
|
||||
foaf:mbox a rdf:Property,
|
||||
owl:InverseFunctionalProperty .
|
||||
<http://example.com/anotherGreatBook> dc11:creator "June Doe (june@example.com)";
|
||||
dc11:format "application/pdf";
|
||||
dc11:language "en";
|
||||
dc11:publisher "Examples-R-Us";
|
||||
dc11:rights "Copyright (C) 2004 Examples-R-Us. All rights reserved.";
|
||||
dc11:title "Another Great Book";
|
||||
dc:issued "2004-05-03"^^xsd:date;
|
||||
rdfs:seeAlso <http://example.com/aReallyGreatBook> .
|
||||
<http://example.com/aReallyGreatBook> dc11:contributor [ a foaf:Person;
|
||||
foaf:name "Jane Doe"];
|
||||
dc11:creator [ a foaf:Person;
|
||||
foaf:img <http://example.com/john.jpg>;
|
||||
foaf:mbox "john@example.com";
|
||||
foaf:name "John Doe";
|
||||
foaf:phone <tel:+1-999-555-1234>];
|
||||
dc11:format "application/pdf";
|
||||
dc11:language "en";
|
||||
dc11:publisher "Examples-R-Us";
|
||||
dc11:rights "Copyright (C) 2004 Examples-R-Us. All rights reserved.";
|
||||
dc11:title "A Really Great Book";
|
||||
dc:issued "2004-01-19"^^xsd:date;
|
||||
rdfs:seeAlso <http://example.com/anotherGreatBook> .
|
||||
[ rdf:object "image/jpeg";
|
||||
rdf:predicate dc11:format;
|
||||
rdf:subject foaf:Image;
|
||||
a rdf:Statement;
|
||||
rdfs:isDefinedBy <http://example.com/image-formats.rdf>] .
|
||||
[ rdf:object "application/pdf";
|
||||
rdf:predicate dc11:format;
|
||||
rdf:subject <http://example.com/aReallyGreatBook>;
|
||||
a rdf:Statement;
|
||||
rdfs:isDefinedBy <http://example.com/book-formats.rdf>] .
|
|
@ -2,4 +2,205 @@ defmodule RDF.SerializationTest do
|
|||
use ExUnit.Case
|
||||
|
||||
doctest RDF.Serialization
|
||||
|
||||
use RDF.Vocabulary.Namespace
|
||||
|
||||
defvocab EX,
|
||||
base_iri: "http://example.org/",
|
||||
terms: [], strict: false
|
||||
|
||||
@example_turtle_file "test/data/cbd.ttl"
|
||||
@example_turtle_string """
|
||||
@prefix ex: <http://example.org/#> .
|
||||
ex:Aaron <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ex:Person .
|
||||
"""
|
||||
|
||||
@example_graph RDF.Graph.new [{EX.S, EX.p, EX.O}]
|
||||
@example_graph_turtle """
|
||||
@prefix : <#{to_string(EX. __base_iri__)}> .
|
||||
|
||||
:S
|
||||
:p :O .
|
||||
"""
|
||||
|
||||
defp file(name), do: System.tmp_dir!() |> Path.join(name)
|
||||
|
||||
|
||||
describe "read_string/2" do
|
||||
test "with correct format name" do
|
||||
assert {:ok, %RDF.Graph{}} =
|
||||
RDF.Serialization.read_string(@example_turtle_string, format: :turtle)
|
||||
end
|
||||
|
||||
test "with wrong format name" do
|
||||
assert {:error, "N-Triple scanner error" <> _} =
|
||||
RDF.Serialization.read_string(@example_turtle_string, format: :ntriples)
|
||||
end
|
||||
|
||||
test "with invalid format name" do
|
||||
assert {:error, "unable to detect serialization format"} ==
|
||||
RDF.Serialization.read_string(@example_turtle_string, format: :foo)
|
||||
end
|
||||
|
||||
test "with media_type" do
|
||||
assert {:ok, %RDF.Graph{}} =
|
||||
RDF.Serialization.read_string(@example_turtle_string, media_type: "text/turtle")
|
||||
end
|
||||
end
|
||||
|
||||
describe "read_string!/2" do
|
||||
test "with correct format name" do
|
||||
assert %RDF.Graph{} =
|
||||
RDF.Serialization.read_string!(@example_turtle_string, format: :turtle)
|
||||
end
|
||||
|
||||
test "with wrong format name" do
|
||||
assert_raise RuntimeError, ~r/^N-Triple scanner error.*/, fn ->
|
||||
RDF.Serialization.read_string!(@example_turtle_string, format: :ntriples)
|
||||
end
|
||||
end
|
||||
|
||||
test "with invalid format name" do
|
||||
assert_raise RuntimeError, "unable to detect serialization format", fn ->
|
||||
RDF.Serialization.read_string!(@example_turtle_string, format: :foo)
|
||||
end
|
||||
end
|
||||
|
||||
test "with media_type" do
|
||||
assert %RDF.Graph{} =
|
||||
RDF.Serialization.read_string!(@example_turtle_string, media_type: "text/turtle")
|
||||
end
|
||||
end
|
||||
|
||||
describe "read_file/2" do
|
||||
test "without arguments, i.e. via correct file extension" do
|
||||
assert {:ok, %RDF.Graph{}} = RDF.Serialization.read_file(@example_turtle_file)
|
||||
end
|
||||
|
||||
test "with correct format name" do
|
||||
assert {:ok, %RDF.Graph{}} =
|
||||
RDF.Serialization.read_file(@example_turtle_file, format: :turtle)
|
||||
end
|
||||
|
||||
test "with wrong format name" do
|
||||
assert {:error, "N-Triple scanner error" <> _} =
|
||||
RDF.Serialization.read_file(@example_turtle_file, format: :ntriples)
|
||||
end
|
||||
|
||||
test "with invalid format name, but correct file extension" do
|
||||
assert {:ok, %RDF.Graph{}} = RDF.Serialization.read_file(@example_turtle_file, format: :foo)
|
||||
end
|
||||
|
||||
test "with media_type" do
|
||||
assert {:ok, %RDF.Graph{}} =
|
||||
RDF.Serialization.read_file(@example_turtle_file, media_type: "text/turtle")
|
||||
end
|
||||
end
|
||||
|
||||
describe "read_file!/2" do
|
||||
test "without arguments, i.e. via correct file extension" do
|
||||
assert %RDF.Graph{} = RDF.Serialization.read_file!(@example_turtle_file)
|
||||
end
|
||||
|
||||
test "with correct format name" do
|
||||
assert %RDF.Graph{} =
|
||||
RDF.Serialization.read_file!(@example_turtle_file, format: :turtle)
|
||||
end
|
||||
|
||||
test "with wrong format name" do
|
||||
assert_raise RuntimeError, ~r/^N-Triple scanner error.*/, fn ->
|
||||
RDF.Serialization.read_file!(@example_turtle_file, format: :ntriples)
|
||||
end
|
||||
end
|
||||
|
||||
test "with media_type name" do
|
||||
assert %RDF.Graph{} =
|
||||
RDF.Serialization.read_file!(@example_turtle_file, media_type: "text/turtle")
|
||||
end
|
||||
end
|
||||
|
||||
describe "write_string/2" do
|
||||
test "with name of available format" do
|
||||
assert RDF.Serialization.write_string(@example_graph, format: :turtle,
|
||||
prefixes: %{"" => EX. __base_iri__}) ==
|
||||
{:ok, @example_graph_turtle}
|
||||
end
|
||||
|
||||
test "with invalid format name" do
|
||||
assert RDF.Serialization.write_string(@example_graph, format: :foo,
|
||||
prefixes: %{"" => EX. __base_iri__}) ==
|
||||
{:error, "unable to detect serialization format"}
|
||||
end
|
||||
|
||||
test "with media type" do
|
||||
assert RDF.Serialization.write_string(@example_graph, media_type: "text/turtle",
|
||||
prefixes: %{"" => EX. __base_iri__}) ==
|
||||
{:ok, @example_graph_turtle}
|
||||
end
|
||||
end
|
||||
|
||||
describe "write_string!/2" do
|
||||
test "with name of available format" do
|
||||
assert RDF.Serialization.write_string!(@example_graph, format: :turtle,
|
||||
prefixes: %{"" => EX. __base_iri__}) ==
|
||||
@example_graph_turtle
|
||||
end
|
||||
|
||||
test "with invalid format name" do
|
||||
assert_raise RuntimeError, "unable to detect serialization format", fn ->
|
||||
RDF.Serialization.write_string!(@example_graph, format: :foo,
|
||||
prefixes: %{"" => EX. __base_iri__})
|
||||
end
|
||||
end
|
||||
|
||||
test "with media type" do
|
||||
assert RDF.Serialization.write_string!(@example_graph, media_type: "text/turtle",
|
||||
prefixes: %{"" => EX. __base_iri__}) ==
|
||||
@example_graph_turtle
|
||||
end
|
||||
end
|
||||
|
||||
describe "write_file/2" do
|
||||
test "without arguments, i.e. via file extension" do
|
||||
file = file("write_file_test.ttl")
|
||||
if File.exists?(file), do: File.rm(file)
|
||||
assert RDF.Serialization.write_file(@example_graph, file,
|
||||
prefixes: %{"" => EX. __base_iri__}) == :ok
|
||||
assert File.exists?(file)
|
||||
assert File.read!(file) == @example_graph_turtle
|
||||
File.rm(file)
|
||||
end
|
||||
|
||||
test "with format name" do
|
||||
file = file("write_file_test.nt")
|
||||
if File.exists?(file), do: File.rm(file)
|
||||
assert RDF.Serialization.write_file(@example_graph, file, format: :turtle,
|
||||
prefixes: %{"" => EX. __base_iri__}) == :ok
|
||||
assert File.exists?(file)
|
||||
assert File.read!(file) == @example_graph_turtle
|
||||
File.rm(file)
|
||||
end
|
||||
end
|
||||
|
||||
describe "write_file!/2" do
|
||||
test "without arguments, i.e. via file extension" do
|
||||
file = file("write_file_test.ttl")
|
||||
if File.exists?(file), do: File.rm(file)
|
||||
assert RDF.Serialization.write_file!(@example_graph, file,
|
||||
prefixes: %{"" => EX. __base_iri__}) == :ok
|
||||
assert File.exists?(file)
|
||||
assert File.read!(file) == @example_graph_turtle
|
||||
File.rm(file)
|
||||
end
|
||||
|
||||
test "with format name" do
|
||||
file = file("write_file_test.nt")
|
||||
if File.exists?(file), do: File.rm(file)
|
||||
assert RDF.Serialization.write_file!(@example_graph, file, format: :turtle,
|
||||
prefixes: %{"" => EX. __base_iri__}) == :ok
|
||||
assert File.exists?(file)
|
||||
assert File.read!(file) == @example_graph_turtle
|
||||
File.rm(file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue