Module: JSON::LD::Compact

Includes:
Utils
Included in:
API
Defined in:
lib/json/ld/compact.rb

Instance Method Summary collapse

Methods included from Utils

#blank_node?, #list?, #node?, #node_reference?, #value?

Instance Method Details

#compact(element, property = nil) ⇒ Array, Hash

Compact an expanded Array or Hash given an active property and a context.

Parameters:

  • element (Array, Hash)
  • property (String) (defaults to: nil)

    (nil)

Returns:



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/json/ld/compact.rb', line 11

def compact(element, property = nil)
  if property.nil?
    debug("compact") {"element: #{element.inspect}, ec: #{context.inspect}"}
  else
    debug("compact") {"property: #{property.inspect}"}
  end
  case element
  when Array
    # 1) If value is an array, process each item in value recursively using
    #    this algorithm, passing copies of the active context and the
    #    active property.
    debug("compact") {"Array #{element.inspect}"}
    result = depth {element.map {|v| compact(v, property)}}
    
    # If element has a single member and the active property has no
    # @container mapping to @list or @set, the compacted value is that
    # member; otherwise the compacted value is element
    if result.length == 1 && @options[:compactArrays]
      debug("=> extract single element: #{result.first.inspect}")
      result.first
    else
      debug("=> array result: #{result.inspect}")
      result
    end
  when Hash
    # 2) Otherwise, if element is an object:
    result = {}
    
    if k = %w(@list @set @value).detect {|container| element.has_key?(container)}
      debug("compact") {"#{k}: container(#{property}) = #{context.container(property)}"}
    end

    k ||= '@id' if element.keys == ['@id']
    
    case k
    when '@value', '@id'
      # If element has an @value property or element is a node reference, return the result of performing Value Compaction on element using active property.
      v = context.compact_value(property, element, :depth => @depth)
      debug("compact") {"value optimization, return as #{v.inspect}"}
      return v
    when '@list'
      # Otherwise, if the active property has a @container mapping to @list and element has a corresponding @list property, recursively compact that property's value passing a copy of the active context and the active property ensuring that the result is an array with all null values removed.
      
      # If there already exists a value for active property in element and the full IRI of property is also coerced to @list, return an error.
      # FIXME: check for full-iri list coercion

      # Otherwise store the resulting array as value of active property if empty or property otherwise.
      compacted_key = context.compact_iri(k, :position => :predicate, :depth => @depth)
      v = depth { compact(element[k], property) }
      
      # Return either the result as an array, as an object with a key of @list (or appropriate alias from active context
      v = [v].compact unless v.is_a?(Array)
      v = {compacted_key => v} unless context.container(property) == k
      debug("compact") {"@list result, return as #{v.inspect}"}
      return v
    end

    # Otherwise, for each property and value in element:
    element.each do |key, value|
      debug("compact") {"#{key}: #{value.inspect}"}

      if %(@id @type).include?(key)
        position = key == '@id' ? :subject : :type
        compacted_key = context.compact_iri(key, :position => :predicate, :depth => @depth)

        result[compacted_key] = case value
        when String
          # If value is a string, the compacted value is the result of performing IRI Compaction on value.
          debug {" => compacted string for #{key}"}
          context.compact_iri(value, :position => position, :depth => @depth)
        when Array
          # Otherwise, value must be an array. Perform IRI Compaction on every entry of value. If value contains just one entry, value is set to that entry
          compacted_value = value.map {|v| context.compact_iri(v, :position => position, :depth => @depth)}
          debug {" => compacted value(#{key}): #{compacted_value.inspect}"}
          compacted_value = compacted_value.first if compacted_value.length == 1 && @options[:compactArrays]
          compacted_value
        end
      else
        if value.empty?
          # Make sure that an empty array is preserved
          compacted_key = context.compact_iri(key, :position => :predicate, :depth => @depth)
          next if compacted_key.nil?
          result[compacted_key] = value
        end

        # For each item in value:
        raise ProcessingError, "found #{value.inspect} for #{key} if #{element.inspect}" unless value.is_a?(Array)
        value.each do |item|
          compacted_key = context.compact_iri(key, :position => :predicate, :value => item, :depth => @depth)
          debug {" => compacted key: #{compacted_key.inspect} for #{item.inspect}"}
          next if compacted_key.nil?

          compacted_item = depth {self.compact(item, compacted_key)}
          debug {" => compacted value: #{compacted_value.inspect}"}
        
          case result[compacted_key]
          when Array
            result[compacted_key] << compacted_item
          when nil
            if !compacted_value.is_a?(Array) && context.container(compacted_key) == '@set'
              compacted_item = [compacted_item].compact
              debug {" => as @set: #{compacted_item.inspect}"}
            end
            result[compacted_key] = compacted_item
          else
            result[compacted_key] = [result[compacted_key], compacted_item]
          end
        end
      end
    end
    
    # Re-order result keys
    r = Hash.ordered
    result.keys.sort.each {|k| r[k] = result[k]}
    r
  else
    # For other types, the compacted value is the element value
    debug("compact") {element.class.to_s}
    element
  end
end