json_ld: JSON-LD API conform options
This commit is contained in:
parent
9be156100a
commit
4ec22b8e66
6 changed files with 127 additions and 91 deletions
|
@ -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)
|
||||
|
|
|
@ -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 \\ [])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
18
lib/json/ld/options.ex
Normal 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
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue