Skip to content

Commit 586bfe0

Browse files
committed
Prune BNodes before compaction.
1 parent b2675a1 commit 586bfe0

File tree

8 files changed

+205
-11
lines changed

8 files changed

+205
-11
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"ex:claim": [
4+
{
5+
"@graph": [
6+
{
7+
"@id": "ex:1",
8+
"https://example.com#test": [
9+
{
10+
"@value": "foo"
11+
}
12+
]
13+
}
14+
]
15+
}
16+
]
17+
}
18+
]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"@context": {
3+
"@version": 1.1,
4+
"@vocab": "https://example.com#",
5+
"ex": "http://example.org/",
6+
"claim": {
7+
"@id": "ex:claim",
8+
"@container": "@graph"
9+
},
10+
"id": "@id"
11+
},
12+
"claim": {}
13+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"@context": {
3+
"@version": 1.1,
4+
"@vocab": "https://example.com#",
5+
"ex": "http://example.org/",
6+
"claim": {
7+
"@id": "ex:claim",
8+
"@container": "@graph"
9+
},
10+
"id": "@id"
11+
},
12+
"@graph": [
13+
{
14+
"claim": {
15+
"@graph": {
16+
"id": "ex:1",
17+
"test": "foo"
18+
}
19+
}
20+
}
21+
]
22+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"@context": {
3+
"@version": 1.1,
4+
"@vocab": "https://example.com#",
5+
"ex": "http://example.org/",
6+
"claim": {
7+
"@id": "ex:claim",
8+
"@container": "@graph"
9+
},
10+
"id": "@id"
11+
},
12+
"claim": {
13+
"id": "ex:1",
14+
"test": "foo"
15+
}
16+
}

lib/json/ld/api.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ def self.frame(input, frame, expanded: false, **options)
375375
expanded_frame = API.expand(frame, framing: true, **options)
376376

377377
# Initialize input using frame as context
378-
API.new(expanded_input, nil, no_default_base: true, **options) do
378+
API.new(expanded_input, frame['@context'], no_default_base: true, **options) do
379379
log_debug(".frame") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
380380
log_debug(".frame") {"expanded frame: #{expanded_frame.to_json(JSON_STATE) rescue 'malformed json'}"}
381381

@@ -399,8 +399,9 @@ def self.frame(input, frame, expanded: false, **options)
399399
frame(framing_state, framing_state[:subjects].keys.sort, (expanded_frame.first || {}), parent: result, **options)
400400

401401
# Count blank node identifiers used in the document, if pruning
402-
bnodes_to_clear = if options[:pruneBlankNodeIdentifiers]
403-
count_blank_node_identifiers(result).collect {|k, v| k if v == 1}.compact
402+
if options[:pruneBlankNodeIdentifiers]
403+
bnodes_to_clear = count_blank_node_identifiers(result).collect {|k, v| k if v == 1}.compact
404+
result = prune_bnodes(result, bnodes_to_clear)
404405
end
405406

406407
# Initalize context from frame
@@ -417,7 +418,7 @@ def self.frame(input, frame, expanded: false, **options)
417418
context.serialize.merge({kwgraph => compacted})
418419
end
419420
log_debug(".frame") {"after compact: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
420-
result = cleanup_preserve(result, bnodes_to_clear || [])
421+
result = cleanup_preserve(result)
421422
end
422423

423424
block_given? ? yield(result) : result

lib/json/ld/frame.rb

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,19 +211,45 @@ def count_blank_node_identifiers_internal(input, results)
211211
end
212212
end
213213

214+
##
215+
# Prune BNode identifiers recursively
216+
#
217+
# @param [Array, Hash] input
218+
# @param [Array<String>] bnodes_to_clear
219+
# @return [Array, Hash]
220+
def prune_bnodes(input, bnodes_to_clear)
221+
result = case input
222+
when Array
223+
# If, after replacement, an array contains only the value null remove the value, leaving an empty array.
224+
input.map {|o| prune_bnodes(o, bnodes_to_clear)}.compact
225+
when Hash
226+
output = Hash.new
227+
input.each do |key, value|
228+
if context.expand_iri(key) == '@id' && bnodes_to_clear.include?(value)
229+
# Don't add this to output, as it is pruned as being superfluous
230+
else
231+
output[key] = prune_bnodes(value, bnodes_to_clear)
232+
end
233+
end
234+
output
235+
else
236+
input
237+
end
238+
result
239+
end
240+
214241
##
215242
# Replace @preserve keys with the values, also replace @null with null.
216243
#
217244
# Optionally, remove BNode identifiers only used once.
218245
#
219246
# @param [Array, Hash] input
220-
# @param [Array<String>] bnodes_to_clear
221247
# @return [Array, Hash]
222-
def cleanup_preserve(input, bnodes_to_clear)
248+
def cleanup_preserve(input)
223249
result = case input
224250
when Array
225251
# If, after replacement, an array contains only the value null remove the value, leaving an empty array.
226-
v = input.map {|o| cleanup_preserve(o, bnodes_to_clear)}.compact
252+
v = input.map {|o| cleanup_preserve(o)}.compact
227253

228254
# If the array contains a single member, which is itself an array, use that value as the result
229255
(v.length == 1 && v.first.is_a?(Array)) ? v.first : v
@@ -232,11 +258,9 @@ def cleanup_preserve(input, bnodes_to_clear)
232258
input.each do |key, value|
233259
if key == '@preserve'
234260
# replace all key-value pairs where the key is @preserve with the value from the key-pair
235-
output = cleanup_preserve(value, bnodes_to_clear)
236-
elsif context.expand_iri(key) == '@id' && bnodes_to_clear.include?(value)
237-
# Don't add this to output, as it is pruned as being superfluous
261+
output = cleanup_preserve(value)
238262
else
239-
v = cleanup_preserve(value, bnodes_to_clear)
263+
v = cleanup_preserve(value)
240264

241265
# Because we may have added a null value to an array, we need to clean that up, if we possible
242266
v = v.first if v.is_a?(Array) && v.length == 1 && !context.as_array?(key)

spec/compact_spec.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,53 @@
10781078
}
10791079
})
10801080
},
1081+
"Odd framing test" => {
1082+
input: %([
1083+
{
1084+
"http://example.org/claim": [
1085+
{
1086+
"@graph": [
1087+
{
1088+
"@id": "http://example.org/1",
1089+
"https://example.com#test": [
1090+
{
1091+
"@value": "foo"
1092+
}
1093+
]
1094+
}
1095+
]
1096+
}
1097+
]
1098+
}
1099+
]
1100+
),
1101+
context: %( {
1102+
"@version": 1.1,
1103+
"@vocab": "https://example.com#",
1104+
"ex": "http://example.org/",
1105+
"claim": {
1106+
"@id": "ex:claim",
1107+
"@container": "@graph"
1108+
},
1109+
"id": "@id"
1110+
}),
1111+
output: %({
1112+
"@context": {
1113+
"@version": 1.1,
1114+
"@vocab": "https://example.com#",
1115+
"ex": "http://example.org/",
1116+
"claim": {
1117+
"@id": "ex:claim",
1118+
"@container": "@graph"
1119+
},
1120+
"id": "@id"
1121+
},
1122+
"claim": {
1123+
"id": "ex:1",
1124+
"test": "foo"
1125+
}
1126+
})
1127+
}
10811128
}.each_pair do |title, params|
10821129
it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
10831130
end

spec/frame_spec.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,59 @@
14761476
})
14771477
do_frame(input: input, frame: frame, output: expected)
14781478
end
1479+
1480+
it "framing with @container: @graph assumes pruneBnodeIdentifiers" do
1481+
input = JSON.parse %({
1482+
"@context": {
1483+
"@version": 1.1,
1484+
"@vocab": "https://example.com#",
1485+
"ex": "http://example.org/",
1486+
"claim": {
1487+
"@id": "ex:claim",
1488+
"@container": "@graph"
1489+
},
1490+
"id": "@id"
1491+
},
1492+
"claim": {
1493+
"id": "ex:1",
1494+
"test": "foo"
1495+
}
1496+
})
1497+
frame = JSON.parse %({
1498+
"@context": {
1499+
"@version": 1.1,
1500+
"@vocab": "https://example.com#",
1501+
"ex": "http://example.org/",
1502+
"claim": {
1503+
"@id": "ex:claim",
1504+
"@container": "@graph"
1505+
},
1506+
"id": "@id"
1507+
},
1508+
"claim": {}
1509+
})
1510+
expected = JSON.parse %({
1511+
"@context": {
1512+
"@version": 1.1,
1513+
"@vocab": "https://example.com#",
1514+
"ex": "http://example.org/",
1515+
"claim": {
1516+
"@id": "ex:claim",
1517+
"@container": "@graph"
1518+
},
1519+
"id": "@id"
1520+
},
1521+
"@graph": [
1522+
{
1523+
"claim": {
1524+
"id": "ex:1",
1525+
"test": "foo"
1526+
}
1527+
}
1528+
]
1529+
})
1530+
do_frame(input: input, frame: frame, output: expected)
1531+
end
14791532
end
14801533

14811534
def do_frame(params)

0 commit comments

Comments
 (0)