Skip to content

Commit b5b019d

Browse files
committed
Use internalRepresentation option in fromRdf to preserve raw RDF literals as values.
1 parent 91e20d3 commit b5b019d

File tree

3 files changed

+89
-16
lines changed

3 files changed

+89
-16
lines changed

lib/json/ld/api.rb

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class API
6666
# @param [String, #read, Hash, Array, JSON::LD::Context] context
6767
# An external context to use additionally to the context embedded in input when expanding the input.
6868
# @param [Hash{Symbol => Object}] options
69+
# @option options [Symbol] :adapter used with MultiJson
6970
# @option options [RDF::URI, String, #to_s] :base
7071
# The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context. If not specified, and a base IRI is found from `input`, options[:base] will be modified with this value.
7172
# @option options [Boolean] :compactArrays (true)
@@ -74,10 +75,10 @@ class API
7475
# Creates document relative IRIs when compacting, if `true`, otherwise leaves expanded.
7576
# @option options [Proc] :documentLoader
7677
# The callback of the loader to be used to retrieve remote documents and contexts. If specified, it must be used to retrieve remote documents and contexts; otherwise, if not specified, the processor's built-in loader must be used. See {documentLoader} for the method signature.
77-
# @option options [Boolean] :lowercaseLanguage
78-
# By default, language tags are left as is. To normalize to lowercase, set this option to `true`.
7978
# @option options [String, #read, Hash, Array, JSON::LD::Context] :expandContext
8079
# A context that is used to initialize the active context when expanding a document.
80+
# @option options [Boolean] :extendedRepresentation (false)
81+
# Use the extended internal representation.
8182
# @option options [Boolean] :extractAllScripts
8283
# If set, when given an HTML input without a fragment identifier, extracts all `script` elements with type `application/ld+json` into an array during expansion.
8384
# @option options [Boolean, String, RDF::URI] :flatten
@@ -86,19 +87,19 @@ class API
8687
# When set, this has the effect of inserting a context definition with `@language` set to the associated value, creating a default language for interpreting string values.
8788
# @option options [Symbol] :library
8889
# One of :nokogiri or :rexml. If nil/unspecified uses :nokogiri if available, :rexml otherwise.
90+
# @option options [Boolean] :lowercaseLanguage
91+
# By default, language tags are left as is. To normalize to lowercase, set this option to `true`.
92+
# @option options [Boolean] :ordered (true)
93+
# Order traversal of dictionary members by key when performing algorithms.
8994
# @option options [String] :processingMode
9095
# Processing mode, json-ld-1.0 or json-ld-1.1.
91-
# 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`.
92-
# @option options [Boolean] rdfstar (false)
96+
# @option options [Boolean] :rdfstar (false)
9397
# support parsing JSON-LD-star statement resources.
9498
# @option options [Boolean] :rename_bnodes (true)
9599
# Rename bnodes as part of expansion, or keep them the same.
96100
# @option options [Boolean] :unique_bnodes (false)
97101
# Use unique bnode identifiers, defaults to using the identifier which the node was originally initialized with (if any).
98-
# @option options [Symbol] :adapter used with MultiJson
99102
# @option options [Boolean] :validate Validate input, if a string or readable object.
100-
# @option options [Boolean] :ordered (true)
101-
# Order traversal of dictionary members by key when performing algorithms.
102103
# @yield [api]
103104
# @yieldparam [API]
104105
# @raise [JsonLdError]
@@ -562,6 +563,7 @@ def self.fromRdf(input, useRdfType: false, useNativeTypes: false, serializer: ni
562563

563564
API.new(nil, nil, **options) do
564565
result = from_statements(input,
566+
extendedRepresentation: options[:extendedRepresentation],
565567
useRdfType: useRdfType,
566568
useNativeTypes: useNativeTypes)
567569
end

lib/json/ld/from_rdf.rb

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ module FromRDF
1414
# @param [Boolean] useRdfType (false)
1515
# If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
1616
# @param [Boolean] useNativeTypes (false) use native representations
17+
# @param extendedRepresentation (false)
18+
# Use the extended internal representation for native types.
1719
#
1820
# @return [Array<Hash>] the JSON-LD document in normalized form
19-
def from_statements(dataset, useRdfType: false, useNativeTypes: false)
21+
def from_statements(dataset, useRdfType: false, useNativeTypes: false, extendedRepresentation: false)
2022
default_graph = {}
2123
graph_map = {'@default' => default_graph}
2224
referenced_once = {}
@@ -41,9 +43,9 @@ def from_statements(dataset, useRdfType: false, useNativeTypes: false)
4143
default_graph[name] ||= {'@id' => name} unless name == '@default'
4244

4345
subject = statement.subject.statement? ?
44-
resource_representation(statement.subject, useNativeTypes)['@id'].to_json_c14n :
46+
resource_representation(statement.subject, useNativeTypes, extendedRepresentation)['@id'].to_json_c14n :
4547
statement.subject.to_s
46-
node = node_map[subject] ||= resource_representation(statement.subject, useNativeTypes)
48+
node = node_map[subject] ||= resource_representation(statement.subject, useNativeTypes, extendedRepresentation)
4749

4850
# If predicate is rdf:datatype, note subject in compound literal subjects map
4951
if @options[:rdfDirection] == 'compound-literal' && statement.predicate == RDF.to_uri + 'direction'
@@ -53,10 +55,10 @@ def from_statements(dataset, useRdfType: false, useNativeTypes: false)
5355
# If object is an IRI, blank node identifier, or statement, and node map does not have an object member, create one and initialize its value to a new JSON object consisting of a single member @id whose value is set to object.
5456
unless statement.object.literal?
5557
object = statement.object.statement? ?
56-
resource_representation(statement.object, useNativeTypes)['@id'].to_json_c14n :
58+
resource_representation(statement.object, useNativeTypes, extendedRepresentation)['@id'].to_json_c14n :
5759
statement.object.to_s
5860
node_map[object] ||=
59-
resource_representation(statement.object, useNativeTypes)
61+
resource_representation(statement.object, useNativeTypes, extendedRepresentation)
6062
end
6163

6264
# If predicate equals rdf:type, and object is an IRI or blank node identifier, append object to the value of the @type member of node. If no such member exists, create one and initialize it to an array whose only item is object. Finally, continue to the next RDF triple.
@@ -66,7 +68,7 @@ def from_statements(dataset, useRdfType: false, useNativeTypes: false)
6668
end
6769

6870
# Set value to the result of using the RDF to Object Conversion algorithm, passing object, rdfDirection, and use native types.
69-
value = resource_representation(statement.object, useNativeTypes)
71+
value = resource_representation(statement.object, useNativeTypes, extendedRepresentation)
7072

7173
merge_value(node, statement.predicate.to_s, value)
7274

@@ -171,18 +173,18 @@ def from_statements(dataset, useRdfType: false, useNativeTypes: false)
171173

172174
RDF_LITERAL_NATIVE_TYPES = Set.new([RDF::XSD.boolean, RDF::XSD.integer, RDF::XSD.double]).freeze
173175

174-
def resource_representation(resource, useNativeTypes)
176+
def resource_representation(resource, useNativeTypes, extendedRepresentation)
175177
case resource
176178
when RDF::Statement
177179
# Note, if either subject or object are a BNode which is used elsewhere,
178180
# this might not work will with the BNode accounting from above.
179-
rep = {'@id' => resource_representation(resource.subject, false)}
181+
rep = {'@id' => resource_representation(resource.subject, false, extendedRepresentation)}
180182
if resource.predicate == RDF.type
181183
rep['@id'].merge!('@type' => resource.object.to_s)
182184
else
183185
rep['@id'].merge!(
184186
resource.predicate.to_s =>
185-
as_array(resource_representation(resource.object, useNativeTypes)))
187+
as_array(resource_representation(resource.object, useNativeTypes, extendedRepresentation)))
186188
end
187189
rep
188190
when RDF::Literal
@@ -197,6 +199,8 @@ def resource_representation(resource, useNativeTypes)
197199
rescue ::JSON::ParserError => e
198200
raise JSON::LD::JsonLdError::InvalidJsonLiteral, e.message
199201
end
202+
elsif useNativeTypes && extendedRepresentation
203+
res['@value'] = resource # Raw literal
200204
elsif resource.datatype.start_with?("https://www.w3.org/ns/i18n#") && rdfDirection == 'i18n-datatype' && @context.processingMode('json-ld-1.1')
201205
lang, dir = resource.datatype.fragment.split('_')
202206
res['@value'] = resource.to_s

spec/from_rdf_spec.rb

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,73 @@
268268
it(title) {do_fromRdf(processingMode: "json-ld-1.1", **params)}
269269
end
270270
end
271+
272+
context "extendedRepresentation: true" do
273+
{
274+
"true": {
275+
output: [{
276+
"@id" => "http://example.org/vocab#id",
277+
"http://example.org/vocab#bool" => [{"@value" => RDF::Literal(true)}]
278+
}],
279+
input:%(
280+
@prefix ex: <http://example.org/vocab#> .
281+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
282+
ex:id ex:bool true .
283+
)
284+
},
285+
"false": {
286+
output: [{
287+
"@id" => "http://example.org/vocab#id",
288+
"http://example.org/vocab#bool" => [{"@value" => RDF::Literal(false)}]
289+
}],
290+
input: %(
291+
@prefix ex: <http://example.org/vocab#> .
292+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
293+
ex:id ex:bool false .
294+
)
295+
},
296+
"double": {
297+
output: [{
298+
"@id" => "http://example.org/vocab#id",
299+
"http://example.org/vocab#double" => [{"@value" => RDF::Literal(1.23E0)}]
300+
}],
301+
input: %(
302+
@prefix ex: <http://example.org/vocab#> .
303+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
304+
ex:id ex:double 1.23E0 .
305+
)
306+
},
307+
"double-zero": {
308+
output: [{
309+
"@id" => "http://example.org/vocab#id",
310+
"http://example.org/vocab#double" => [{"@value" => RDF::Literal(0, datatype: RDF::XSD.double)}]
311+
}],
312+
input: %(
313+
@prefix ex: <http://example.org/vocab#> .
314+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
315+
ex:id ex:double 0.0E0 .
316+
)
317+
},
318+
"integer": {
319+
output: [{
320+
"@id" => "http://example.org/vocab#id",
321+
"http://example.org/vocab#integer" => [{"@value" => RDF::Literal(123)}]
322+
}],
323+
input: %(
324+
@prefix ex: <http://example.org/vocab#> .
325+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
326+
ex:id ex:integer 123 .
327+
)
328+
},
329+
}.each do |title, params|
330+
params[:input] = RDF::Graph.new << RDF::Turtle::Reader.new(params[:input])
331+
it(title) {
332+
do_fromRdf(processingMode: "json-ld-1.1",
333+
useNativeTypes: true,
334+
extendedRepresentation: true,
335+
**params)}
336+
end
337+
end
271338
end
272339

273340
context "anons" do

0 commit comments

Comments
 (0)