Json LD
Here is a simple example taken from json-ld.org
{
"@context": "https://json-ld.org/contexts/person.jsonld",
"@id": "http://dbpedia.org/resource/John_Lennon",
"name": "John Lennon",
"born": "1940-10-09",
"spouse": "http://dbpedia.org/resource/Cynthia_Lennon"
}
It’s similar on how a Person
would be represented in JSON, with additional known properties such as @context
and
@id
.
The @id
is used to uniquely identify an object.
The @context
is used to define how terms should be interpreted and help
expressing specific identifier with short-hand names instead
of IRI.
Exhausting reserved keywords list and their meaning is available here
In the above example the @context
is a remote one, but the @context
can also be defined inline. Here is the same
JSON-LD object using locally defined terms.
{
"@context": {
"xsd": "http://www.w3.org/2001/XMLSchema#",
"name": "http://xmlns.com/foaf/0.1/name",
"born": {
"@id": "http://schema.org/birthDate",
"@type": "xsd:date"
},
"spouse": {
"@id": "http://schema.org/spouse",
"@type": "@id"
}
},
"@id": "http://dbpedia.org/resource/John_Lennon",
"name": "John Lennon",
"born": "1940-10-09",
"spouse": "http://dbpedia.org/resource/Cynthia_Lennon"
}
which defines inline the name
, born
and spouse
terms.
The two objects have the same meaning as Linked Data.
A JSON-LD document can be described in multiple forms and by applying certain transformations a document can change shape without changing the meaning.
Relevant forms in the realm of EDC are:
- Expanded document form
- Compacted document form
The examples above are in compacted
form and by applying
the expansion algorithm the output would look like this
[
{
"@id": "http://dbpedia.org/resource/John_Lennon",
"http://schema.org/birthDate": [
{
"@type": "http://www.w3.org/2001/XMLSchema#date",
"@value": "1940-10-09"
}
],
"http://xmlns.com/foaf/0.1/name": [
{
"@value": "John Lennon"
}
],
"http://schema.org/spouse": [
{
"@id": "http://dbpedia.org/resource/Cynthia_Lennon"
}
]
}
]
The expansion is the process of taking in input a JSON-LD
document and applying the @context
so that it is no longer necessary, as all the terms are resolved in their IRI
representation.
The compaction is the inverse process. It takes in input a
JSON-LD in expanded form and by applying the supplied @context
, it creates the compacted form.
For playing around JSON-LD and processing algorithm the playground is a useful tool.
1. JSON-LD in EDC
EDC uses JSON-LD as primary serialization format at API layer and at runtime EDC manages the objects in their expanded
form, for example when transforming JsonObject
into EDC entities and and backwards
in transformers or when validating input
JsonObject
at API level.
Extensible properties in entities are always stored expanded form.
To achieve that, EDC uses an interceptor (JerseyJsonLdInterceptor
) that always expands in ingress and compacts in
egress the JsonObject
.
EDC uses JSON-LD for two main reasons:
Fist EDC embraces different protocols and standards such as:
and they all rely on JSON-LD as serialization format.
The second reason is that EDC allows to extends entities like Asset
with custom properties, and uses JSON-LD as the
way to extend objects with custom namespaces.
EDC handles JSON-LD through the JsonLd
SPI. It supports different operation and configuration for managing JSON-LD in
the EDC runtime.
It supports expansion and compaction process:
Result<JsonObject> expand(JsonObject json);
Result<JsonObject> compact(JsonObject json, String scope);
and allows the configuration of which @context
and namespaces
to use when processing the JSON-LD in a specific
scope.
For example when using the JsonLd
service in the management API the @context
and namespaces
configured might
differs when using the same service in the dsp
layer.
The JsonLd
service also can configure cached contexts by allowing to have a local copy of the remote context. This
limits the network request required when processing the JSON-LD and reduces the attack surface if the remote host of the
context is compromised.
By default EDC make usage of
@vocab
for processing input/output JSON-LD document. This can provide a default vocabulary for extensible properties. An on-going initiative is available with
this extension
in order to provide a cached terms mapping (context) for EDC management API. The remote context definition is available here.
Implementors that need additional @context
and namespaces
to be supported in EDC runtime, should develop a custom
extension that registers the required @context
and namespace
.
For example let’s say we want to support a custom namespace http://w3id.org/starwars/v0.0.1/ns/
in the extensible
properties of an Asset.
The input JSON would look like this:
{
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"sw": "http://w3id.org/starwars/v0.0.1/ns/"
},
"@type": "Asset",
"@id": "79d9c360-476b-47e8-8925-0ffbeba5aec2",
"properties": {
"sw:faction": "Galactic Imperium",
"sw:person": {
"sw:name": "Darth Vader",
"sw:webpage": "https://death.star"
}
},
"dataAddress": {
"@type": "DataAddress",
"type": "myType"
}
}
Even if we don’t register a any additional @context
or namespace
prefix in the EDC runtime,
the Asset will still be persisted correctly since the JSON-LD gets expanded correctly and
stored in the expanded form.
But in the egress
the JSON-LD document gets always compacted, and without additional configuration, it will look like
this:
{
"@id": "79d9c360-476b-47e8-8925-0ffbeba5aec2",
"@type": "Asset",
"properties": {
"http://w3id.org/starwars/v0.0.1/ns/faction": "Galactic Imperium",
"http://w3id.org/starwars/v0.0.1/ns/person": {
"http://w3id.org/starwars/v0.0.1/ns/name": "Darth Vader",
"http://w3id.org/starwars/v0.0.1/ns/webpage": "https://death.star"
},
"id": "79d9c360-476b-47e8-8925-0ffbeba5aec2"
},
"dataAddress": {
"@type": "DataAddress",
"type": "myType"
},
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"odrl": "http://www.w3.org/ns/odrl/2/"
}
}
That means that the IRIs are not shortened to terms or compact iri. This might be ok for some runtime and configuration. But if implementors want to achieve more usability and easy of usage, two main strategy can be applied:
1.1 Compact IRI
The first strategy is to register a namespace prefix in an extension:
public class MyExtension implements ServiceExtension {
@Inject
private JsonLd jsonLd;
@Override
public void initialize(ServiceExtensionContext context) {
jsonLd.registerNamespace("sw", "http://w3id.org/starwars/v0.0.1/ns/", "MANAGEMENT_API");
}
}
This will shorten the IRI to compact IRI when compacting the same JSON-LD:
{
"@id": "79d9c360-476b-47e8-8925-0ffbeba5aec2",
"@type": "Asset",
"properties": {
"sw:faction": "Galactic Imperium",
"sw:person": {
"sw:name": "Darth Vader",
"sw:webpage": "https://death.star"
},
"id": "79d9c360-476b-47e8-8925-0ffbeba5aec2"
},
"dataAddress": {
"@type": "DataAddress",
"type": "myType"
},
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"odrl": "http://www.w3.org/ns/odrl/2/",
"sw": "http://w3id.org/starwars/v0.0.1/ns/"
}
}
1.2 Custom Remote Context
An improved version requires developers to draft a context (which should be resolvable with an URL), for example
http://w3id.org/starwars/context.jsonld
, that contains the terms definition.
An example of a definition might look like this:
{
"@context": {
"@version": 1.1,
"sw": "http://w3id.org/starwars/v0.0.1/ns/",
"person": "sw:person",
"faction": "sw:faction",
"name": "sw:name",
"webpage": "sw:name"
}
}
Then in a an extension the context URL should be registered in the desired scope and cached:
public class MyExtension implements ServiceExtension {
@Inject
private JsonLd jsonLd;
@Override
public void initialize(ServiceExtensionContext context) {
jsonld.registerContext("http://w3id.org/starwars/context.jsonld", "MANAGEMENT_API");
URI documentLocation = // load from filesystem or classpath
jsonLdService.registerCachedDocument("http://w3id.org/starwars/context.jsonld", documentLocation)
}
}
With this configuration the JSON-LD will be representend without the sw
prefix, since the terms mapping is defined in
the remote context http://w3id.org/starwars/context.jsonld
:
{
"@id": "79d9c360-476b-47e8-8925-0ffbeba5aec2",
"@type": "Asset",
"properties": {
"faction": "Galactic Imperium",
"person": {
"name": "Darth Vader",
"webpage": "https://death.star"
},
"id": "79d9c360-476b-47e8-8925-0ffbeba5aec2"
},
"dataAddress": {
"@type": "DataAddress",
"type": "myType"
},
"@context": [
"http://w3id.org/starwars/context.jsonld",
{
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"odrl": "http://www.w3.org/ns/odrl/2/"
}
]
}
In case of name clash in the terms definition, the JSON-LD processor should fallback to the compact URI representation.
1.1 JSON-LD Validation
EDC provides a mechanism to validate JSON-LD objects. The validation phase is typically handled at the
network/controller layer. For each entity identified by it’s own @type
, it is possible to register a custom
Validator<JsonObject>
using the registry JsonObjectValidatorRegistry
. By default EDC provides validation for all the
entities it manages like Asset
, ContractDefinition
..etc.
For custom validator it is possible to either implements Validator<JsonObject>
interface (not recommended) or
or use the bundled JsonObjectValidator
, which is a declarative way of configuring a validator for an object through
the builder pattern. It also comes with a preset of validation rules such as id not empty, mandatory properties and many
more.
An example of validator for a custom type Foo
:
{
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"edc": "https://w3id.org/edc/v0.0.1/ns/"
},
"@id": "79d9c360-476b-47e8-8925-0ffbeba5aec2",
"@type": "Foo",
"bar": "value"
}
might look like this:
public class FooValidator {
public static JsonObjectValidator instance() {
return JsonObjectValidator.newValidator()
.verifyId(OptionalIdNotBlank::new)
.verify("https://w3id.org/edc/v0.0.1/ns/bar")
.build();
}
}
and can be registered with the @Injectable
JsonObjectValidatorRegistry
:
public class MyExtension implements ServiceExtension {
@Inject
private JsonObjectValidatorRegistry validator;
@Override
public void initialize(ServiceExtensionContext context) {
validator.register("https://w3id.org/edc/v0.0.1/ns/Foo", FooValidator.instance());
}
}
When needed, it can be invoked like this:
public class MyController {
private JsonObjectValidatorRegistry validator;
@Override
public void doSomething(JsonObject input) {
validator.validate("https://w3id.org/edc/v0.0.1/ns/Foo", input)
.orElseThrow(ValidationFailureException::new);
}
}
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.