Façade-X mapping for JSONThis document specifies how a JSON resource is represented as a Façade-X data source.
This is the editor's draft of this document. It changes with every update to the specification sources and is not a stable release. The latest release, version 0.1, is at https://w3c-facade-x.github.io/facade-x-specs/mapping-json.html.
This document is a first draft. It follows the behaviour of the reference implementation, SPARQL Anything, and the test cases in facade-x-tests, except where the Issues section says otherwise.
This document is listed in Façade-X Mappings, one of the seven Façade-X specification documents produced by the Data Façades Community Group:
A JSON text is a single value: an
object, an array, a
string, a number,
true, false, or null [RFC8259]. Objects hold members by name, and arrays hold
elements by position.
In Façade-X, objects and arrays are containers. Object members are held by string slots and
array elements by numbered slots. Strings, numbers and booleans are values.
This document applies the common principles of the format mappings.
| Prefix | Namespace |
|---|---|
| fx | http://sparql.xyz/facade-x/ns/ |
| fxe | http://sparql.xyz/facade-x/engine/ |
| xyz | http://sparql.xyz/facade-x/data/ |
| rdf | http://www.w3.org/1999/02/22-rdf-syntax-ns# |
| xsd | http://www.w3.org/2001/XMLSchema# |
The typing of numbers follows the decision in
issue #41. It differs from the
reference implementation, which types every number as xsd:decimal (giving ill-typed literals for
numbers with an exponent) and writes -0 as "0".
When fxe:json.include-null-values is true, the reference implementation represents null with
the IRI xyz:null. Whether null should have a term in the Schema vocabulary is open.
The representation of a top-level string, number or boolean, and the treatment of duplicate member names (which RFC 8259 §4 allows, although names SHOULD be unique), are not yet specified.
An engine MUST apply this mapping to a resource whose media type is
application/json, or any media type with the
structured syntax suffix +json
[RFC6839] (e.g. application/problem+json).
When the media type is not given, an engine SHOULD apply this mapping to resources with the file
extension json.
A JSON resource yields exactly one data source. Its triples are placed in the named graph whose IRI
is the value of fxe:root.
The top-level value is represented by the root container, typed fx:Root:
An object is represented by a container. Each member is held by a string slot whose IRI is minted
from the member name, as defined in the common principles. With the
default namespace, the member "name" gives the slot xyz:name.
Characters that are not allowed in an IRI are percent-encoded as UTF-8; other characters, including
non-ASCII letters, are kept. The member "key with spaces" gives xyz:key%20with%20spaces, and the
empty member name "" gives the namespace IRI itself.
A member whose value is an object or an array holds the container that represents it. An empty object gives a container with no slots.
An array is represented by a container. Each element is held by a numbered slot, in the order of the
array: the first element in rdf:_1, the second in rdf:_2, and so on.
A null element keeps its position: no triple is produced for it, and the following element keeps
its own index. For example, in [1, null, 3] the value 3 is held by rdf:_3.
An element that is an object or an array holds the container that represents it. An empty array gives a container with no slots.
JSON defines value types. Following the common principles, the lexical form of every value is its source form, and the datatype is chosen so that the literal is well-typed [XMLSCHEMA11-2]:
| JSON value | Literal |
|---|---|
| string | xsd:string; the lexical form is the sequence of Unicode characters the string denotes, after unescaping |
| number without an exponent | xsd:decimal, with the numeral as written in the source |
number with an exponent (exp) |
xsd:double, with the numeral as written in the source |
true, false |
xsd:boolean |
null |
no triple, unless fxe:json.include-null-values is true |
Numbers are typed as follows:
frac) and exponent (exp). The natural datatype for a JSON number is xsd:decimal, whose
value space contains every JSON number exactly.
A numeral without an exponent is always in the
lexical space of xsd:decimal,
so it is typed xsd:decimal (e.g. 3.0 gives "3.0"^^xsd:decimal, and -0 gives "-0"^^xsd:decimal).xsd:decimal has no exponent notation. A numeral with an exponent is in the
lexical space of xsd:double, so it is
typed xsd:double (e.g. 1.5e3 gives "1.5e3"^^xsd:double).xsd:string. This does not happen for a
well-formed JSON text.The value of an xsd:double literal is the nearest double-precision value to the number, so it may
differ from the number written in the source (e.g. 2E-4). The lexical form keeps the number as
written.
These options are terms of the Engine vocabulary and apply only to this mapping.
| Term | Range | Default | Description |
|---|---|---|---|
fxe:json.include-null-values |
xsd:boolean |
false | Produce a triple for members and elements whose value is null. |
fxe:json.path |
xsd:string |
— | A JSONPath query [RFC9535]. The root container holds the nodes selected by the query in numbered slots, in the order they are selected. |
fxe:json.literalize |
xsd:string |
— | A member name. The values of members with this name are represented as a single literal holding their JSON text, instead of as containers. |
The reference implementation accepts several values for json.path and json.literalize as
numbered options (json.path.1, json.path.2, …). Whether this form is kept is open.
Input:
{
"name": "Alice",
"age": 30
}
Façade-X representation:
@prefix fx: <http://sparql.xyz/facade-x/ns/> .
@prefix xyz: <http://sparql.xyz/facade-x/data/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
[] a fx:Root ;
xyz:name "Alice" ;
xyz:age "30"^^xsd:decimal .
Input:
{
"object": { "nested": { "deep": "value" } },
"array": [1, "two", 3.0, true, null, {"key": "value"}, ["nested", "array"], [], {}],
"emptyObject": {},
"key with spaces": "spaces in key"
}
Façade-X representation:
@prefix fx: <http://sparql.xyz/facade-x/ns/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xyz: <http://sparql.xyz/facade-x/data/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
[] a fx:Root ;
xyz:object [ xyz:nested [ xyz:deep "value" ] ] ;
xyz:array [
rdf:_1 "1"^^xsd:decimal ;
rdf:_2 "two" ;
rdf:_3 "3.0"^^xsd:decimal ;
rdf:_4 true ;
rdf:_6 [ xyz:key "value" ] ;
rdf:_7 [ rdf:_1 "nested" ; rdf:_2 "array" ] ;
rdf:_8 [ ] ;
rdf:_9 [ ]
] ;
xyz:emptyObject [ ] ;
<http://sparql.xyz/facade-x/data/key%20with%20spaces> "spaces in key" .
The null element produces no triple, so there is no rdf:_5.
Input:
{
"integer": 42,
"negativeZero": -0,
"decimal": 3.0,
"exponent": 1.5e3,
"negativeExponent": 2E-4,
"bigInteger": 12345678901234567890
}
Façade-X representation:
@prefix fx: <http://sparql.xyz/facade-x/ns/> .
@prefix xyz: <http://sparql.xyz/facade-x/data/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
[] a fx:Root ;
xyz:integer "42"^^xsd:decimal ;
xyz:negativeZero "-0"^^xsd:decimal ;
xyz:decimal "3.0"^^xsd:decimal ;
xyz:exponent "1.5e3"^^xsd:double ;
xyz:negativeExponent "2E-4"^^xsd:double ;
xyz:bigInteger "12345678901234567890"^^xsd:decimal .
Listing each element of the array member, with its position:
PREFIX fx: <http://sparql.xyz/facade-x/ns/>
PREFIX fxe: <http://sparql.xyz/facade-x/engine/>
PREFIX xyz: <http://sparql.xyz/facade-x/data/>
SELECT ?slot ?value WHERE {
SERVICE <x-sparql-anything:> {
fx:properties fxe:location "features.json" .
?root a fx:Root ;
xyz:array ?array .
?array ?slot ?value .
}
}