Façade-X mapping for JSON

Representing JSON in Façade-X.

This 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.

Set of Documents

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:

  1. Façade-X Overview
  2. Façade-X Primer
  3. Façade-X Schema vocabulary
  4. Façade-X Engine vocabulary
  5. Façade-X Data Access in SPARQL
  6. Façade-X Concepts and Metamodel Specification
  7. Façade-X Mappings

Introduction

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.

Namespaces

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#

Issues

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.

Selection

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.

Mapping

Data source

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:

Objects

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.

Arrays

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.

Values

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:

  1. The JSON number grammar is a decimal numeral with an optional fraction (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).
  2. The lexical space of 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).
  3. A numeral that is in neither lexical space is typed 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.

Options

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.

Examples

Object

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 .

    

Nested objects and arrays

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.

Numbers

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 .

    

Query

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 .
  }
}

    

References

[RFC8259]
The JavaScript Object Notation (JSON) Data Interchange Format. T. Bray. IETF. December 2017. Internet Standard.
[RFC6839]
Additional Media Type Structured Syntax Suffixes. T. Hansen; A. Melnikov. IETF. January 2013. Informational.
[RFC9535]
JSONPath: Query Expressions for JSON. S. Gössner; G. Normington; C. Bormann. IETF. February 2024. Proposed Standard.
[XMLSCHEMA11-2]
W3C XML Schema Definition Language (XSD) 1.1 Part 2: Datatypes. David Peterson; Shudi Gao; Ashok Malhotra; C. M. Sperberg-McQueen; Henry S. Thompson. W3C. 5 April 2012. W3C Recommendation.
[sparql-anything-json]
SPARQL Anything — JSON.