I'm trying to insert a JSON-LD file into my CouchDB. The only problem I have is that when I insert my JSON-LD file, the resulting CouchDB is meaningless because the IDs aren't linked together.
An example of what my JSON-LD file looks like:
"contributor": [
{
"#id": "_:N6e57c55b35b74782ada714fdc6d66bf1"
},
{
"#id": "_:N810e115dfb3348579a7b826a7548095b"
}
And another part:
{
"#id": "_:N6e57c55b35b74782ada714fdc6d66bf1",
"#type": "Person",
"label": "Isely, Duane, 1918-"
},
{
"#id": "_:N810e115dfb3348579a7b826a7548095b",
"#type": "Person",
"label": "Cronquist, Arthur"
}
Now the IDs in "contributor" are linking to the two fields of the second part, which is describing person. I would like to know how to link them (the correct way), so I would get something like this:
"contributor": [
{
"#type": "Person",
"label": "Isely, Duane, 1918-"
},
{
"#type": "Person",
"label": "Cronquist, Arthur"
}
You could use a JSON-LD processor to (re)create nicer JSON for your DB. A good possibility to prescribe the structure of JSON-LD documents is to define a frame.
Quote from spec:
JSON-LD Framing allows developers to query by example and force a specific tree layout to a JSON-LD document.
Example:
Assuming your document looks like
{
"#context": {
"contributor": {
"#type": "#id",
"#id": "http://purl.org/dc/terms/contributor",
"#container": "#list"
},
"label": {
"#id": "http://www.w3.org/2004/02/skos/core#prefLabel"
}
},
"#graph": [
{
"#type": "MainResource",
"#id": "_:foo",
"contributor": [
{
"#id": "_:N6e57c55b35b74782ada714fdc6d66bf1"
},
{
"#id": "_:N810e115dfb3348579a7b826a7548095b"
}
]
},
{
"#id": "_:N6e57c55b35b74782ada714fdc6d66bf1",
"#type": "Person",
"label": "Isely, Duane, 1918-"
},
{
"#id": "_:N810e115dfb3348579a7b826a7548095b",
"#type": "Person",
"label": "Cronquist, Arthur"
}
]
}
Add a JSON-LD Frame like
{
"#context": {
"contributor": {
"#type": "#id",
"#id": "http://purl.org/dc/terms/contributor",
"#container": "#list"
},
"label": {
"#id": "http://www.w3.org/2004/02/skos/core#prefLabel"
}
},
"#type": "MainResource",
"#embed": "always"
}
Throw it to a JSON-LD processor of your choice and you will get something like
{
"#context": {
"contributor": {
"#type": "#id",
"#id": "http://purl.org/dc/terms/contributor",
"#container": "#list"
},
"label": {
"#id": "http://www.w3.org/2004/02/skos/core#prefLabel"
}
},
"#graph": [
{
"#id": "_:b0",
"#type": "http://json-ld.org/playground/MainResource",
"contributor": [
{
"#id": "_:b1",
"#type": "http://json-ld.org/playground/Person",
"label": "Isely, Duane, 1918-"
},
{
"#id": "_:b2",
"#type": "http://json-ld.org/playground/Person",
"label": "Cronquist, Arthur"
}
]
}
]
}
Here is the complete example in json-ld.org/playground
Unfortunately framing is not equaly well supported. So the result depends on the JSON-LD processor you're using.
You can elaborate further by removing "#" signs from your data. Simply add the following to your context:
"type" : "#type",
"id" :"#id"
Also, you can add shortenings for types to your context document
"MainResource": "http://json-ld.org/playground/MainResource"
See example in json-ld.org/playground
For full code java example with rdf4j look here: How to convert RDF to pretty nested JSON using java rdf4j .
Related
I have a problem with handling JSON data from different sources. So, my plan was to use JSON-LD, and store the data from a source in RDF so that I can do some analysis work on them. But I don't know how to turn a regular JSON in a JSON-LD correctly. For example, I don't know how to get the correct context for the JSON-LD object.
In my project, each of the sources contain information about the infra configs. This information can be extracted in JSON format, but each source has a different structure.
In the following example you can see how I try to use "pyld" and "rdflib" to turn a JSON object in a Graph, but you can see that the output is not as expected:
Side note, my question was reported as spam by stackoverflow when I used an URL as IRI, even the example URL used most examples. So if you want to run this example you have to replace the <unique_iri> for a real URL to make it work.
Example
import json
from pyld import jsonld
from rdflib import Graph
# JSON data
nodes = [
{
"sysid": "vm_remote",
"type": "vm",
"name": "remote",
"config": {
"id": "worker_1",
"cpu": "2",
},
"connect": [
"db_users"
]
},
{
"sysid": "db_users",
"type": "db",
"name": "users",
"config": {
"id": "database_1",
"location": "eu_west",
}
}
]
# Define the context for the JSON-LD object
context = {
"#version": 1.1,
"#base": "<unique_iri>/team_name/",
"#vocab": "<unique_iri>/resources/onprem/",
"sysid": "#id",
"type": "#type",
"config": {
"#id": "config",
"#context": {
"#base": "<unique_iri>/team_name/config/"
}
},
"connect": {"#id": "relation#connect", "#type": "#id", "#container": "#set"}
}
doc = {
"#context": context,
"#graph": nodes,
"#id": "graph",
"#type": "graph"
}
print("\nInput JSON-LD:\n" + json.dumps(doc, indent=2))
expended_data = jsonld.expand(doc)
print("\n\Expanded JSON-LD:\n" + json.dumps(expended_data, indent=2))
graph = Graph().parse(data=json.dumps(expended_data), format='json-ld')
print("\nRDF Graph:\n" + graph.serialize(format='json-ld'))
# Find the type of each entry (a resource)
q = """
PREFIX resources: <<unique_iri>/resources/onprem/>
SELECT DISTINCT ?type
WHERE
{
?s resources:type ?type .
}
"""
print()
for row in graph.query(q):
print("Type: %s" % row)
Output
Input JSON-LD:
{
"#context": {
"#version": 1.1,
"#base": "<unique_iri>/team_name/",
"#vocab": "<unique_iri>/resources/onprem/",
"sysid": "#id",
"type": "#type",
"config": {
"#id": "config",
"#context": {
"#base": "<unique_iri>/team_name/config/"
}
},
"connect": {
"#id": "relation#connect",
"#type": "#id",
"#container": "#set"
}
},
"#graph": [
{
"sysid": "vm_remote",
"type": "vm",
"name": "remote",
"config": {
"id": "worker_1",
"cpu": "2"
},
"connect": [
"db_users"
]
},
{
"sysid": "db_users",
"type": "db",
"name": "users",
"config": {
"id": "database_1",
"location": "eu_west"
}
}
],
"#id": "graph",
"#type": "graph"
}
\Expanded JSON-LD:
[
{
"#graph": [
{
"<unique_iri>/resources/onprem/config": [
{
"<unique_iri>/resources/onprem/cpu": [
{
"#value": "2"
}
],
"<unique_iri>/resources/onprem/id": [
{
"#value": "worker_1"
}
]
}
],
"<unique_iri>/resources/onprem/relation#connect": [
{
"#id": "db_users"
}
],
"<unique_iri>/resources/onprem/name": [
{
"#value": "remote"
}
],
"#id": "vm_remote",
"#type": [
"<unique_iri>/resources/onprem/vm"
]
},
{
"<unique_iri>/resources/onprem/config": [
{
"<unique_iri>/resources/onprem/id": [
{
"#value": "database_1"
}
],
"<unique_iri>/resources/onprem/location": [
{
"#value": "eu_west"
}
]
}
],
"<unique_iri>/resources/onprem/name": [
{
"#value": "users"
}
],
"#id": "db_users",
"#type": [
"<unique_iri>/resources/onprem/db"
]
}
],
"#id": "graph",
"#type": [
"<unique_iri>/resources/onprem/graph"
]
}
]
RDF Graph:
[
{
"#id": "file:///C:...",
"#type": [
"<unique_iri>/resources/onprem/graph"
]
}
]
Type: <unique_iri>/resources/onprem/graph
The graph is missing the nodes and I don't understand what I am doing wrong.
I am also not sure how to deal with the config nodes. These should be nodes with their own unique identifier since other data sources will be pointing to these too.
Also, these python libraries give me other results than the online playground tool from json-ld.
Can somebody please help me?
I work for a website with 10000+ products and I decided to input the structured data dynamically via CSS selectors in GTM because doing it manually would be too time consuming. It all worked fine, but then I ran into some problems.
Firstly, the structured data added slashes to links that lead to a 404, like so:
"url": "https:\/\/example.com\/shop\/category\/subcategory\/product"
Secondly, the prices in my country are separated with a comma, and not a dot, so the rich results test returns an error. Is there a way to change this?
Full schema here.
<script type="application/ld+json"> {
"#context": "https://www.schema.org",
"#type": "product",
"sku": "{{sku}}",
"image": ["{{carousel-item active}}"],
"name": "{{product-title}}",
"description": "{{product-description}}",
"category": "{{category}}",
"brand": {
"#type": "brand",
"name": "BRAND",
"logo": "https://example.com/images/logo.png"
},
"offers": {
"#type": "Offer",
"itemCondition": "http://schema.org/NewCondition",
"availability": "http://schema.org/InStock",
"price": "{{price}}",
"priceValidUntil": "2021-11-11",
"priceCurrency": "HRK",
"url": "{{Page URL}}",
"shippingDetails": {
"#type": "OfferShippingDetails",
"shippingRate": {
"#type": "MonetaryAmount",
"value": "25",
"currency": "HRK"
},
"shippingDestination": {
"#type": "DefinedRegion",
"addressCountry": "HR"
},
"deliveryTime": {
"#type": "ShippingDeliveryTime",
"transitTime": {
"#type": "QuantitativeValue",
"minValue": "3",
"maxValue": "5"
},
"cutOffTime": "17:00-08:00",
"businessDays": {
"#type": "OpeningHoursSpecification",
"dayOfWeek": [
"https://schema.org/Monday",
"https://schema.org/Tuesday",
"https://schema.org/Wednesday",
"https://schema.org/Thursday",
"https://schema.org/Friday" ]
}
}
}
}
}
}
</script>
I am very new to this, so please be gentle.
P.S. All other parts of schema work fine.
So, lets consider the following data fetched from db:
[
{
"#id": "http://example.com/1",
"http://example.com/label": "Parent",
"http://example.com/status": "Active",
"http://example.com/children": [
{
"#id": "http://example.com/2"
}
]
},
{
"#id": "http://example.com/2",
"http://example.com/label": "Child",
"http://example.com/status": "Active"
}
]
And the frame:
{
"#context": {
"#base": "http://example.com/",
"#vocab": "http://example.com/"
},
"#graph": {
"status":{}
}
}
The result will look like:
{
"#context": {
"#base": "http://example.com/",
"#vocab": "http://example.com/"
},
"#graph": [
{
"#id": "1",
"children": {
"#id": "2",
"label": "Child",
"status": "Active"
},
"label": "Parent",
"status": "Active"
},
{
"#id": "2",
"label": "Child",
"status": "Active"
}
]
}
As you can see in the first object, in the children section I get some extra parameters in addition to id.
Is there a way I could simplify the children list to just contain ids:
"children": [
"2"
]
I tried adding this to my frame:
"children": {
"#id": "http://example.com/children",
"#type": "#id"
}
But it doesn't work as I expect.
Use framing flags: "#embed": "#never" or "#explicit": true.
{
"#context": {
"#base": "http://example.com/",
"#vocab": "http://example.com/"
},
"#graph": {
"status": {},
"#embed": "#never"
}
}
or
{
"#context": {
"#base": "http://example.com/",
"#vocab": "http://example.com/"
},
"#graph": {
"status": {},
"children": {"#explicit": true, "#omitDefault": true}
}
}
But perhaps all you need is compaction.
If you don't want to compact arrays, toggle the respective option. In JSONLD-Java:
final JsonLdOptions options = new JsonLdOptions();
options.setCompactArrays(false);
Playground: 1, 2, 3.
So, this is my json-ld data:
[
{
"#id": "http://example.com/id2",
"http://www.w3.org/2008/05/skos-xl#literalForm": [
{
"#value": "Secondary entity"
}
]
},
{
"#id": "http://example.com/id1",
"http://example.com/describedBy": [
{
"#id": "http://example.com/id2"
}
],
"http://www.w3.org/2000/01/rdf-schema#label": [
{
"#value": "Main entity"
}
]
}
]
And this is the frame I'm using:
{
"#context" :
{
"label": {
"#id":"http://www.w3.org/2000/01/rdf-schema#label"
},
"describedBy": {
"#id":"http://example.com/describedBy"
},
"id": "#id",
"#vocab" : "http://example.com/",
"#base" : "http://example.com/"
},
"#graph" : {
"describedBy": {}
}
}
The result I'm getting looks like this:
{
"#context": {
"label": {
"#id": "http://www.w3.org/2000/01/rdf-schema#label"
},
"describedBy": {
"#id": "http://example.com/describedBy"
},
"id": "#id",
"#vocab": "http://example.com/",
"#base": "http://example.com/"
},
"id": "id1",
"describedBy": {
"id": "id2",
"http://www.w3.org/2008/05/skos-xl#literalForm": "Secondary entity"
},
"label": "Main entity"
}
Is it possible to frame that data to look like this:
{
"#context": {
"label": {
"#id": "http://www.w3.org/2000/01/rdf-schema#label"
},
"describedBy": {
"#id": "http://example.com/describedBy"
},
"id": "#id",
"#vocab": "http://example.com/",
"#base": "http://example.com/"
},
"id": "id1",
"describedById": "id2",
"describedByLabel": "Secondary entity",
"label": "Main entity"
}
Which is basically creating two new properties: describedById and describedByLabel based on previous describedBy one.
Can't really do this with framing, as "Secondary entity" is a property of the node for "id2", and this would require promoting it to be a property of the "id1" node.
You could do this with a SPARQL CONSTRUCT, but JSON-LD doesn't allow you to really create new data.
I'm trying to create a frame to include every children inside an array, so Detail (see the example) must contain all the other nodes inside itself.
This is an example of the data I'm using, in expanded JSON-LD:
[
{
"#id": "A",
"http://ontology.ayvu.net/#Person": [
{
"#value": "101023",
"#type": "http://www.w3.org/2001/XMLSchema#integer"
}
],
"http://ontology.ayvu.net/#Detail": [
{
"#id": "_:g70157685738360"
},
{
"#id": "_:g70157685722960"
}
]
},
{
"#id": "_:g70157685722960",
"http://ontology.ayvu.net/#Deuda": [
{
"#value": "OFICINA"
}
],
"http://ontology.ayvu.net/#Detalle": [
{
"#value": "100"
"#type": "http://www.w3.org/2001/XMLSchema#decimal"
}
]
},
{
"#id": "_:g70157685738360",
"http://ontology.ayvu.net/#Deuda": [
{
"#value": "3573.04",
"#type": "http://www.w3.org/2001/XMLSchema#decimal"
}
],
"http://ontology.ayvu.net/#Detalle": [
{
"#value": "AUTOMOTORES"
}
]
}
]
The following frame does that (and sets the default vocabulary to http://ontology.ayvu.net/# so that those long URLs disappear):
{
"#context": {
"#vocab": "http://ontology.ayvu.net/#"
},
"Detail": {}
}
The frame ensures that the top-level object contains a Detail property so that you get the right root object. The children are then automatically moved down the JSON tree.
The result will look as follows:
{
"#context": {
"#vocab": "http://ontology.ayvu.net/#"
},
"#graph": [
{
"#id": "A",
"Person": {
"#value": "101023"
"#type": "http://www.w3.org/2001/XMLSchema#integer",
},
"Detail": [
{
"#id": "_:b0",
"Detalle": "AUTOMOTORES",
"Deuda": {
"#value": "3573.04"
"#type": "http://www.w3.org/2001/XMLSchema#decimal",
}
},
{
"#id": "_:b1",
"Detalle": {
"#value": "100"
"#type": "http://www.w3.org/2001/XMLSchema#decimal",
},
"Deuda": "OFICINA"
}
]
}
]
}
Or live in the JSON-LD playground: http://tinyurl.com/q844vkd