json-ld 2.1.2 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1a24e95bd68b625c54093b4d41f19f264147cf28
4
- data.tar.gz: db126bfc8830837da1cd36329451443ff610a624
3
+ metadata.gz: 2089bccb0e9e19a4f349908dd6456c908a653e9f
4
+ data.tar.gz: 97c3aef58769ce0c233d85e027fca1e0bb660fd6
5
5
  SHA512:
6
- metadata.gz: 0ecf9190facd71bb800cdce96eb8069da3359afe1816ab381bc21735828f0ca8c19d066850ca30339c405b0408955438d53b29e290efde9fdbe1be72430ae43a
7
- data.tar.gz: 3b6bf6517ba347b8b73679c213c2c625b0957990cf9c1a2ecc220a85b4ec0340c3299d79fa8c10453a194bf9a2a5195ac8cf5e001cd96324241d899d07e99fba
6
+ metadata.gz: 625898c1cbb1f6dbe8a2a4fce2067f6702a3205911099878d1fecaad97dd18c014ff0dcea995d90885e13fff67f04362a6b8efe1fdbad8ac7c5a62b09070c28d
7
+ data.tar.gz: 8501add1dca033f2b2c61966bf4425aeed1f1852b39b1b9dabeb2253510c56b87b70d8c480652dcc0ac4c28ef078218915554c2037c747ac939b06b65d6d4781
data/README.md CHANGED
@@ -14,6 +14,8 @@ JSON::LD can now be used to create a _context_ from an RDFS/OWL definition, and
14
14
 
15
15
  If the [jsonlint][] gem is installed, it will be used when validating an input document.
16
16
 
17
+ [Implementation Report](file.earl.html)
18
+
17
19
  Install with `gem install json-ld`
18
20
 
19
21
  ### MultiJson parser
@@ -45,9 +47,9 @@ This gem implements an optimized streaming writer used for generating JSON-LD fr
45
47
  JSON::LD::API.expand(input) =>
46
48
 
47
49
  [{
48
- "http://xmlns.com/foaf/0.1/name": ["Manu Sporny"],
49
- "http://xmlns.com/foaf/0.1/homepage": ["http://manu.sporny.org/"],
50
- "http://xmlns.com/foaf/0.1/avatar": ["http://twitter.com/account/profile_image/manusporny"]
50
+ "http://xmlns.com/foaf/0.1/name": [{"@value"=>"Manu Sporny"}],
51
+ "http://xmlns.com/foaf/0.1/homepage": [{"@value"=>"http://manu.sporny.org/"}],
52
+ "http://xmlns.com/foaf/0.1/avatar": [{"@value": "http://twitter.com/account/profile_image/manusporny"}]
51
53
  }]
52
54
 
53
55
  ### Compact a Document
@@ -261,6 +263,107 @@ A context may be serialized to Ruby to speed this process using `Context#to_rb`.
261
263
 
262
264
  As JSON-LD may come from many different sources, included as an embedded script tag within an HTML document, the RDF Reader will strip input before the leading `{` or `[` and after the trailing `}` or `]`.
263
265
 
266
+ ## Extensions from JSON-LD 1.0
267
+ This implementation is being used as a test-bed for features planned for an upcoming JSON-LD 1.1 Community release.
268
+
269
+ ### Scoped Contexts
270
+ A term definition can include `@context`, which is applied to values of that object. This is also used when compacting. Taken together, this allows framing to effectively include context definitions more deeply within the framed structure.
271
+
272
+ {
273
+ "@context": {
274
+ "ex": "http://example.com/",
275
+ "foo": {
276
+ "@id": "ex:foo",
277
+ "@type": "@vocab"
278
+ "@context": {
279
+ "Bar": "ex:Bar",
280
+ "Baz": "ex:Baz"
281
+ }
282
+ }
283
+ },
284
+ "foo": "Bar"
285
+ }
286
+
287
+ ### @id and @type maps
288
+ The value of `@container` in a term definition can include `@id` or `@type`, in addition to `@set`, `@list`, `@language`, and `@index`. This allows value indexing based on either the `@id` or `@type` of associated objects.
289
+
290
+ {
291
+ "@context": {
292
+ "@vocab": "http://example/",
293
+ "idmap": {"@container": "@id"}
294
+ },
295
+ "idmap": {
296
+ "http://example.org/foo": {"label": "Object with @id <foo>"},
297
+ "_:bar": {"label": "Object with @id _:bar"}
298
+ }
299
+ }
300
+
301
+ ### Transparent Nesting
302
+ Many JSON APIs separate properties from their entities using an intermediate object. For example, a set of possible labels may be grouped under a common property:
303
+
304
+ {
305
+ "@context": {
306
+ "skos": "http://www.w3.org/2004/02/skos/core#",
307
+ "labels": "@nest",
308
+ "main_label": {"@id": "skos:prefLabel"},
309
+ "other_label": {"@id": "skos:altLabel"},
310
+ "homepage": {"@id":"http://schema.org/description", "@type":"@id"}
311
+ },
312
+ "@id":"http://example.org/myresource",
313
+ "homepage": "http://example.org",
314
+ "labels": {
315
+ "main_label": "This is the main label for my resource",
316
+ "other_label": "This is the other label"
317
+ }
318
+ }
319
+
320
+ In this case, the `labels` property is semantically meaningless. Defining it as equivalent to `@nest` causes it to be ignored when expanding, making it equivalent to the following:
321
+
322
+ {
323
+ "@context": {
324
+ "skos": "http://www.w3.org/2004/02/skos/core#",
325
+ "labels": "@nest",
326
+ "main_label": {"@id": "skos:prefLabel"},
327
+ "other_label": {"@id": "skos:altLabel"},
328
+ "homepage": {"@id":"http://schema.org/description", "@type":"@id"}
329
+ },
330
+ "@id":"http://example.org/myresource",
331
+ "homepage": "http://example.org",
332
+ "main_label": "This is the main label for my resource",
333
+ "other_label": "This is the other label"
334
+ }
335
+
336
+ Similarly, properties may be marked with "@nest": "nest-term", to cause them to be nested. Note that the `@nest` keyword can also be aliased in the context.
337
+
338
+ {
339
+ "@context": {
340
+ "skos": "http://www.w3.org/2004/02/skos/core#",
341
+ "labels": "@nest",
342
+ "main_label": {"@id": "skos:prefLabel", "@nest": "labels"},
343
+ "other_label": {"@id": "skos:altLabel", "@nest": "labels"},
344
+ "homepage": {"@id":"http://schema.org/description", "@type":"@id"}
345
+ },
346
+ "@id":"http://example.org/myresource",
347
+ "homepage": "http://example.org",
348
+ "labels": {
349
+ "main_label": "This is the main label for my resource",
350
+ "other_label": "This is the other label"
351
+ }
352
+ }
353
+
354
+ In this way, nesting survives round-tripping through expansion, and framed output can include nested properties.
355
+
356
+ ### Framing Updates
357
+ The [JSON-LD Framing 1.1 Specification]() improves on previous un-released versions.
358
+
359
+ * [More Specific Frame matching](https://github.com/json-ld/json-ld.org/issues/110) – Allows framing to extend to elements of value objects, and objects are matched through recursive frame matching. `{}` is used as a wildcard, and `[]` as matching nothing.
360
+ * [Graph framing](https://github.com/json-ld/json-ld.org/issues/118) – previously, only the merged graph can be framed, this update allows arbitrary graphs to be framed.
361
+ * Use `@graph` in frame, matches the default graph, not the merged graph.
362
+ * Use `@graph` in property value, causes the apropriatly named graph to be used for filling in values.
363
+ * [Reverse properties](https://github.com/json-ld/json-ld.org/issues/311) – `@reverse` (or a property defined with `@reverse`) can cause matching values to be included, allowing a matched object to include reverse references to any objects referencing it.
364
+ * [@omitDefault behavior](https://github.com/json-ld/json-ld.org/issues/389) – In addition to `true` and `false`, `@omitDefault` can take `@last`, `@always`, `@never`, and `@link`.
365
+ * [multiple `@id` matching](https://github.com/json-ld/json-ld.org/issues/424) – A frame can match based on one or more specific object `@id` values.
366
+
264
367
  ## Documentation
265
368
  Full documentation available on [RubyDoc](http://rubydoc.info/gems/json-ld/file/README.md)
266
369
 
@@ -286,7 +389,7 @@ Note, the API method signatures differed in versions before 1.0, in that they al
286
389
 
287
390
  ## Dependencies
288
391
  * [Ruby](http://ruby-lang.org/) (>= 2.2.2)
289
- * [RDF.rb](http://rubygems.org/gems/rdf) (>= 2.0)
392
+ * [RDF.rb](http://rubygems.org/gems/rdf) (>= 2.2)
290
393
  * [JSON](https://rubygems.org/gems/json) (>= 1.5)
291
394
 
292
395
  ## Installation
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.2
1
+ 2.1.3
@@ -34,7 +34,7 @@ module JSON
34
34
  autoload :Resource, 'json/ld/resource'
35
35
  autoload :VERSION, 'json/ld/version'
36
36
  autoload :Writer, 'json/ld/writer'
37
-
37
+
38
38
  # Initial context
39
39
  # @see http://json-ld.org/spec/latest/json-ld-api/#appendix-b
40
40
  INITIAL_CONTEXT = {
@@ -53,12 +53,14 @@ module JSON
53
53
  @graph
54
54
  @language
55
55
  @list
56
+ @nest
56
57
  @omitDefault
57
58
  @requireAll
58
59
  @reverse
59
60
  @set
60
61
  @type
61
62
  @value
63
+ @version
62
64
  @vocab
63
65
  ).freeze
64
66
 
@@ -87,7 +89,7 @@ module JSON
87
89
  array_nl: "\n"
88
90
  )
89
91
 
90
- class JsonLdError < Exception
92
+ class JsonLdError < StandardError
91
93
  def to_s
92
94
  "#{self.class.instance_variable_get :@code}: #{super}"
93
95
  end
@@ -95,46 +97,49 @@ module JSON
95
97
  self.class.instance_variable_get :@code
96
98
  end
97
99
 
98
- class LoadingDocumentFailed < JsonLdError; @code = "loading document failed"; end
99
- class ListOfLists < JsonLdError; @code = "list of lists"; end
100
- class InvalidIndexValue < JsonLdError; @code = "invalid @index value"; end
100
+ class CollidingKeywords < JsonLdError; @code = "colliding keywords"; end
101
+ class CompactionToListOfLists < JsonLdError; @code = "compaction to list of lists"; end
101
102
  class ConflictingIndexes < JsonLdError; @code = "conflicting indexes"; end
102
- class InvalidIdValue < JsonLdError; @code = "invalid @id value"; end
103
- class InvalidLocalContext < JsonLdError; @code = "invalid local context"; end
104
- class MultipleContextLinkHeaders < JsonLdError; @code = "multiple context link headers"; end
105
- class LoadingRemoteContextFailed < JsonLdError; @code = "loading remote context failed"; end
106
- class InvalidRemoteContext < JsonLdError; @code = "invalid remote context"; end
107
- class RecursiveContextInclusion < JsonLdError; @code = "recursive context inclusion"; end
103
+ class CyclicIRIMapping < JsonLdError; @code = "cyclic IRI mapping"; end
108
104
  class InvalidBaseIRI < JsonLdError; @code = "invalid base IRI"; end
109
- class InvalidVocabMapping < JsonLdError; @code = "invalid vocab mapping"; end
105
+ class InvalidContainerMapping < JsonLdError; @code = "invalid container mapping"; end
110
106
  class InvalidDefaultLanguage < JsonLdError; @code = "invalid default language"; end
111
- class KeywordRedefinition < JsonLdError; @code = "keyword redefinition"; end
112
- class InvalidTermDefinition < JsonLdError; @code = "invalid term definition"; end
113
- class InvalidReverseProperty < JsonLdError; @code = "invalid reverse property"; end
107
+ class InvalidIdValue < JsonLdError; @code = "invalid @id value"; end
108
+ class InvalidIndexValue < JsonLdError; @code = "invalid @index value"; end
109
+ class InvalidVersionValue < JsonLdError; @code = "invalid @version value"; end
114
110
  class InvalidIRIMapping < JsonLdError; @code = "invalid IRI mapping"; end
115
- class CyclicIRIMapping < JsonLdError; @code = "cyclic IRI mapping"; end
116
111
  class InvalidKeywordAlias < JsonLdError; @code = "invalid keyword alias"; end
117
- class InvalidTypeMapping < JsonLdError; @code = "invalid type mapping"; end
118
112
  class InvalidLanguageMapping < JsonLdError; @code = "invalid language mapping"; end
119
- class CollidingKeywords < JsonLdError; @code = "colliding keywords"; end
120
- class InvalidContainerMapping < JsonLdError; @code = "invalid container mapping"; end
121
- class InvalidTypeValue < JsonLdError; @code = "invalid type value"; end
122
- class InvalidValueObject < JsonLdError; @code = "invalid value object"; end
123
- class InvalidValueObjectValue < JsonLdError; @code = "invalid value object value"; end
113
+ class InvalidLanguageMapValue < JsonLdError; @code = "invalid language map value"; end
124
114
  class InvalidLanguageTaggedString < JsonLdError; @code = "invalid language-tagged string"; end
125
115
  class InvalidLanguageTaggedValue < JsonLdError; @code = "invalid language-tagged value"; end
126
- class InvalidTypedValue < JsonLdError; @code = "invalid typed value"; end
127
- class InvalidSetOrListObject < JsonLdError; @code = "invalid set or list object"; end
128
- class InvalidLanguageMapValue < JsonLdError; @code = "invalid language map value"; end
129
- class CompactionToListOfLists < JsonLdError; @code = "compaction to list of lists"; end
116
+ class InvalidLocalContext < JsonLdError; @code = "invalid local context"; end
117
+ class InvalidNestValue < JsonLdError; @code = "invalid @nest value"; end
118
+ class InvalidRemoteContext < JsonLdError; @code = "invalid remote context"; end
119
+ class InvalidReverseProperty < JsonLdError; @code = "invalid reverse property"; end
130
120
  class InvalidReversePropertyMap < JsonLdError; @code = "invalid reverse property map"; end
131
- class InvalidReverseValue < JsonLdError; @code = "invalid @reverse value"; end
132
121
  class InvalidReversePropertyValue < JsonLdError; @code = "invalid reverse property value"; end
133
- end
134
-
135
- class InvalidFrame < Exception
136
- class MultipleEmbeds < InvalidFrame; end
137
- class Syntax < InvalidFrame; end
122
+ class InvalidReverseValue < JsonLdError; @code = "invalid @reverse value"; end
123
+ class InvalidScopedContext < JsonLdError; @code = "invalid scoped context"; end
124
+ class InvalidSetOrListObject < JsonLdError; @code = "invalid set or list object"; end
125
+ class InvalidTermDefinition < JsonLdError; @code = "invalid term definition"; end
126
+ class InvalidTypedValue < JsonLdError; @code = "invalid typed value"; end
127
+ class InvalidTypeMapping < JsonLdError; @code = "invalid type mapping"; end
128
+ class InvalidTypeValue < JsonLdError; @code = "invalid type value"; end
129
+ class InvalidValueObject < JsonLdError; @code = "invalid value object"; end
130
+ class InvalidValueObjectValue < JsonLdError; @code = "invalid value object value"; end
131
+ class InvalidVocabMapping < JsonLdError; @code = "invalid vocab mapping"; end
132
+ class KeywordRedefinition < JsonLdError; @code = "keyword redefinition"; end
133
+ class ListOfLists < JsonLdError; @code = "list of lists"; end
134
+ class LoadingDocumentFailed < JsonLdError; @code = "loading document failed"; end
135
+ class LoadingRemoteContextFailed < JsonLdError; @code = "loading remote context failed"; end
136
+ class MultipleContextLinkHeaders < JsonLdError; @code = "multiple context link headers"; end
137
+ class ProcessingModeConflict < JsonLdError; @code = "processing mode conflict"; end
138
+ class RecursiveContextInclusion < JsonLdError; @code = "recursive context inclusion"; end
139
+ class InvalidFrame < JsonLdError
140
+ class MultipleEmbeds < InvalidFrame; end
141
+ class Syntax < InvalidFrame; end
142
+ end
138
143
  end
139
144
  end
140
- end
145
+ end
@@ -69,10 +69,12 @@ module JSON::LD
69
69
  # A context that is used to initialize the active context when expanding a document.
70
70
  # @option options [Boolean, String, RDF::URI] :flatten
71
71
  # If set to a value that is not `false`, the JSON-LD processor must modify the output of the Compaction Algorithm or the Expansion Algorithm by coalescing all properties associated with each subject via the Flattening Algorithm. The value of `flatten must` be either an _IRI_ value representing the name of the graph to flatten, or `true`. If the value is `true`, then the first graph encountered in the input document is selected and flattened.
72
- # @option options [String] :processingMode ("json-ld-1.1")
72
+ # @option options [String] :processingMode
73
73
  # Processing mode, json-ld-1.0 or json-ld-1.1. Also can have other values:
74
74
  #
75
75
  # * json-ld-1.1-expand-frame – special frame expansion mode.
76
+ #
77
+ # If `processingMode` is not specified, a mode of `json-ld-1.0` or `json-ld-1.1` is set, the context used for `expansion` or `compaction`.
76
78
  # @option options [Boolean] :rename_bnodes (true)
77
79
  # Rename bnodes as part of expansion, or keep them the same.
78
80
  # @option options [Boolean] :unique_bnodes (false)
@@ -88,10 +90,8 @@ module JSON::LD
88
90
  @options = {
89
91
  compactArrays: true,
90
92
  rename_bnodes: true,
91
- processingMode: "json-ld-1.1",
92
93
  documentLoader: self.class.method(:documentLoader)
93
94
  }
94
- @options[:validate] = %w(json-ld-1.0 json-ld-1.1).include?(@options[:processingMode])
95
95
  @options = @options.merge(options)
96
96
  @namer = options[:unique_bnodes] ? BlankNodeUniqer.new : (@options[:rename_bnodes] ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
97
97
 
@@ -135,6 +135,10 @@ module JSON::LD
135
135
  context ||= (@value['@context'] if @value.is_a?(Hash)) || context_ref
136
136
  @context = Context.parse(context || {}, @options)
137
137
 
138
+ # If not set explicitly, the context figures out the processing mode
139
+ @options[:processingMode] ||= @context.processingMode
140
+ @options[:validate] ||= %w(json-ld-1.0 json-ld-1.1).include?(@options[:processingMode])
141
+
138
142
  if block_given?
139
143
  case block.arity
140
144
  when 0, -1 then instance_eval(&block)
@@ -207,7 +211,7 @@ module JSON::LD
207
211
  expanded_input = options[:expanded] ? input : API.expand(input, options)
208
212
 
209
213
  API.new(expanded_input, context, options) do
210
- log_debug(".compact") {"expanded input: #{expanded.to_json(JSON_STATE) rescue 'malformed json'}"}
214
+ log_debug(".compact") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
211
215
  result = compact(value)
212
216
 
213
217
  # xxx) Add the given context to the output
@@ -298,6 +302,7 @@ module JSON::LD
298
302
  # @option options [Boolean] :omitDefault (false)
299
303
  # a flag specifying that properties that are missing from the JSON-LD input should be omitted from the output.
300
304
  # @option options [Boolean] :expanded Input is already expanded
305
+ # @option options [Boolean] :pruneBlankNodeIdentifiers (true) removes blank node identifiers that are only used once.
301
306
  # @yield jsonld
302
307
  # @yieldparam [Hash] jsonld
303
308
  # The framed JSON-LD document
@@ -309,13 +314,14 @@ module JSON::LD
309
314
  def self.frame(input, frame, options = {})
310
315
  result = nil
311
316
  options = {
312
- base: input.is_a?(String) ? input : '',
313
- compactArrays: true,
314
- embed: '@last',
315
- explicit: false,
316
- requireAll: true,
317
- omitDefault: false,
318
- documentLoader: method(:documentLoader)
317
+ base: (input if input.is_a?(String)),
318
+ compactArrays: true,
319
+ embed: '@last',
320
+ explicit: false,
321
+ requireAll: true,
322
+ omitDefault: false,
323
+ pruneBlankNodeIdentifiers: true,
324
+ documentLoader: method(:documentLoader)
319
325
  }.merge!(options)
320
326
 
321
327
  framing_state = {
@@ -365,7 +371,12 @@ module JSON::LD
365
371
 
366
372
  result = []
367
373
  frame(framing_state, framing_state[:subjects].keys.sort, (expanded_frame.first || {}), options.merge(parent: result))
368
-
374
+
375
+ # Count blank node identifiers used in the document, if pruning
376
+ bnodes_to_clear = if options[:pruneBlankNodeIdentifiers]
377
+ count_blank_node_identifiers(result).collect {|k, v| k if v == 1}.compact
378
+ end
379
+
369
380
  # Initalize context from frame
370
381
  @context = @context.parse(frame['@context'])
371
382
  # Compact result
@@ -376,7 +387,7 @@ module JSON::LD
376
387
  kwgraph = context.compact_iri('@graph', quiet: true)
377
388
  result = context.serialize.merge({kwgraph => compacted})
378
389
  log_debug(".frame") {"after compact: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
379
- result = cleanup_preserve(result)
390
+ result = cleanup_preserve(result, bnodes_to_clear || [])
380
391
  end
381
392
 
382
393
  block_given? ? yield(result) : result
@@ -450,6 +461,7 @@ module JSON::LD
450
461
  # @option options (see #initialize)
451
462
  # @option options [Boolean] :useRdfType (false)
452
463
  # If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
464
+ # @option options [Boolean] :useNativeTypes (false) use native representations
453
465
  # @yield jsonld
454
466
  # @yieldparam [Hash] jsonld
455
467
  # The JSON-LD document in expanded form
@@ -458,10 +470,11 @@ module JSON::LD
458
470
  # If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
459
471
  def self.fromRdf(input, options = {}, &block)
460
472
  useRdfType = options.fetch(:useRdfType, false)
473
+ useNativeTypes = options.fetch(:useNativeTypes, false)
461
474
  result = nil
462
475
 
463
476
  API.new(nil, nil, options) do |api|
464
- result = api.from_statements(input, useRdfType: useRdfType)
477
+ result = api.from_statements(input, useRdfType: useRdfType, useNativeTypes: useNativeTypes)
465
478
  end
466
479
 
467
480
  block_given? ? yield(result) : result
@@ -16,6 +16,12 @@ module JSON::LD
16
16
  #else
17
17
  # log_debug("compact") {"property: #{property.inspect}"}
18
18
  #end
19
+
20
+ # If the term definition for active property itself contains a context, use that for compacting values.
21
+ input_context = self.context
22
+ td = self.context.term_definitions[property] if property
23
+ self.context = (td && td.context && self.context.parse(td.context)) || input_context
24
+
19
25
  case element
20
26
  when Array
21
27
  #log_debug("") {"Array #{element.inspect}"}
@@ -46,9 +52,9 @@ module JSON::LD
46
52
  end
47
53
 
48
54
  inside_reverse = property == '@reverse'
49
- result = {}
55
+ result, nest_result = {}, nil
50
56
 
51
- element.each_key do |expanded_property|
57
+ element.keys.sort.each do |expanded_property|
52
58
  expanded_value = element[expanded_property]
53
59
  #log_debug("") {"#{expanded_property}: #{expanded_value.inspect}"}
54
60
 
@@ -56,6 +62,15 @@ module JSON::LD
56
62
  compacted_value = [expanded_value].flatten.compact.map do |expanded_type|
57
63
  context.compact_iri(expanded_type, vocab: (expanded_property == '@type'), log_depth: @options[:log_depth])
58
64
  end
65
+
66
+ # If key is @type and any compacted value is a term having a local context, overlay that context.
67
+ if expanded_property == '@type'
68
+ compacted_value.each do |term|
69
+ term_context = self.context.term_definitions[term].context if context.term_definitions[term]
70
+ self.context = context.parse(term_context) if term_context
71
+ end
72
+ end
73
+
59
74
  compacted_value = compacted_value.first if compacted_value.length == 1
60
75
 
61
76
  al = context.compact_iri(expanded_property, vocab: true, quiet: true)
@@ -72,6 +87,7 @@ module JSON::LD
72
87
  value = [value] if !value.is_a?(Array) &&
73
88
  (context.container(prop) == '@set' || !@options[:compactArrays])
74
89
  #log_debug("") {"merge #{prop} => #{value.inspect}"}
90
+
75
91
  merge_compacted_value(result, prop, value)
76
92
  compacted_value.delete(prop)
77
93
  end
@@ -106,8 +122,14 @@ module JSON::LD
106
122
  reverse: inside_reverse,
107
123
  log_depth: @options[:log_depth])
108
124
 
109
- iap = result[item_active_property] ||= []
110
- result[item_active_property] = [iap] unless iap.is_a?(Array)
125
+ if nest_prop = context.nest(item_active_property)
126
+ result[nest_prop] ||= {}
127
+ iap = result[result[nest_prop]] ||= []
128
+ result[nest_prop][item_active_property] = [iap] unless iap.is_a?(Array)
129
+ else
130
+ iap = result[item_active_property] ||= []
131
+ result[item_active_property] = [iap] unless iap.is_a?(Array)
132
+ end
111
133
  end
112
134
 
113
135
  # At this point, expanded value must be an array due to the Expansion algorithm.
@@ -119,6 +141,14 @@ module JSON::LD
119
141
  reverse: inside_reverse,
120
142
  log_depth: @options[:log_depth])
121
143
 
144
+
145
+ nest_result = if nest_prop = context.nest(item_active_property)
146
+ # FIXME??: It's possible that nest_prop will be used both for nesting, and for values of @nest
147
+ result[nest_prop] ||= {}
148
+ else
149
+ result
150
+ end
151
+
122
152
  container = context.container(item_active_property)
123
153
  value = list?(expanded_item) ? expanded_item['@list'] : expanded_item
124
154
  compacted_item = compact(value, property: item_active_property)
@@ -135,14 +165,36 @@ module JSON::LD
135
165
  end
136
166
  else
137
167
  raise JsonLdError::CompactionToListOfLists,
138
- "key cannot have more than one list value" if result.has_key?(item_active_property)
168
+ "key cannot have more than one list value" if nest_result.has_key?(item_active_property)
139
169
  end
140
170
  end
141
171
 
142
- if %w(@language @index).include?(container)
143
- map_object = result[item_active_property] ||= {}
144
- compacted_item = compacted_item['@value'] if container == '@language' && value?(compacted_item)
145
- map_key = expanded_item[container]
172
+ if %w(@language @index @id @type).include?(container)
173
+ map_object = nest_result[item_active_property] ||= {}
174
+ compacted_item = case container
175
+ when '@id'
176
+ id_prop = context.compact_iri('@id', vocab: true, quiet: true)
177
+ map_key = compacted_item[id_prop]
178
+ map_key = context.compact_iri(map_key, quiet: true)
179
+ compacted_item.delete(id_prop)
180
+ compacted_item
181
+ when '@index'
182
+ map_key = expanded_item[container]
183
+ compacted_item
184
+ when '@language'
185
+ map_key = expanded_item[container]
186
+ value?(expanded_item) ? expanded_item['@value'] : compacted_item
187
+ when '@type'
188
+ type_prop = context.compact_iri('@type', vocab: true, quiet: true)
189
+ map_key, *types = Array(compacted_item[type_prop])
190
+ map_key = context.compact_iri(map_key, vocab: true, quiet: true)
191
+ case types.length
192
+ when 0 then compacted_item.delete(type_prop)
193
+ when 1 then compacted_item[type_prop] = types.first
194
+ else compacted_item[type_prop] = types
195
+ end
196
+ compacted_item
197
+ end
146
198
  merge_compacted_value(map_object, map_key, compacted_item)
147
199
  else
148
200
  compacted_item = [compacted_item] if
@@ -151,7 +203,7 @@ module JSON::LD
151
203
  %w(@set @list).include?(container) ||
152
204
  %w(@list @graph).include?(expanded_property)
153
205
  )
154
- merge_compacted_value(result, item_active_property, compacted_item)
206
+ merge_compacted_value(nest_result, item_active_property, compacted_item)
155
207
  end
156
208
  end
157
209
  end
@@ -163,6 +215,9 @@ module JSON::LD
163
215
  #log_debug("compact") {element.class.to_s}
164
216
  element
165
217
  end
218
+
219
+ ensure
220
+ self.context = input_context
166
221
  end
167
222
  end
168
223
  end