Class: JSON::LD::Normalize

Inherits:
Object
  • Object
show all
Defined in:
lib/json/ld/normalize.rb

Overview

Normalize Nodes in a graph. Uses [Normalization Algorithm](json-ld.org/spec/latest/#normalization-1) from [JSON-LD specification](json-ld.org/spec/latest/).

This module takes a graph and returns a new graph, with BNode names normalized to allow for a canonical ordering of all statements within a graph.

Examples:

Normalize a graph

JSON::LD::normalize(graph) => graph

See Also:

Author:

Instance Method Summary collapse

Constructor Details

#initialize(graph) ⇒ Normalize

Create a new normalization instance

Parameters:

  • graph (RDF::Enumerable)


19
20
21
# File 'lib/json/ld/normalize.rb', line 19

def initialize(graph)
  @graph = graph
end

Instance Method Details

#normalizeRDF::Graph

Perform normalization, and return a new graph with node identifiers normalized

Returns:



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/json/ld/normalize.rb', line 26

def normalize
  # Create an empty list of expanded nodes and recursively process every object in the expanded input that is not an
  # expanded IRI, typed literal or language literal
  nodes = graph.subjects.select {|s| s.node?}
  
  forward_mapping = {}
  reverse_mapping = {}
  @node_properties = {}
  graph.each_statment do |st|
    # Create a forward mapping that relates graph nodes to the IRIs of the targets nodes that they reference. For example,
    # if a node alpha refers to a node beta via a property, the key in the forward mapping is the subject IRI of alpha and
    # the value is an array containing at least the subject IRI of beta.
    if st.subject.node? && st.object.uri?
      forward_mapping[st.subject] ||= {}
      forward_mapping[st.subject] << st.object
    end

    # Create a reverse mapping that relates graph nodes to every other node that refers to them in the graph. For example,
    # if a node alpha refers to a node beta via a property, the key in the reverse mapping is the subject IRI for beta and
    # the value is an array containing at least the IRI for alpha.
    if st.object.node? && st.subject.uri?
      reverse_mapping[st.object] ||= {}
      reverse_mapping[st.object] << st.subject
    end
    
    # For node comparisons, keep track of properties of each node
    if st.subject.node?
      @node_properties[st.subject] ||= {}
      @node_properties[st.subject][st.predicate] ||= []
      @node_properties[st.subject][st.predicate] << st.object
    end
  end
  
  # Label every unlabeled node according to the Label Generation Algorithm in descending order using the Deep
  # Comparison Algorithm to determine the sort order.
  node_mapping = {}
  gen = "c14n_1"
  nodes.sort {|a, b| deep_comparison(a) <=> deep_comparison(b) }.each do |node|
    # name with Label Generation Algorithm and create mapping from original node to new name
    node_mapping[node] = RDF::Node.new(gen)
    gen = gen.succ
  end

  # Add statements to new graph using new node names
  graph = RDF::Graph.new
  
  @graph.each_statement do |st|
    if st.subject.node? || st.object.node?
      st = st.dup
      st.subject = node_mapping.fetch(st.subject, st.subject)
      st.object = node_mapping.fetch(st.object, st.object)
    end
    graph << st
  end
  
  # Return new graph
  graph
end