Façade-X mapping for JSON

Representing JSON in Façade-X.

Draft Community Group Report

Latest published version:
https://w3c-facade-x.github.io/facade-x-specs/mapping-json.html
Latest editor's draft:
https://w3c-facade-x.github.io/facade-x-specs/dev/mapping-json.html
Editors:
(Università Telematica San Raffaele - Roma)
(The Open University)
Feedback:
GitHub w3c-facade-x/facade-x-specs (pull requests, new issue, open issues)

Abstract

This document specifies how a JSON resource is represented as a Façade-X data source.

Status of This Document

This specification was published by the Data Façades Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

This is version 0.1 of this document, released on 2026-10-06, at https://w3c-facade-x.github.io/facade-x-specs/0.1/mapping-json.html. This version will not change. The latest release is always at https://w3c-facade-x.github.io/facade-x-specs/mapping-json.html. The editor's draft, which may include changes made after this release, is at https://w3c-facade-x.github.io/facade-x-specs/dev/mapping-json.html. The issues addressed in this release, and those still open, are listed in the release notes.

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.

GitHub Issues are preferred for discussion of this specification.

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

1. Introduction

This section is non-normative.

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.

1.1 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#

2. Issues

Issue 41: Surface form of literals in format mappings (JSON numbers as a first case) Mapping: JSONFX Mappings

The starting point is the idea that Facade-X should not modify the lexical form of data values. Previously, we mapped JSON numerals to xsd:decimal (see also SPARQL-Anything/sparql.anything#620)

However, JSON numerals and xsd:decimal lexical forms are not the same set: JSON allows exponents (1.5e3) and xsd:decimal does not. Keeping the source text, therefore, produces ill-typed literals, so the mappings need a shared rule for when to keep a value's surface form and when to rewrite it as the canonical form. Cases of mismatches between format specifications and XSD datatypes may arise. We should discuss this issue more generally and then make a decision that can guide future mappings.

We review first what SPARQL Anything does, then discuss potential solutions.

SPARQL Anything behaviour

SPARQL Anything types every JSON number as xsd:decimal and copies the numeral from the source as the lexical form. From tests/json/2-features:

JSON SPARQL Anything output
42 "42"^^xsd:decimal
3.0 "3.0"^^xsd:decimal
-0 "0"^^xsd:decimal
1.5e3 "1.5e3"^^xsd:decimal
2E-4 "2E-4"^^xsd:decimal
12345678901234567890 "12345678901234567890"^^xsd:decimal

The last two exponent rows are ill-typed literals. The lexical space of xsd:decimal is (\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+), with no exponent notation (XSD 1.1 Part 2, §3.3.3.1). A SPARQL engine cannot compute a value for them, so numeric comparisons and arithmetic on them fail. The behaviour is also inconsistent with itself: 3.0 keeps its surface form, but -0 is normalised to 0.

The underlying inconsistency

JSON and XSD agree on the values but not on the numerals:

Decision in the JSON mapping draft

Every literal produced is well-typed and exactly equal in value to the JSON number. The surface form is kept wherever XSD allows it.

What changes compared with SPARQL Anything

JSON SPARQL Anything Draft
1.5e3 "1.5e3"^^xsd:decimal (ill-typed) "1500"^^xsd:decimal
2E-4 "2E-4"^^xsd:decimal (ill-typed) "0.0002"^^xsd:decimal
-0 "0"^^xsd:decimal "-0"^^xsd:decimal

All other numbers are unchanged. The expected output of tests/json/2-features needs updating to match, and SPARQL Anything needs a fix.

The general question: surface form versus value

Façade-X aims to represent the source as it is, which argues for keeping the surface form of values. In RDF, though, a typed literal ties the surface form to a datatype's lexical space, and source grammars rarely match XSD lexical spaces exactly. Surface form also matters beyond values: "3.0"^^xsd:decimal and "3"^^xsd:decimal are equal values but different RDF terms, so sameTerm, DISTINCT and joins on literals depend on the lexical form.

This will come up in every mapping whose source format has typed values: YAML (numbers, booleans, dates), spreadsheets (numbers stored as doubles, dates as serial numbers), and possibly XML if schema-aware typing is considered. CSV and plain XML avoid it only because all their values are strings.

Options for a principle shared by all mappings:

  1. Keep the surface form when it is valid for the target datatype; otherwise use the canonical form. This is the current draft. It is well-typed, but some surface forms are lost, and a reader cannot tell which values were rewritten.
  2. Always use the canonical form. Deterministic, but loses the surface form everywhere (3.0 → 3).
  3. Always keep the surface form, with a datatype whose lexical space accepts it. Use the XSD datatype when the source form is in its lexical space; otherwise use a datatype that accepts the source grammar.
  4. Keep the surface form, even if ill-typed. This is the current SPARQL Anything behaviour, and not something a specification should require.

Proposal: option 3

The source text of every value is its lexical form, and no value is rewritten. The datatype is chosen so that the literal is well-typed. In the case of JSON numerals, we first map to xsd:decimal (design choice) and then verify conformance with the surface form:

If agreed, this becomes a common principle in the format mappings page: "The lexical form of a value is its source form. The datatype is the natural XML Schema datatype for the value when the source form is in its lexical space; otherwise it is a datatype whose lexical space accepts the source form, as defined by the mapping." The JSON draft and tests/json/2-features would then be updated accordingly. Opinions welcome.

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.

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

4. Mapping

4.1 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:

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

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

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

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

6. Examples

This section is non-normative.

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

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

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

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

7. References

This section is non-normative.

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