Fix handling of empty descriptions in Turtle encoder

This commit is contained in:
Marcel Otto 2022-04-08 14:20:51 +02:00
parent 5bb1264249
commit c143272f50
4 changed files with 65 additions and 16 deletions

View file

@ -31,6 +31,11 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
- When triples with an empty object list where added to an `RDF.Graph`, it - When triples with an empty object list where added to an `RDF.Graph`, it
included empty descriptions, which lead to inconsistent behaviour included empty descriptions, which lead to inconsistent behaviour
(for example it would be counted in `RDF.Graph.subject_count/1`). (for example it would be counted in `RDF.Graph.subject_count/1`).
- When an `RDF.Graph` contained empty descriptions these were rendered by
the `RDF.Turtle.Encoder` to a subject without predicates and objects, i.e.
invalid Turtle. This actually shouldn't happen and is either caused by
misuse or a bug. So instead, a `RDF.Graph.EmptyDescriptionError` with a
detailed message will be raised now when this case is detected.
[Compare v0.11.0...HEAD](https://github.com/rdf-elixir/rdf-ex/compare/v0.11.0...HEAD) [Compare v0.11.0...HEAD](https://github.com/rdf-elixir/rdf-ex/compare/v0.11.0...HEAD)

View file

@ -22,14 +22,6 @@ defmodule RDF.Triple.InvalidPredicateError do
end end
end end
defmodule RDF.XSD.Datatype.Mismatch do
defexception [:value, :expected_type]
def message(%{value: value, expected_type: expected_type}) do
"'#{inspect(value)}' is not a #{expected_type}"
end
end
defmodule RDF.Quad.InvalidGraphContextError do defmodule RDF.Quad.InvalidGraphContextError do
defexception [:graph_context] defexception [:graph_context]
@ -38,6 +30,31 @@ defmodule RDF.Quad.InvalidGraphContextError do
end end
end end
defmodule RDF.Graph.EmptyDescriptionError do
defexception [:subject]
def message(%{subject: subject}) do
"""
RDF.Graph with empty description about '#{inspect(subject)}' detected.
Empty descriptions in a graph lead to inconsistent behaviour. The RDF.Graph API
should ensure that this never happens. So this probably happened by changing the
contents of the RDF.Graph struct directly, which is strongly discouraged.
You should always use the RDF.Graph API to change the content of a graph.
If this happened while using the RDF.Graph API, this is a bug.
Please report this at https://github.com/rdf-elixir/rdf-ex/issues and describe the
circumstances how this happened.
"""
end
end
defmodule RDF.XSD.Datatype.Mismatch do
defexception [:value, :expected_type]
def message(%{value: value, expected_type: expected_type}) do
"'#{inspect(value)}' is not a #{expected_type}"
end
end
defmodule RDF.Namespace.InvalidVocabBaseIRIError do defmodule RDF.Namespace.InvalidVocabBaseIRIError do
defexception [:message] defexception [:message]
end end

View file

@ -232,11 +232,15 @@ defmodule RDF.Turtle.Encoder do
defp description_order(%{subject: s1}, %{subject: s2}), do: to_string(s1) < to_string(s2) defp description_order(%{subject: s1}, %{subject: s2}), do: to_string(s1) < to_string(s2)
defp description_statements(description, state, nesting) do defp description_statements(description, state, nesting) do
with %BlankNode{} <- description.subject, if Description.empty?(description) do
ref_count when ref_count < 2 <- State.bnode_ref_counter(state, description.subject) do raise Graph.EmptyDescriptionError, subject: description.subject
unrefed_bnode_subject_term(description, ref_count, state, nesting)
else else
_ -> full_description_statements(description, state, nesting) with %BlankNode{} <- description.subject,
ref_count when ref_count < 2 <- State.bnode_ref_counter(state, description.subject) do
unrefed_bnode_subject_term(description, ref_count, state, nesting)
else
_ -> full_description_statements(description, state, nesting)
end
end end
end end

View file

@ -416,13 +416,36 @@ defmodule RDF.Turtle.EncoderTest do
] . ] .
""" """
end end
test "serializing a pathological graph with an empty description" do
description = RDF.description(EX.S)
graph = %Graph{Graph.new() | descriptions: %{description.subject => description}}
assert_raise Graph.EmptyDescriptionError, fn ->
Turtle.Encoder.encode!(graph)
end
end
end end
test "serializing a description" do describe "serializing a description" do
description = EX.S |> EX.p(EX.O) test "a non-empty description" do
description = EX.S |> EX.p(EX.O)
assert Turtle.Encoder.encode!(description) == assert Turtle.Encoder.encode!(description) ==
description |> Graph.new() |> Turtle.Encoder.encode!() description |> Graph.new() |> Turtle.Encoder.encode!()
end
test "an empty description" do
description = RDF.description(EX.S)
assert Turtle.Encoder.encode!(description) |> String.trim() ==
"""
@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#> .
"""
|> String.trim()
end
end end
describe "prefixed_name/2" do describe "prefixed_name/2" do