json-ld 2.1.7 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +80 -0
- data/VERSION +1 -1
- data/lib/json/ld/compact.rb +56 -10
- data/lib/json/ld/context.rb +100 -38
- data/lib/json/ld/expand.rb +35 -16
- data/lib/json/ld/utils.rb +22 -0
- data/spec/api_spec.rb +1 -2
- data/spec/compact_spec.rb +296 -2
- data/spec/context_spec.rb +101 -29
- data/spec/expand_spec.rb +491 -189
- data/spec/flatten_spec.rb +1 -2
- data/spec/format_spec.rb +1 -2
- data/spec/frame_spec.rb +1 -2
- data/spec/from_rdf_spec.rb +1 -2
- data/spec/reader_spec.rb +1 -2
- data/spec/resource_spec.rb +1 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/streaming_writer_spec.rb +1 -2
- data/spec/suite_compact_spec.rb +2 -3
- data/spec/suite_error_spec.rb +2 -3
- data/spec/suite_expand_spec.rb +2 -3
- data/spec/suite_flatten_spec.rb +2 -4
- data/spec/suite_frame_spec.rb +2 -4
- data/spec/suite_from_rdf_spec.rb +2 -3
- data/spec/suite_helper.rb +1 -2
- data/spec/suite_remote_doc_spec.rb +2 -3
- data/spec/suite_to_rdf_spec.rb +2 -3
- data/spec/to_rdf_spec.rb +1 -2
- data/spec/writer_spec.rb +1 -2
- metadata +131 -89
data/lib/json/ld/expand.rb
CHANGED
@@ -23,7 +23,7 @@ module JSON::LD
|
|
23
23
|
result = case input
|
24
24
|
when Array
|
25
25
|
# If element is an array,
|
26
|
-
is_list = context.container(active_property) ==
|
26
|
+
is_list = context.container(active_property) == %w(@list)
|
27
27
|
value = input.each_with_object([]) do |v, memo|
|
28
28
|
# Initialize expanded item to the result of using this algorithm recursively, passing active context, active property, and item as element.
|
29
29
|
v = expand(v, active_property, context, ordered: ordered)
|
@@ -175,7 +175,7 @@ module JSON::LD
|
|
175
175
|
when Array
|
176
176
|
# Framing allows an array of IRIs, and always puts values in an array
|
177
177
|
raise JsonLdError::InvalidIdValue,
|
178
|
-
"value of @id must be a string
|
178
|
+
"value of @id must be a string unless framing: #{value.inspect}" unless framing
|
179
179
|
context.expand_iri(value, documentRelative: true, quiet: true).to_s
|
180
180
|
value.map do |v|
|
181
181
|
raise JsonLdError::InvalidTypeValue,
|
@@ -187,7 +187,7 @@ module JSON::LD
|
|
187
187
|
"value of @id must be a string unless framing: #{value.inspect}" unless framing
|
188
188
|
raise JsonLdError::InvalidTypeValue,
|
189
189
|
"value of @id must be a an empty object for framing: #{value.inspect}" unless
|
190
|
-
value.empty?
|
190
|
+
value.empty?
|
191
191
|
[{}]
|
192
192
|
else
|
193
193
|
raise JsonLdError::InvalidIdValue,
|
@@ -352,7 +352,7 @@ module JSON::LD
|
|
352
352
|
term_context = context.term_definitions[key].context if context.term_definitions[key]
|
353
353
|
active_context = term_context ? context.parse(term_context) : context
|
354
354
|
container = active_context.container(key)
|
355
|
-
expanded_value = if container ==
|
355
|
+
expanded_value = if container == %w(@language) && value.is_a?(Hash)
|
356
356
|
# Otherwise, if key's container mapping in active context is @language and value is a JSON object then value is expanded from a language map as follows:
|
357
357
|
|
358
358
|
# Set multilingual array to an empty array.
|
@@ -375,17 +375,17 @@ module JSON::LD
|
|
375
375
|
end
|
376
376
|
|
377
377
|
ary
|
378
|
-
elsif CONTAINER_MAPPING_INDEX_ID_TYPE.
|
379
|
-
# Otherwise, if key's container mapping in active context
|
378
|
+
elsif !(CONTAINER_MAPPING_INDEX_ID_TYPE & container).empty? && value.is_a?(Hash)
|
379
|
+
# Otherwise, if key's container mapping in active context contains @index, @id, @type and value is a JSON object then value is expanded from an index map as follows:
|
380
380
|
|
381
381
|
# Set ary to an empty array.
|
382
|
-
|
382
|
+
ary = []
|
383
383
|
|
384
384
|
# For each key-value in the object:
|
385
385
|
keys = ordered ? value.keys.sort : value.keys
|
386
386
|
keys.each do |k|
|
387
|
-
# If container mapping in the active context
|
388
|
-
map_context = active_context.term_definitions[k].context if container
|
387
|
+
# If container mapping in the active context includes @type, and k is a term in the active context having a local context, use that context when expanding values
|
388
|
+
map_context = active_context.term_definitions[k].context if container.include?('@type') && active_context.term_definitions[k]
|
389
389
|
map_context = active_context.parse(map_context) if map_context
|
390
390
|
map_context ||= active_context
|
391
391
|
|
@@ -393,15 +393,25 @@ module JSON::LD
|
|
393
393
|
index_value = expand([value[k]].flatten, key, map_context, ordered: ordered)
|
394
394
|
index_value.each do |item|
|
395
395
|
case container
|
396
|
-
when
|
397
|
-
when
|
396
|
+
when %w(@index) then item['@index'] ||= k
|
397
|
+
when %w(@id)
|
398
398
|
# Expand k document relative
|
399
399
|
expanded_k = active_context.expand_iri(k, documentRelative: true, quiet: true).to_s
|
400
|
-
item[
|
401
|
-
when
|
400
|
+
item['@id'] ||= expanded_k
|
401
|
+
when %w(@type)
|
402
402
|
# Expand k vocabulary relative
|
403
403
|
expanded_k = active_context.expand_iri(k, vocab: true, documentRelative: true, quiet: true).to_s
|
404
|
-
item[
|
404
|
+
item['@type'] = [expanded_k].concat(Array(item['@type']))
|
405
|
+
when %w(@graph @index), %w(@graph @id)
|
406
|
+
# Indexed graph by graph name
|
407
|
+
if !graph?(item)
|
408
|
+
item = [item] unless expanded_value.is_a?(Array)
|
409
|
+
item = {'@graph' => item}
|
410
|
+
end
|
411
|
+
expanded_k = container.include?('@index') ? k :
|
412
|
+
active_context.expand_iri(k, documentRelative: true, quiet: true).to_s
|
413
|
+
# Expand k document relative
|
414
|
+
item[container.include?('@index') ? '@index' : '@id'] ||= k
|
405
415
|
end
|
406
416
|
|
407
417
|
# Append item to expanded value.
|
@@ -422,12 +432,21 @@ module JSON::LD
|
|
422
432
|
#log_debug {" => #{expanded_value.inspect}"}
|
423
433
|
|
424
434
|
# If the container mapping associated to key in active context is @list and expanded value is not already a list object, convert expanded value to a list object by first setting it to an array containing only expanded value if it is not already an array, and then by setting it to a JSON object containing the key-value pair @list-expanded value.
|
425
|
-
if active_context.container(key) ==
|
435
|
+
if active_context.container(key) == %w(@list) && !list?(expanded_value)
|
426
436
|
#log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
|
427
|
-
expanded_value =
|
437
|
+
expanded_value = [expanded_value] unless expanded_value.is_a?(Array)
|
438
|
+
expanded_value = {'@list' => expanded_value}
|
428
439
|
end
|
429
440
|
#log_debug {" => #{expanded_value.inspect}"}
|
430
441
|
|
442
|
+
# convert expanded value to @graph if container specifies it
|
443
|
+
# FIXME value may be a named graph, as well as a simple graph.
|
444
|
+
if active_context.container(key) == %w(@graph) && !graph?(expanded_value)
|
445
|
+
#log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
|
446
|
+
expanded_value = [expanded_value] unless expanded_value.is_a?(Array)
|
447
|
+
expanded_value = {'@graph' => expanded_value}
|
448
|
+
end
|
449
|
+
|
431
450
|
# Otherwise, if the term definition associated to key indicates that it is a reverse property
|
432
451
|
# Spec FIXME: this is not an otherwise.
|
433
452
|
if (td = context.term_definitions[key]) && td.reverse_property
|
data/lib/json/ld/utils.rb
CHANGED
@@ -46,6 +46,27 @@ module JSON::LD
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
##
|
50
|
+
# Is value an expaned @graph?
|
51
|
+
#
|
52
|
+
# Note: A value is a simple graph if all of these hold true:
|
53
|
+
# 1. It is an object.
|
54
|
+
# 2. It has an `@graph` key.
|
55
|
+
# 3. It may have '@context', '@id' or '@index'
|
56
|
+
#
|
57
|
+
# @param [Object] value
|
58
|
+
# @return [Boolean]
|
59
|
+
def graph?(value)
|
60
|
+
value.is_a?(Hash) && (value.keys - UTIL_GRAPH_KEYS) == ['@graph']
|
61
|
+
end
|
62
|
+
##
|
63
|
+
# Is value a simple @graph (lacking @id)?
|
64
|
+
# @param [Object] value
|
65
|
+
# @return [Boolean]
|
66
|
+
def simple_graph?(value)
|
67
|
+
graph?(value) && !value.has_key?('@id')
|
68
|
+
end
|
69
|
+
|
49
70
|
##
|
50
71
|
# Is value an expaned @list?
|
51
72
|
#
|
@@ -184,6 +205,7 @@ module JSON::LD
|
|
184
205
|
end
|
185
206
|
|
186
207
|
private
|
208
|
+
UTIL_GRAPH_KEYS = %w(@context @id @index).freeze
|
187
209
|
|
188
210
|
# Merge the last value into an array based for the specified key if hash is not null and value is not already in that array
|
189
211
|
def merge_value(hash, key, value)
|
data/spec/api_spec.rb
CHANGED
data/spec/compact_spec.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
2
|
+
require_relative 'spec_helper'
|
4
3
|
|
5
4
|
describe JSON::LD::API do
|
6
5
|
let(:logger) {RDF::Spec.logger}
|
@@ -744,6 +743,301 @@ describe JSON::LD::API do
|
|
744
743
|
end
|
745
744
|
end
|
746
745
|
|
746
|
+
context "@container: @graph" do
|
747
|
+
{
|
748
|
+
"Compacts simple graph" => {
|
749
|
+
input: %([{
|
750
|
+
"http://example.org/input": [{
|
751
|
+
"@graph": [{
|
752
|
+
"http://example.org/value": [{"@value": "x"}]
|
753
|
+
}]
|
754
|
+
}]
|
755
|
+
}]),
|
756
|
+
context: %({
|
757
|
+
"@vocab": "http://example.org/",
|
758
|
+
"input": {"@container": "@graph"}
|
759
|
+
}),
|
760
|
+
output: %({
|
761
|
+
"@context": {
|
762
|
+
"@vocab": "http://example.org/",
|
763
|
+
"input": {"@container": "@graph"}
|
764
|
+
},
|
765
|
+
"input": {
|
766
|
+
"value": "x"
|
767
|
+
}
|
768
|
+
})
|
769
|
+
},
|
770
|
+
"Compacts simple graph with @set" => {
|
771
|
+
input: %([{
|
772
|
+
"http://example.org/input": [{
|
773
|
+
"@graph": [{
|
774
|
+
"http://example.org/value": [{"@value": "x"}]
|
775
|
+
}]
|
776
|
+
}]
|
777
|
+
}]),
|
778
|
+
context: %({
|
779
|
+
"@vocab": "http://example.org/",
|
780
|
+
"input": {"@container": ["@graph", "@set"]}
|
781
|
+
}),
|
782
|
+
output: %({
|
783
|
+
"@context": {
|
784
|
+
"@vocab": "http://example.org/",
|
785
|
+
"input": {"@container": ["@graph", "@set"]}
|
786
|
+
},
|
787
|
+
"input": [{
|
788
|
+
"value": "x"
|
789
|
+
}]
|
790
|
+
})
|
791
|
+
},
|
792
|
+
"Compacts simple graph with @index" => {
|
793
|
+
input: %([{
|
794
|
+
"http://example.org/input": [{
|
795
|
+
"@graph": [{
|
796
|
+
"http://example.org/value": [{"@value": "x"}]
|
797
|
+
}],
|
798
|
+
"@index": "ndx"
|
799
|
+
}]
|
800
|
+
}]),
|
801
|
+
context: %({
|
802
|
+
"@vocab": "http://example.org/",
|
803
|
+
"input": {"@container": "@graph"}
|
804
|
+
}),
|
805
|
+
output: %({
|
806
|
+
"@context": {
|
807
|
+
"@vocab": "http://example.org/",
|
808
|
+
"input": {"@container": "@graph"}
|
809
|
+
},
|
810
|
+
"input": {
|
811
|
+
"value": "x"
|
812
|
+
}
|
813
|
+
})
|
814
|
+
},
|
815
|
+
"Does not compact graph with @id" => {
|
816
|
+
input: %([{
|
817
|
+
"http://example.org/input": [{
|
818
|
+
"@graph": [{
|
819
|
+
"http://example.org/value": [{"@value": "x"}]
|
820
|
+
}],
|
821
|
+
"@id": "http://example.org/id"
|
822
|
+
}]
|
823
|
+
}]),
|
824
|
+
context: %({
|
825
|
+
"@vocab": "http://example.org/",
|
826
|
+
"input": {"@container": "@graph"}
|
827
|
+
}),
|
828
|
+
output: %({
|
829
|
+
"@context": {
|
830
|
+
"@vocab": "http://example.org/",
|
831
|
+
"input": {"@container": "@graph"}
|
832
|
+
},
|
833
|
+
"input": {
|
834
|
+
"@id": "http://example.org/id",
|
835
|
+
"@graph": [{"value": "x"}]
|
836
|
+
}
|
837
|
+
})
|
838
|
+
},
|
839
|
+
}.each_pair do |title, params|
|
840
|
+
it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
|
841
|
+
end
|
842
|
+
|
843
|
+
context "+ @index" do
|
844
|
+
{
|
845
|
+
"Compacts simple graph" => {
|
846
|
+
input: %([{
|
847
|
+
"http://example.org/input": [{
|
848
|
+
"@index": "g1",
|
849
|
+
"@graph": [{
|
850
|
+
"http://example.org/value": [{"@value": "x"}]
|
851
|
+
}]
|
852
|
+
}]
|
853
|
+
}]),
|
854
|
+
context: %({
|
855
|
+
"@vocab": "http://example.org/",
|
856
|
+
"input": {"@container": ["@graph", "@index"]}
|
857
|
+
}),
|
858
|
+
output: %({
|
859
|
+
"@context": {
|
860
|
+
"@vocab": "http://example.org/",
|
861
|
+
"input": {"@container": ["@graph", "@index"]}
|
862
|
+
},
|
863
|
+
"input": {
|
864
|
+
"g1": {"value": "x"}
|
865
|
+
}
|
866
|
+
})
|
867
|
+
},
|
868
|
+
"Compacts simple graph with @set" => {
|
869
|
+
input: %([{
|
870
|
+
"http://example.org/input": [{
|
871
|
+
"@index": "g1",
|
872
|
+
"@graph": [{
|
873
|
+
"http://example.org/value": [{"@value": "x"}]
|
874
|
+
}]
|
875
|
+
}]
|
876
|
+
}]),
|
877
|
+
context: %({
|
878
|
+
"@vocab": "http://example.org/",
|
879
|
+
"input": {"@container": ["@graph", "@index", "@set"]}
|
880
|
+
}),
|
881
|
+
output: %({
|
882
|
+
"@context": {
|
883
|
+
"@vocab": "http://example.org/",
|
884
|
+
"input": {"@container": ["@graph", "@index", "@set"]}
|
885
|
+
},
|
886
|
+
"input": {
|
887
|
+
"g1": [{"value": "x"}]
|
888
|
+
}
|
889
|
+
})
|
890
|
+
},
|
891
|
+
"Does not compact graph with @id" => {
|
892
|
+
input: %([{
|
893
|
+
"http://example.org/input": [{
|
894
|
+
"@graph": [{
|
895
|
+
"http://example.org/value": [{"@value": "x"}]
|
896
|
+
}],
|
897
|
+
"@index": "g1",
|
898
|
+
"@id": "http://example.org/id"
|
899
|
+
}]
|
900
|
+
}]),
|
901
|
+
context: %({
|
902
|
+
"@vocab": "http://example.org/",
|
903
|
+
"input": {"@container": ["@graph", "@index"]}
|
904
|
+
}),
|
905
|
+
output: %({
|
906
|
+
"@context": {
|
907
|
+
"@vocab": "http://example.org/",
|
908
|
+
"input": {"@container": ["@graph", "@index"]}
|
909
|
+
},
|
910
|
+
"input": {
|
911
|
+
"@id": "http://example.org/id",
|
912
|
+
"@index": "g1",
|
913
|
+
"@graph": [{"value": "x"}]
|
914
|
+
}
|
915
|
+
})
|
916
|
+
},
|
917
|
+
}.each_pair do |title, params|
|
918
|
+
it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
|
919
|
+
end
|
920
|
+
end
|
921
|
+
|
922
|
+
context "+ @id" do
|
923
|
+
{
|
924
|
+
"Compacts simple graph" => {
|
925
|
+
input: %([{
|
926
|
+
"http://example.org/input": [{
|
927
|
+
"@graph": [{
|
928
|
+
"http://example.org/value": [{"@value": "x"}]
|
929
|
+
}]
|
930
|
+
}]
|
931
|
+
}]),
|
932
|
+
context: %({
|
933
|
+
"@vocab": "http://example.org/",
|
934
|
+
"input": {"@container": ["@graph", "@id"]}
|
935
|
+
}),
|
936
|
+
output: %({
|
937
|
+
"@context": {
|
938
|
+
"@vocab": "http://example.org/",
|
939
|
+
"input": {"@container": ["@graph", "@id"]}
|
940
|
+
},
|
941
|
+
"input": {
|
942
|
+
"_:b0": {"value": "x"}
|
943
|
+
}
|
944
|
+
})
|
945
|
+
},
|
946
|
+
"Compacts simple graph with @set" => {
|
947
|
+
input: %([{
|
948
|
+
"http://example.org/input": [{
|
949
|
+
"@graph": [{
|
950
|
+
"http://example.org/value": [{"@value": "x"}]
|
951
|
+
}]
|
952
|
+
}]
|
953
|
+
}]),
|
954
|
+
context: %({
|
955
|
+
"@vocab": "http://example.org/",
|
956
|
+
"input": {"@container": ["@graph", "@id", "@set"]}
|
957
|
+
}),
|
958
|
+
output: %({
|
959
|
+
"@context": {
|
960
|
+
"@vocab": "http://example.org/",
|
961
|
+
"input": {"@container": ["@graph", "@id", "@set"]}
|
962
|
+
},
|
963
|
+
"input": {"_:b0": [{"value": "x"}]}
|
964
|
+
})
|
965
|
+
},
|
966
|
+
"Compacts simple graph with @index" => {
|
967
|
+
input: %([{
|
968
|
+
"http://example.org/input": [{
|
969
|
+
"@graph": [{
|
970
|
+
"http://example.org/value": [{"@value": "x"}]
|
971
|
+
}],
|
972
|
+
"@index": "ndx"
|
973
|
+
}]
|
974
|
+
}]),
|
975
|
+
context: %({
|
976
|
+
"@vocab": "http://example.org/",
|
977
|
+
"input": {"@container": ["@graph", "@id"]}
|
978
|
+
}),
|
979
|
+
output: %({
|
980
|
+
"@context": {
|
981
|
+
"@vocab": "http://example.org/",
|
982
|
+
"input": {"@container": ["@graph", "@id"]}
|
983
|
+
},
|
984
|
+
"input": {
|
985
|
+
"_:b0": {"value": "x"}
|
986
|
+
}
|
987
|
+
})
|
988
|
+
},
|
989
|
+
"Compacts graph with @id" => {
|
990
|
+
input: %([{
|
991
|
+
"http://example.org/input": [{
|
992
|
+
"@graph": [{
|
993
|
+
"http://example.org/value": [{"@value": "x"}]
|
994
|
+
}],
|
995
|
+
"@id": "http://example.org/id"
|
996
|
+
}]
|
997
|
+
}]),
|
998
|
+
context: %({
|
999
|
+
"@vocab": "http://example.org/",
|
1000
|
+
"input": {"@container": ["@graph", "@id"]}
|
1001
|
+
}),
|
1002
|
+
output: %({
|
1003
|
+
"@context": {
|
1004
|
+
"@vocab": "http://example.org/",
|
1005
|
+
"input": {"@container": ["@graph", "@id"]}
|
1006
|
+
},
|
1007
|
+
"input": {
|
1008
|
+
"http://example.org/id" : {"value": "x"}
|
1009
|
+
}
|
1010
|
+
})
|
1011
|
+
},
|
1012
|
+
"Compacts graph with @id and @set" => {
|
1013
|
+
input: %([{
|
1014
|
+
"http://example.org/input": [{
|
1015
|
+
"@graph": [{
|
1016
|
+
"http://example.org/value": [{"@value": "x"}]
|
1017
|
+
}],
|
1018
|
+
"@id": "http://example.org/id"
|
1019
|
+
}]
|
1020
|
+
}]),
|
1021
|
+
context: %({
|
1022
|
+
"@vocab": "http://example.org/",
|
1023
|
+
"input": {"@container": ["@graph", "@id", "@set"]}
|
1024
|
+
}),
|
1025
|
+
output: %({
|
1026
|
+
"@context": {
|
1027
|
+
"@vocab": "http://example.org/",
|
1028
|
+
"input": {"@container": ["@graph", "@id", "@set"]}
|
1029
|
+
},
|
1030
|
+
"input": {
|
1031
|
+
"http://example.org/id" : [{"value": "x"}]
|
1032
|
+
}
|
1033
|
+
})
|
1034
|
+
},
|
1035
|
+
}.each_pair do |title, params|
|
1036
|
+
it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
|
1037
|
+
end
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
|
747
1041
|
context "@nest" do
|
748
1042
|
{
|
749
1043
|
"Indexes to @nest for property with @container: @nest" => {
|