json_ld: JSON-LD API conform options

This commit is contained in:
Marcel Otto 2017-03-22 00:35:05 +01:00
parent 9be156100a
commit 4ec22b8e66
6 changed files with 127 additions and 91 deletions

View file

@ -2,36 +2,24 @@ defmodule JSON.LD.Compaction do
@moduledoc nil
import JSON.LD.Utils
alias JSON.LD.Context
@doc """
Compacts the given input according to the steps in the JSON-LD Compaction Algorithm.
> Compaction is the process of applying a developer-supplied context to shorten
> IRIs to terms or compact IRIs and JSON-LD values expressed in expanded form
> to simple values such as strings or numbers. Often this makes it simpler to
> work with document as the data is expressed in application-specific terms.
> Compacted documents are also typically easier to read for humans.
-- <https://www.w3.org/TR/json-ld/#compacted-document-form>
Details at <https://www.w3.org/TR/json-ld-api/#compaction-algorithms>
"""
def compact(input, context, opts \\ []) do
with active_context = JSON.LD.context(context),
def compact(input, context, options \\ %JSON.LD.Options{}) do
with options = JSON.LD.Options.new(options),
active_context = JSON.LD.context(context),
inverse_context = Context.inverse(active_context),
compact_arrays = Keyword.get(opts, :compact_arrays, true)
expanded = JSON.LD.expand(input, options)
do
result = case do_compact(JSON.LD.expand(input), active_context, inverse_context,
nil, compact_arrays) do
[] ->
%{}
result when is_list(result) ->
%{compact_iri("@graph", active_context, inverse_context) => result}
result ->
result
end
result =
case do_compact(expanded, active_context, inverse_context, nil, options.compact_arrays) do
[] ->
%{}
result when is_list(result) ->
%{compact_iri("@graph", active_context, inverse_context) => result}
result ->
result
end
if Context.empty?(active_context),
do: result,
else: Map.put(result, "@context", context["@context"] || context)

View file

@ -10,14 +10,12 @@ defmodule JSON.LD.Context do
alias JSON.LD.Context.TermDefinition
def new(opts \\ [])
def new([base: base_iri]), do: %JSON.LD.Context{base_iri: base_iri}
def new(_), do: %JSON.LD.Context{}
def new(options \\ %JSON.LD.Options{}),
do: %JSON.LD.Context{base_iri: JSON.LD.Options.new(options).base}
def create(%{"@context" => json_ld_context}, opts),
do: new(opts) |> update(json_ld_context, Keyword.get(opts, :remote, []))
def create(%{"@context" => json_ld_context}, options),
do: new(options) |> update(json_ld_context)
def update(active, local, remote \\ [])

View file

@ -1,40 +1,42 @@
defmodule JSON.LD.Expansion do
@moduledoc nil
import JSON.LD.IRIExpansion
import JSON.LD.Utils
import JSON.LD.{IRIExpansion, Utils}
@doc """
Expands the given input according to the steps in the JSON-LD Expansion Algorithm.
def expand(input, options \\ %JSON.LD.Options{}) do
with options = JSON.LD.Options.new(options),
active_context = JSON.LD.Context.new(options)
do
active_context =
case options.expand_context do
%{"@context" => context} ->
JSON.LD.Context.update(active_context, context)
%{} = context ->
JSON.LD.Context.update(active_context, context)
nil ->
active_context
end
> Expansion is the process of taking a JSON-LD document and applying a `@context`
> such that all IRIs, types, and values are expanded so that the `@context` is
> no longer necessary.
-- <https://www.w3.org/TR/json-ld/#expanded-document-form>
Details at <http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm>
"""
def expand(json_ld_object, opts \\ []) do
case do_expand(JSON.LD.Context.new(opts), nil, json_ld_object, Keyword.delete(opts, :base)) do
result = %{"@graph" => graph} when map_size(result) == 1 ->
graph
nil ->
[]
result when not is_list(result) ->
[result]
result -> result
case do_expand(active_context, nil, input, options) do
result = %{"@graph" => graph} when map_size(result) == 1 ->
graph
nil ->
[]
result when not is_list(result) ->
[result]
result -> result
end
end
end
defp do_expand(active_context, active_property, element, opts \\ [])
defp do_expand(active_context, active_property, element, options)
# 1) If element is null, return null.
defp do_expand(_, _, nil, _), do: nil
# 2) If element is a scalar, ...
defp do_expand(active_context, active_property, element, opts)
defp do_expand(active_context, active_property, element, options)
when is_binary(element) or is_number(element) or is_boolean(element) do
if active_property in [nil, "@graph"] do
nil
@ -44,13 +46,13 @@ defmodule JSON.LD.Expansion do
end
# 3) If element is an array, ...
defp do_expand(active_context, active_property, element, opts)
defp do_expand(active_context, active_property, element, options)
when is_list(element) do
term_def = active_context.term_defs[active_property]
container_mapping = term_def && term_def.container_mapping
element
|> Enum.reduce([], fn (item, result) ->
expanded_item = do_expand(active_context, active_property, item)
expanded_item = do_expand(active_context, active_property, item, options)
if (active_property == "@list" or container_mapping == "@list") and
(is_list(expanded_item) or Map.has_key?(expanded_item, "@list")),
do: raise JSON.LD.ListOfListsError,
@ -66,7 +68,7 @@ defmodule JSON.LD.Expansion do
end
# 4) - 13)
defp do_expand(active_context, active_property, element, opts)
defp do_expand(active_context, active_property, element, options)
when is_map(element) do
# 5)
if Map.has_key?(element, "@context") do
@ -107,7 +109,7 @@ defmodule JSON.LD.Expansion do
message: "#{inspect value} is not a valid @type value"
end
"@graph" -> # 7.4.5)
do_expand(active_context, "@graph", value, opts)
do_expand(active_context, "@graph", value, options)
"@value" -> # 7.4.6)
if scalar?(value) or is_nil(value) do
if is_nil(value) do
@ -133,7 +135,7 @@ defmodule JSON.LD.Expansion do
if active_property in [nil, "@graph"] do # 7.4.9.1)
{:skip, result}
else
value = do_expand(active_context, active_property, value, opts)
value = do_expand(active_context, active_property, value, options)
# Spec FIXME: need to be sure that result is a list [from RDF.rb implementation]
value = if is_list(value),
@ -148,12 +150,12 @@ defmodule JSON.LD.Expansion do
value
end
"@set" -> # 7.4.10)
do_expand(active_context, active_property, value, opts)
do_expand(active_context, active_property, value, options)
"@reverse" -> # 7.4.11)
unless is_map(value),
do: raise JSON.LD.InvalidReverseValueError,
message: "#{inspect value} is not a valid @reverse value"
expanded_value = do_expand(active_context, "@reverse", value, opts) # 7.4.11.1)
expanded_value = do_expand(active_context, "@reverse", value, options) # 7.4.11.1)
new_result =
if Map.has_key?(expanded_value, "@reverse") do # 7.4.11.2) If expanded value contains an @reverse member, i.e., properties that are reversed twice, execute for each of its property and item the following steps:
Enum.reduce expanded_value["@reverse"], result,
@ -234,7 +236,7 @@ defmodule JSON.LD.Expansion do
index_value = if(is_list(index_value),
do: index_value,
else: [index_value])
index_value = do_expand(active_context, key, index_value, opts)
index_value = do_expand(active_context, key, index_value, options)
Enum.map(index_value, fn item ->
Map.put_new(item, "@index", index)
end)
@ -242,7 +244,7 @@ defmodule JSON.LD.Expansion do
end)
# 7.7)
true ->
do_expand(active_context, key, value, opts)
do_expand(active_context, key, value, options)
end
# 7.8)
if is_nil(expanded_value) do

View file

@ -1,24 +1,14 @@
defmodule JSON.LD.Flattening do
@moduledoc nil
import JSON.LD.Utils
import JSON.LD.{NodeIdentifierMap, Utils}
alias JSON.LD.NodeIdentifierMap
@doc """
Flattens the given input according to the steps in the JSON-LD Flattening Algorithm.
> Flattening collects all properties of a node in a single JSON object and labels
> all blank nodes with blank node identifiers. This ensures a shape of the data
> and consequently may drastically simplify the code required to process JSON-LD
> in certain applications.
-- <https://www.w3.org/TR/json-ld/#flattened-document-form>
Details at <https://www.w3.org/TR/json-ld-api/#flattening-algorithms>
"""
def flatten(input, context \\ nil, opts \\ []) do
with expanded = JSON.LD.expand(input) do
def flatten(input, context \\ nil, options \\ %JSON.LD.Options{}) do
with options = JSON.LD.Options.new(options),
expanded = JSON.LD.expand(input, options)
do
{:ok, node_id_map} = NodeIdentifierMap.start_link
node_map =
try do
@ -66,7 +56,7 @@ defmodule JSON.LD.Flattening do
|> Enum.reverse
if context && !Enum.empty?(flattened) do # TODO: Spec fixme: !Enum.empty?(flattened) is not in the spec, but in other implementations (Ruby, Java, Go, ...)
JSON.LD.compact(flattened, context, opts)
JSON.LD.compact(flattened, context, options)
else
flattened
end
@ -106,7 +96,7 @@ defmodule JSON.LD.Flattening do
types = Enum.reduce(types, [],
fn (item, types) ->
if blank_node_id?(item) do
identifier = NodeIdentifierMap.generate_blank_node_id(node_id_map, item)
identifier = generate_blank_node_id(node_id_map, item)
types ++ [identifier]
else
types ++ [item]
@ -165,13 +155,13 @@ defmodule JSON.LD.Flattening do
id =
if id do
if blank_node_id?(id) do
NodeIdentifierMap.generate_blank_node_id(node_id_map, id)
generate_blank_node_id(node_id_map, id)
else
id
end
# 6.2)
else
NodeIdentifierMap.generate_blank_node_id(node_id_map)
generate_blank_node_id(node_id_map)
end
# 6.3)
@ -271,7 +261,7 @@ defmodule JSON.LD.Flattening do
|> Enum.sort_by(fn {property, _} -> property end)
|> Enum.reduce(node_map, fn ({property, value}, node_map) ->
if blank_node_id?(property) do
property = NodeIdentifierMap.generate_blank_node_id(node_id_map, property)
property = generate_blank_node_id(node_id_map, property)
end
unless Map.has_key?(node_map[active_graph][id], property) do
node_map = update_in node_map, [active_graph, id], fn node ->

18
lib/json/ld/options.ex Normal file
View file

@ -0,0 +1,18 @@
defmodule JSON.LD.Options do
@moduledoc """
Options accepted by the JSON-LD processing algorithms.
as specified at <https://www.w3.org/TR/json-ld-api/#the-jsonldoptions-type>
"""
defstruct base: nil,
compact_arrays: true,
document_loader: nil,
expand_context: nil,
processing_mode: "json-ld-1.0"
def new(), do: %JSON.LD.Options{}
def new(%JSON.LD.Options{} = options), do: options
def new(options), do: struct(JSON.LD.Options, options)
end

View file

@ -30,27 +30,67 @@ defmodule JSON.LD do
def keyword?(value) when is_binary(value) and value in @keywords, do: true
def keyword?(value), do: false
defdelegate expand(input, options \\ []),
@doc """
Expands the given input according to the steps in the JSON-LD Expansion Algorithm.
> Expansion is the process of taking a JSON-LD document and applying a `@context`
> such that all IRIs, types, and values are expanded so that the `@context` is
> no longer necessary.
-- <https://www.w3.org/TR/json-ld/#expanded-document-form>
Details at <http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm>
"""
defdelegate expand(input, options \\ %JSON.LD.Options{}),
to: JSON.LD.Expansion
defdelegate compact(input, context, options \\ []),
@doc """
Compacts the given input according to the steps in the JSON-LD Compaction Algorithm.
> Compaction is the process of applying a developer-supplied context to shorten
> IRIs to terms or compact IRIs and JSON-LD values expressed in expanded form
> to simple values such as strings or numbers. Often this makes it simpler to
> work with document as the data is expressed in application-specific terms.
> Compacted documents are also typically easier to read for humans.
-- <https://www.w3.org/TR/json-ld/#compacted-document-form>
Details at <https://www.w3.org/TR/json-ld-api/#compaction-algorithms>
"""
defdelegate compact(input, context, options \\ %JSON.LD.Options{}),
to: JSON.LD.Compaction
defdelegate flatten(input, context \\ nil, options \\ []),
@doc """
Flattens the given input according to the steps in the JSON-LD Flattening Algorithm.
> Flattening collects all properties of a node in a single JSON object and labels
> all blank nodes with blank node identifiers. This ensures a shape of the data
> and consequently may drastically simplify the code required to process JSON-LD
> in certain applications.
-- <https://www.w3.org/TR/json-ld/#flattened-document-form>
Details at <https://www.w3.org/TR/json-ld-api/#flattening-algorithms>
"""
defdelegate flatten(input, context \\ nil, options \\ %JSON.LD.Options{}),
to: JSON.LD.Flattening
@doc """
Generator function for `JSON.LD.Context`s.
You can either pass a map with a `"@context"` key having the JSON-LD context
object its value, or the JSON-LD context object directly.
"""
def context(args, opts \\ [])
def context(args, opts \\ %JSON.LD.Options{})
def context(%{"@context" => _} = object, opts),
do: JSON.LD.Context.create(object, opts)
def context(%{"@context" => _} = object, options),
do: JSON.LD.Context.create(object, options)
def context(context, opts),
do: JSON.LD.Context.create(%{"@context" => context}, opts)
def context(context, options),
do: JSON.LD.Context.create(%{"@context" => context}, options)
end