Which are the differences of NGSI-LD and JSON-LD? - fiware

In Fiware tutorials I read that NGSI-LD is not 100% JSON-LD. Can you explain me which are the differences?

JSON-LD is an extension to JavaScript Object notation which enables linked data concepts to be expressed in JSON and provide a format which is both human and computer readable. This is JSON-LD:
JSON-LD
{
"#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"
}
NGSI-LD is an API which uses multiple payload formats, but here is the John Lennon entity expressed using "normalized" NGSI-LD - one of several formats that all NGSI context-brokers must support (they also cover plain key-values JSON and GeoJSON for example - see below).
"normalized" NGSI-LD
GET ../ngsi-ld/v1/entities/John_Lennon Content-Type: application/ld+json
{
"#context": [
"https://json-ld.org/contexts/person.jsonld",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld"
],
"id": "http://dbpedia.org/resource/John_Lennon",
"type": "Person",
"name": {"type": "Property", "value": "John Lennon"},
"born": {"type": "Property", "value": "1940-10-09"},
"spouse": {
"type": "Relationship", "object":
"http://dbpedia.org/resource/Cynthia_Lennon"
}
}
Every NSGI-LD payload is a valid JSON-LD document. However this "normalized" NGSI-LD format has additional rules and restrictions (e.g. all attributes must have a type of Property or Relationship) therefore not all JSON-LD snippets are valid "normalized" NGSI-LD.
Much like JSON-LD being JSON plus linked data concepts, NGSI-LD is NGSI-v2 plus linked data concepts.
The NGSI-LD specification describes how to make requests to a context broker and the expected format of the responses. There are 27 well-defined operations within NGSI. Additionally NGSI-LD defines strict definitions of terms such as Subscription and Registration and Property and Relationship, how timestamps should only be held in an observedAt, geographical position must be found in a location attribute as a GeoJSON Point and so on.
JSON-LD is a data representation only, it makes no assumptions about how the payload is to be accessed or how the payload is to be structured beyond having attributes in JSON format and an #context attribute.
Context-Broker to Context-Broker operations in "normalized" NGSI-LD can make the assumption that the core context https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld is assumed to be part of the request and is processed last and therefore always overrides other #context elements. the #context element can also be represented using a Link Header
JSON-LD representations must always contain an #context attribute (after all that is what makes them application/ld+json rather than just plain application/json) and the #context is processed according the order of the array of elements found.
Since NGSI-LD is defined as an API, responses to NGSI-LD requests may also be returned in JSON application/json and GeoJSON application/geo+json format or as "normalized" or as key-value pairs with or without the #context and Properties annotations. For example, the key-values format will return the John_Lennon entity as follows:
"key-values" NGSI-LD
GET ../ngsi-ld/v1/entities/John_Lennon?options=keyValues Content-Type: application/ld+json
{
"#context": "https://json-ld.org/contexts/person.jsonld",
"name": "John Lennon",
"born": "1940-10-09",
"spouse": "http://dbpedia.org/resource/Cynthia_Lennon"
}
which is the same as the original JSON-LD example. And it's also possible to return as plain JSON or GeoJSON by amending the content-type.
GET ../ngsi-ld/v1/entities/John_Lennon?options=keyValues Content-Type: application/json
{
"name": "John Lennon",
"born": "1940-10-09",
"spouse": "http://dbpedia.org/resource/Cynthia_Lennon"
}
GET ../ngsi-ld/v1/entities/John_Lennon?options=keyValues Content-Type: application/geo+json
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-73.975, 40.775556]
},
"properties": {
"name": "John Lennon",
"born": "1940-10-09",
"spouse": "http://dbpedia.org/resource/Cynthia_Lennon"
}
}
"normalized" is typically used for broker-to-broker data exchange. "keyValues" is more likely to be used by an application developer. Indeed when you get the data back as just JSON all the attributes have been washed using a expansion/compaction operation which means that you can get data from multiple sources using different attribute names to return using a single common short name for agreed IRIs - this is the goal of an interoperability exchange API for data.
So in summary the "normalized" NGSI-LD format is an extended subset of JSON-LD. The NGSI-LD API itself is a more flexible API which outputs various JSON and JSON-LD formats and is used for data exchange.
If you need more information, there is also a more extensive review of NGSI-LD data formats (including a new "concise" data format) in a recent Video Presentation from the FIWARE Foundation.

Related

FIWARE, NGSI-LD - Understand the #context

I am creating a data model for a particular application and I did not start from any base model; since I did not start from any base model, the context below is sufficient, correct?
"#context": [
"https://schema.lab.fiware.org/ld/context",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.3.jsonld"
]
My data model is not complicated, with just these properties and entity being more "complex":
"address": {
"type": "Property",
"value": {
"streetAddress": "",
"postalCode": "",
"addressLocality": "",
"addressCountry": ""
}
},
"location": {
"type": "Point",
"coordinates": [
,
]
},
{
"id": "urn:ngsi-ld:MeasurementSensor:",
"type": "MeasurementSensor",
"measurementVariable": {
"type": "Property",
"value": "Temperature"
},
"measurementValue": {
"type": "Property",
"value": 32.0,
"unitCode": "ºC",
"observedAt": "2022-05-10T11:09:00.000Z"
},
"refX": {
"type": "Relationship",
"object": "urn:ngsi-ld:"
},
"#context": [
"https://schema.lab.fiware.org/ld/context",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.3.jsonld"
]
}
If you are using your own custom vocabulary you should declare your types and properties in your own LD #context. For instance,
{
"#context": [
{
"MeasurementSensor": "https://example.org/my-types/MesaurementSensor"
},
"https://schema.lab.fiware.org/ld/context",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.3.jsonld"
]
}
it also seems you are not using URNs properly, you should check. unitCode seems to be broken as well, as it must follow the UN/CEFACT unit codes.
Nonetheless, I would not recommend to define your own vocabulary for sensors, given there are existing Vocabularies such as SAREF or W3C SOSA that can and should be reused.
I'm not a data model expert but I do know a thing or two about NGSI-LD and NGSI-LD brokers.
The #context you use is an array of "https://schema.lab.fiware.org/ld/context" and v1.3 of the core context.
"https://schema.lab.fiware.org/ld/context" in its turn is an array of "https://fiware.github.io/data-models/context.jsonld" and v1.1 of the core context ...
And, ""https://fiware.github.io/data-models/context.jsonld" doesn't define any of the three terms you are using, so, no need to give any context for that. The terms will be expanded using the default URL of the core context (the value of the #vocab member of the core context defines the default URL).
An NGSI-LD broker has the core context built-in, you don't need to pass it, so do yourself a favor, and get faster responses by not passing the core context to the broker. No need.
And, if you need a user context, pass it in the HTTP Header "Link" instead.
Host it somewhere (an NGSi-LD broker offers that service), so you don't force the poor broker to parse the #conterxt in each and every request.
Lastly, do follow Jose Manuels recommendations and use standard names for your attributes (and value for unitCode).

Is returning only IDs for a JSON API collection allowed?

So let's say I have a resources called articles. These have a numeric id and you can access them under something such as:
GET /articles/1 for a specific article.
And let's say that returns something like:
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API paints my bikeshed!",
"body": "A bunch of text here"
}
}
}
Now my question is how to handle a request to GET /articles. I.e. how to deal with the request to the collection.
You see, accessing the body of the article is slow and painful. The last thing I want this REST API to do is actually try to get all that information. Yet as far as I can tell the JSON API schema seems to assume that you can always return full resources.
Is there any "allowed" way to return just the IDs (or partial attributes, like "title") under JSON API while actively not providing the ability to get the full resource?
Something like:
GET /articles returning:
{
"data": [
{
"type": "article_snubs",
"id": 1,
"attributes": {
"title": "JSON:API paints my bikeshed!"
}
}, {
"type": "article_snubs",
"id": 2,
"attributes": {
"title": "Some second thing here"
}
}
]
}
Maybe with links to the full articles?
Basically, is this at all possible while following JSON API or a REST standard? Because there is absolutely no way that GET /articles is ever going to be returning full resources due to the associate cost of getting the data, which I do not think is a rare situation to be in.
As far as I understand the JSON API specification there is no requirement that an API must return all fields (attributes and relationships) of a resource by default. The only MUST statement regarding fields inclusion that I'm aware of is related to Sparse Fieldsets (fields query param):
Sparse Fieldsets
[...]
If a client requests a restricted set of fields for a given resource type, an endpoint MUST NOT include additional fields in resource objects of that type in its response.
https://jsonapi.org/format/#fetching-sparse-fieldsets
Even so this is not forbidden by spec I would not recommend that approach. Returning only a subset of fields makes consuming your API much harder as you have to consult the documentation in order to get a list of all supported fields. It's much more within the meaning of the spec to let the client decide which information (and related resources) should be included.
The "attributes" object of a JSON-API doc does not need to be a complete representation:
attributes: an attributes object representing some of the resource’s data.
You can provide a "self" link to get the full representation, or perhaps even a "body" link to get just the body:
links: a links object containing links related to the resource.
E.g.
{
"data": [
{
"type": "articles_snubs",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"links": {
"self": "/articles/1",
"body": "/articles/1/body"
}
},
{
"type": "article_snubs",
"id": "2",
"attributes": {
"title": "Some second thing here"
},
"links": {
"self": "/articles/2",
"body": "/articles/2/body"
}
}
]
}

Dynamic list of valid values in JSON Schema

Given the following JSON, how can I use JSON Schema to validate that each string in nodes[].targets matches the name of a listed node?
{
"nodes": [
{"name": "app_server"},
{"name": "web_server1"}
{"name": "web_server2"}
{
"name": "load_balancer",
"targets": ["web_server1", "web_server2"]
}
]
}
JSON schema does not support such validation criteria.
You either need to define custom keywords if the validator supports them (e.g. in Ajv) or validate outside of schema.
Disclaimer: I have created Ajv.

Fetching XML attributes through JSON

I am using ClusterPoint database and I have imported XML document which contains tags and attributes:
<db>
<document>
<id>1</id>
<name lang="en">John</name>
<name lang="ru">Джон</name>
</document>
<document>
<id>2</id>
<name lang="en">Bill</name>
<name lang="ru">Билл</name>
</document>
</db>
When I use the JSON RestAPI to retrieve my document I get the following:
"documents": [
{
"id": "1",
"name": [
"John",
"Джон"
]
},
{
"id": "2",
"name": [
"Bill",
"Билл"
]
}]
Is it possible to somehow get the attribute?
I stumbled on the same problem some time ago too. I would not be surprised if there is no way to get attributes back in JSON (as of now), because documentation does not list such scenario.
It's not just Clusterpoint with similar problems. For example - if you would take particular XML, load it in PHP with simplexml and then encode it to JSON, attributes would be dropped too. XML does not map to JSON 1:1 and things which are ambiguous require special care or they get dropped.
There is not enough information on how to convert
<name lang="en">John</name>
<name lang="ru">Джон</name>
And there is no 1 common standard on how to do things. It's clear that this should result in array. But how to structure it? We could convert each element to object and then store objects in array. However what key to give to values? Something like this might be plausible:
{ "name": [
{"lang": "en", "name": "John"},
{"lang": "ru", "name": "Джон"}
]}
But again we run into problem, that its hard to convert data back as we do not know if this came from attributes. We might use different setup:
{ "name": [
{"#lang": "en", "name": "John"},
{"#lang": "ru", "name": "Джон"}
]}
Then # would indicate that this came from attribute. Or like this:
{ "name": [
{"#lang": "en", "#value": "John"},
{"#lang": "ru", "#value": "Джон"}
]}
to indicate that #value always holds value which was in tag.
To sum up - it looks like currently there is no way to get attributes in JSON. If there is - guys/girls from Clusterpoint should add detailed info to their docs. However if there really is no way to get attributes, then I bet that in future versions there will be some documented standard that Clusterpoint will use.
While there is no documented standard on how Clusterpoint handles XML attribute conversion to JSON and you really need attributes - just use XML. Or if you want JSON you can reshape your documents to something that's does not require attributes.

JSON data with array structure

I am using Neo4j and ExtJS in my application.
One good thing is that both handle JSON with array structure.
Neo4j returns and ExtJS can consume JSON like this:
{
columns: ["name", "age"],
data: [
["Peter", 34],
["Mike", 52]
]
}
instead of:
[
{"name": "Peter", "age" 34},
{"name": "Mike", "age" 52},
]
However, from my application server's HTTP API I want people to be able to choose which one of these JSON structures to receive.
So they are both JSON which means the HTTP header should be "Accept": "application/json". But how should I allow them to pick either one of the structures? Should they set a header or a query param? What is best practice?
I think that a simple param in the request would be enough.