diff --git a/CHANGELOG.md b/CHANGELOG.md
index 121d3e3..732ceea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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`
diff --git a/lib/rdf.ex b/lib/rdf.ex
index 72b4a72..ad77841 100644
--- a/lib/rdf.ex
+++ b/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.
diff --git a/lib/rdf/serialization/serialization.ex b/lib/rdf/serialization/serialization.ex
index ec087f8..d7be716 100644
--- a/lib/rdf/serialization/serialization.ex
+++ b/lib/rdf/serialization/serialization.ex
@@ -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
diff --git a/test/data/cbd.ttl b/test/data/cbd.ttl
new file mode 100644
index 0000000..9a1678c
--- /dev/null
+++ b/test/data/cbd.ttl
@@ -0,0 +1,47 @@
+ @prefix dc: .
+ @prefix dc11: .
+ @prefix foaf: .
+ @prefix owl: .
+ @prefix rdf: .
+ @prefix rdfs: .
+ @prefix xsd: .
+
+ ;
+ .
+ dc11:extent "1234";
+ dc11:format "image/jpeg";
+ a foaf:Image .
+ foaf:mbox a rdf:Property,
+ owl:InverseFunctionalProperty .
+ 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 .
+ dc11:contributor [ a foaf:Person;
+ foaf:name "Jane Doe"];
+ dc11:creator [ a foaf:Person;
+ foaf:img ;
+ foaf:mbox "john@example.com";
+ foaf:name "John Doe";
+ foaf:phone ];
+ 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 .
+ [ rdf:object "image/jpeg";
+ rdf:predicate dc11:format;
+ rdf:subject foaf:Image;
+ a rdf:Statement;
+ rdfs:isDefinedBy ] .
+ [ rdf:object "application/pdf";
+ rdf:predicate dc11:format;
+ rdf:subject ;
+ a rdf:Statement;
+ rdfs:isDefinedBy ] .
diff --git a/test/unit/serialization/serialization_test.exs b/test/unit/serialization/serialization_test.exs
index 3b3914f..3fd4ee8 100644
--- a/test/unit/serialization/serialization_test.exs
+++ b/test/unit/serialization/serialization_test.exs
@@ -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: .
+ ex:Aaron 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