From ec7f4b3e7e9a7991d0b3b5d1e82f262ae56892af Mon Sep 17 00:00:00 2001 From: Marcel Otto Date: Fri, 19 May 2017 18:11:58 +0200 Subject: [PATCH] core: NTriples encoder --- lib/rdf/serializations/ntriples_encoder.ex | 47 +++++++++++++++ test/unit/ntriples_encoder_test.exs | 69 ++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 lib/rdf/serializations/ntriples_encoder.ex create mode 100644 test/unit/ntriples_encoder_test.exs diff --git a/lib/rdf/serializations/ntriples_encoder.ex b/lib/rdf/serializations/ntriples_encoder.ex new file mode 100644 index 0000000..e5d9ccc --- /dev/null +++ b/lib/rdf/serializations/ntriples_encoder.ex @@ -0,0 +1,47 @@ +defmodule RDF.NTriples.Encoder do + use RDF.Serialization.Encoder + + alias RDF.{Literal, BlankNode} + + @xsd_string RDF.Datatype.NS.XSD.string + + def encode(data, opts \\ []) do + result = + data + |> Enum.reduce([], fn (statement, result) -> + [statement(statement) | result] + end) + |> Enum.reverse + |> Enum.join("\n") + {:ok, (if result == "", do: result, else: result <> "\n")} + end + + def statement({subject, predicate, object}) do + "#{term(subject)} #{term(predicate)} #{term(object)} ." + end + + def term(%URI{} = uri) do + "<#{to_string(uri)}>" + end + + def term(%Literal{value: value, language: language}) when not is_nil(language) do + ~s["#{value}"@#{language}] + end + + def term(%Literal{value: value, language: language}) when not is_nil(language) do + ~s["#{value}"@#{language}] + end + + def term(%Literal{datatype: @xsd_string} = literal) do + ~s["#{Literal.lexical(literal)}"] + end + + def term(%Literal{datatype: datatype} = literal) do + ~s["#{Literal.lexical(literal)}"^^<#{to_string(datatype)}>] + end + + def term(%BlankNode{} = bnode) do + to_string(bnode) + end + +end diff --git a/test/unit/ntriples_encoder_test.exs b/test/unit/ntriples_encoder_test.exs new file mode 100644 index 0000000..d2ad61b --- /dev/null +++ b/test/unit/ntriples_encoder_test.exs @@ -0,0 +1,69 @@ +defmodule RDF.NTriples.EncoderTest do + use ExUnit.Case, async: false + + alias RDF.NTriples + + doctest NTriples.Encoder + + alias RDF.Graph + alias RDF.NS.XSD + + import RDF.Sigils + + use RDF.Vocabulary.Namespace + + defvocab EX, + base_uri: "http://example.org/#", + terms: [], strict: false + + + describe "serializing a graph" do + test "an empty graph is serialized to an empty string" do + assert NTriples.Encoder.encode!(Graph.new) == "" + end + + test "statements with URIs only" do + assert NTriples.Encoder.encode!(Graph.new [ + {EX.S1, EX.p1, EX.O1}, + {EX.S1, EX.p1, EX.O2}, + {EX.S1, EX.p2, EX.O3}, + {EX.S2, EX.p3, EX.O4}, + ]) == + """ + . + . + . + . + """ + end + + test "statements with literals" do + assert NTriples.Encoder.encode!(Graph.new [ + {EX.S1, EX.p1, ~L"foo"}, + {EX.S1, EX.p1, ~L"foo"en}, + {EX.S1, EX.p2, 42}, + {EX.S2, EX.p3, RDF.literal("strange things", datatype: EX.custom)}, + ]) == + """ + "foo"@en . + "foo" . + "42"^^<#{XSD.integer}> . + "strange things"^^<#{EX.custom}> . + """ + end + + test "statements with blank nodes" do + assert NTriples.Encoder.encode!(Graph.new [ + {EX.S1, EX.p1, RDF.bnode(1)}, + {EX.S1, EX.p1, RDF.bnode("foo")}, + {EX.S1, EX.p1, RDF.bnode(:bar)}, + ]) == + """ + _:1 . + _:bar . + _:foo . + """ + end + end + +end