<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Sparna Blog &#187; json-ld</title>
	<atom:link href="https://blog.sparna.fr/tag/json-ld/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.sparna.fr</link>
	<description>Web de données &#124; Architecture de l&#039;information &#124; Accès aux connaissances</description>
	<lastBuildDate>Tue, 03 Jun 2025 10:30:27 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
	<item>
		<title>European Parliament Open Data Portal : a SHACL-powered knowledge graph</title>
		<link>https://blog.sparna.fr/2025/04/09/european-parliament-open-data-portal-a-shacl-powered-knowledge-graph/</link>
		<comments>https://blog.sparna.fr/2025/04/09/european-parliament-open-data-portal-a-shacl-powered-knowledge-graph/#comments</comments>
		<pubDate>Wed, 09 Apr 2025 14:10:12 +0000</pubDate>
		<dc:creator><![CDATA[Marie Muller]]></dc:creator>
				<category><![CDATA[Editeurs]]></category>
		<category><![CDATA[FAIR]]></category>
		<category><![CDATA[Linked Data]]></category>
		<category><![CDATA[Non classé]]></category>
		<category><![CDATA[Ontologies]]></category>
		<category><![CDATA[Open Data]]></category>
		<category><![CDATA[SHACL]]></category>
		<category><![CDATA[SHACL Play]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[DCAT]]></category>
		<category><![CDATA[ELI]]></category>
		<category><![CDATA[google spreadsheets]]></category>
		<category><![CDATA[json-ld]]></category>
		<category><![CDATA[knowledge graph]]></category>
		<category><![CDATA[SKOS]]></category>
		<category><![CDATA[SPARQL]]></category>

		<guid isPermaLink="false">https://blog.sparna.fr/?p=1959</guid>
		<description><![CDATA[<p>A second usecase Thomas wrote for Veronika Heimsbakk’s SHACL for the Practitioner upcoming book is about Sparna&#8217;s work for the European Parliament. From validation of the data in the knowledge graph to further projects of data integration and dissemination, many different usages of SHACL specifications were explored&#8230; &#8230; and more exploratory usages of SHACL are foreseen ! “&#8230;</p>
<p>Cet article <a rel="nofollow" href="https://blog.sparna.fr/2025/04/09/european-parliament-open-data-portal-a-shacl-powered-knowledge-graph/">European Parliament Open Data Portal : a SHACL-powered knowledge graph</a> est apparu en premier sur <a rel="nofollow" href="https://blog.sparna.fr">Sparna Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>A second usecase Thomas wrote for Veronika Heimsbakk’s <em><a href="https://veronahe.wordpress.com/shacl-for-the-practitioner/">SHACL for the Practitioner</a></em> upcoming book is about Sparna&rsquo;s work for the European Parliament.</p>
<p>From validation of the data in the knowledge graph to further projects of data integration and dissemination, many different usages of SHACL specifications were explored&#8230;</p>
<p>&#8230; and more exploratory usages of SHACL are foreseen !</p>
<h1>“</h1>
<h2><strong>A knowledge-graph powered open data portal</strong></h2>
<p><a href="https://data.europarl.europa.eu/">The European Parliament Open Data Portal (EPODP)</a> went live in January 2023. Its particularity is that it is not a mere aggregation of documents or dump files from business applications in custom formats; but rather a <strong>collection of datasets each extracted from a central semantic knowledge graph</strong>, itself aggregating data migrated from approximately <strong>twenty business applications</strong>. The result is a semantically interoperable open data portal : the semantic of its data model is clearly defined and documented, and reuses widely deployed existing ontologies. It already provides its data to different consumers (most notably <a href="https://www.europarl.europa.eu/">the europarl website</a> and <a href="https://law-tracker.europa.eu/">the EU law tracker</a>) in a context of cross-institutions interoperability. The data captures the activity of the parliament : as co-legislator together with the Council of the EU, the European Parliament (EP) holds plenary sittings, in which reports originating from committees, as well as motion for resolutions, are amended and voted; after the vote, the final adopted texts are published.</p>
<p>The focus on semantic interoperability of EPODP maximizes the potential of reuse and linkage of its datasets, and <strong>maximizes the quality</strong> of the offered data. It comes however at a cost when building the portal : deep analysis and understanding of the existing data and documents structure is required to capture the business semantic. SHACL is the way to formally encode this business semantic &#8211; but how is it deployed in practice ? how is it maintained ? what are the different types of SHACL specifications used ?</p>
<h2><strong>SHACL at the center of a model-driven approach</strong></h2>
<p>SHACL in the EPODP is at the basis of multiple model-driven usages depicted in the following diagram:</p>
<p><a href="https://blog.sparna.fr/wp-content/uploads/2025/04/spec-SHACL.png"><img class="aligncenter size-large wp-image-1961" src="https://blog.sparna.fr/wp-content/uploads/2025/04/spec-SHACL-1024x508.png" alt="spec-SHACL" width="650" height="322" /></a></p>
<p>There was two key drivers for introducing the use of SHACL in the EPODP project : <strong>validation of the data</strong> in the knowledge graph, and <strong>generation of public documentations</strong> of the models. The same SHACL specification that captures the business semantic is directly actionable to be published as a documentation and to validate the data. The produced documentation is a set of public files, such as <a href="https://data.europarl.europa.eu/def/eli-ep">the ELI-EP application profile documentation</a> and others accessible from <a href="https://data.europarl.europa.eu/en/developer-corner">the EPODP developer&rsquo;s corner</a>. <a href="https://shacl-play.sparna.fr/play/doc">The SHACL Play documentation generator</a> is used to produce the documentation pages. Data validation happens at earlier stages, after data transformation steps.</p>
<p>Two additional usages of SHACL specifications were explored : one was to generate SPARQL queries to extract the content of datasets from the larger knowledge graph. The SHACL specification of a dataset content is interpreted to generate SPARQL CONSTRUCT queries, executed against the entire knowledge graph, to return a subset of data corresponding to the specification. The query generation was implemented <a href="https://shacl-play.sparna.fr/play/sparql">in SHACL Play</a>, however the EPODP chose to continue using manually crafted SPARQL queries to generate the datasets. The other usage was to complement the SHACL specifications with the mapping rules used to feed the corresponding properties or classes in the graph. This has the advantage that the mapping rules are documented and maintained alongside the specification and not in a separate document. This work is ongoing.</p>
<p>More exploratory usages of SHACL are foreseen : generating a query user interface based on the SHACL specification, <a href="https://docs.sparnatural.eu/how-to-configure-shacl/How-to-configure-Sparnatural-shacl.html">using the <strong>Sparnatural</strong> query builder</a>, and also input forms to facilitate the creation of DCAT datasets descriptions. Additionally, automated generation of the JSON-LD context and the JSON schema of the API are foreseen.</p>
<h2><strong>Not &laquo;&nbsp;1 SHACL to rule them all&nbsp;&raquo;, but application profiles, dataset definitions, and migration specifications</strong></h2>
<p>The definition of the EPODP knowledge graph is not captured in a single SHACL specification, but rather in three different application profiles, each being a selection of classes and properties of one sub-domain : <strong><a href="https://data.europarl.europa.eu/def/eli-ep">ELI-EP</a></strong> covers the description of documents and activities, <strong><a href="https://data.europarl.europa.eu/def/org-ep">ORG-EP</a></strong> covers the definitions of EP organisations (such as committees, political groups, etc.) and members of the parliament, and <strong><a href="https://europarl.github.io/skos-ep">SKOS-EP</a></strong> covers how controlled vocabularies are structured. In addition, <a href="https://data.europarl.europa.eu/def/dcat-ep">DCAT-EP</a> is the specification for how dataset records are described in the EPODP catalog &#8211; but this is not part of the knowledge graph <em>per se</em>.</p>
<p>Together, ELI-EP, ORG-EP and SKOS-EP specify the structure of the entire knowledge graph from which the datasets are extracted. In addition, the structure of each dataset family available in the EPODP (such as adopted texts, plenary documents, parliamentary questions, etc.) is also described in SHACL, referred to as <strong>&laquo;&nbsp;DSD&nbsp;&raquo; for &laquo;&nbsp;Dataset Definition&nbsp;&raquo;</strong>. While the application profiles describe every possible properties on generic shapes, the DSDs will specify only the subset of properties used in a dataset, with possibly different cardinalities or range. For example, ELI-EP specifies that <em>&laquo;&nbsp;a Work may have the property</em><em> </em><em>eli:adopts</em><em>&laquo;&nbsp;</em> (with no minimum cardinality (eli:adopts is defined as <em>&laquo;&nbsp;Indicates that the work represents the adopted work of one or several related works&nbsp;&raquo;</em>). The DSD for adopted texts datasets specifies the shape of &laquo;&nbsp;Adopted texts&nbsp;&raquo; as a subset of the Works, and indicates that the minimum cardinality of eli:adopts is 1 for this particular subset. Besides, some properties, such as eli:amends are not available for adopted texts, thus not declared in the DSD.</p>
<p>In addition, specifications of the conversion of some data sources are also specified in independent SHACL files. The articulations of these 3 kinds of SHACL files and the reused ontologies is depicted in the following diagram:</p>
<p><a href="https://blog.sparna.fr/wp-content/uploads/2025/04/3-SHACL-shapes.png"><img class="aligncenter size-large wp-image-1962" src="https://blog.sparna.fr/wp-content/uploads/2025/04/3-SHACL-shapes-1024x603.png" alt="3-SHACL-shapes" width="650" height="383" /></a></p>
<p>There is currently no reuse or reference of shapes across the different specifications. Each is independent. A nice improvement would be to study how SHACL DSDs could be derived from the application profile SHACL, without redeclaring the identical constraints.</p>
<h2><strong>Editing SHACL in spreadsheets</strong></h2>
<p>In total 16 SHACL specifications are currently published in the EPODP, and around 80 are used to validate data migrated from each individual sources. The first step in the specification of each model is the design in a diagram such as the ones visible in the public documentations of the models. The EPODP team is then using spreadsheets to encode the specifications, adapted from the one provided <a href="https://shacl-play.sparna.fr/play/shaclexcel">in the SHACL Play suite</a>. The spreadsheet is converted to SHACL using <a href="https://xls2rdf.sparna.fr/rest/">the xls2rdf converter</a>. <strong>Spreadsheets provide a simple editing solution</strong>, with an easy learning curve, made even easier with a few formulas to compute cell values automatically. It even provides ways for editing advanced patterns (such as the ability to directly turtle lists for sh:or, or blank nodes for property paths), but of course still limits the expressivity. The following screenshot shows how property shapes look like in the spreadsheet:</p>
<p><a href="https://blog.sparna.fr/wp-content/uploads/2025/04/properties-ELI.png"><img class="aligncenter size-large wp-image-1963" src="https://blog.sparna.fr/wp-content/uploads/2025/04/properties-ELI-1024x442.png" alt="properties-ELI" width="650" height="281" /></a></p>
<h2></h2>
<h2><strong>Results and future perspectives</strong></h2>
<p>The EPODP use-case shows how SHACL can be applied in a systematic way in a data integration and dissemination project : at the data transformation step, at the knowledge graph level, and at the data dissemination. <strong>Public documentation, data validation, data extraction are tasks that can be be automated based on a SHACL specification</strong>. While the context is one of a large public institution, the same approach can be applied in industrial contexts. The SHACL specifications are a cornerstone of such projects, enabling semantic interoperability at large and a mutual understanding between business experts, data analysts, developers, and data consumers.</p>
<p>&nbsp;</p>
<h1>”</h1>
<p>Veronika&rsquo;s book will be divided into three parts :<br class="html-br" /><br class="html-br" />1. Back to Basics<br class="html-br" />Introduction to logic and RDF, brief skimming of the topics. Also covering various world assumptions.</p>
<p>2. Getting to know the stuff<br class="html-br" />Introduction to SHACL, including core, sh-sparql, advanced features.</p>
<p>3. Working with the stuff<br class="html-br" />SHACL Stories. Use cases, user stories and implementations.</p>
<p><em>Image : © European Union, [2024] &#8211; EP</em></p>
<p>Cet article <a rel="nofollow" href="https://blog.sparna.fr/2025/04/09/european-parliament-open-data-portal-a-shacl-powered-knowledge-graph/">European Parliament Open Data Portal : a SHACL-powered knowledge graph</a> est apparu en premier sur <a rel="nofollow" href="https://blog.sparna.fr">Sparna Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.sparna.fr/2025/04/09/european-parliament-open-data-portal-a-shacl-powered-knowledge-graph/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Clean JSON(-LD) from RDF using Framing</title>
		<link>https://blog.sparna.fr/2022/07/20/clean-json-ld-from-rdf-using-framing/</link>
		<comments>https://blog.sparna.fr/2022/07/20/clean-json-ld-from-rdf-using-framing/#comments</comments>
		<pubDate>Wed, 20 Jul 2022 06:56:36 +0000</pubDate>
		<dc:creator><![CDATA[Thomas Francart]]></dc:creator>
				<category><![CDATA[Linked Data]]></category>
		<category><![CDATA[RDF]]></category>
		<category><![CDATA[SHACL Play]]></category>
		<category><![CDATA[Framing]]></category>
		<category><![CDATA[json-ld]]></category>

		<guid isPermaLink="false">http://blog.sparna.fr/?p=1462</guid>
		<description><![CDATA[<p>Say you have a nice RDF knowledge graph based on an ontology, or maybe reusing ontologies, and maybe you have specified the structure of the knowledge graph with SHACL. And now you would like to expose your RDF in JSON in an API, for the average developer (or maybe you would like to produce a&#8230;</p>
<p>Cet article <a rel="nofollow" href="https://blog.sparna.fr/2022/07/20/clean-json-ld-from-rdf-using-framing/">Clean JSON(-LD) from RDF using Framing</a> est apparu en premier sur <a rel="nofollow" href="https://blog.sparna.fr">Sparna Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Say you have a nice RDF knowledge graph based on an ontology, or maybe reusing ontologies, and maybe you have specified the structure of the knowledge graph with SHACL. And now you would like to <strong>expose your RDF in JSON</strong> in an API, for the average developer (or maybe you would like to produce a clean JSON to be indexed by Elastic). And the average developer (or Elastic) does not care about RDF and does not care about the “-LD” in “JSON-LD”, he just cares about JSON; and he is right ! we are here to care about the “-LD” part for him.</p>
<p>So what you need is to produce a clean JSON structure from your raw RDF triples. And when I mean “clean”, I mean :</p>
<ul>
<li><strong>no URIs</strong>. Nowhere. No URIs in JSON keys, No URIs in types of entities, no URIs in the value of properties controlled by a closed list; the only places where it is acceptable to see a URI are : to give the id of the entities, and when making a reference to such an id of entity within the graph; even in these cases the URIs can be shortened.</li>
<li><strong>no fancy JSON-LD keys</strong> like @type, @value, @datatype, @id, etc.</li>
<li><strong>indented</strong>.</li>
</ul>
<p>You have 2 possibilities to do that :</p>
<ol>
<li>You develop a custom script, to either generate a JSON export of your data, or to implement the API that will query the knowledge graph, parse the triples, and generate that clean JSON output.</li>
<li>You use <strong><a href="https://www.w3.org/TR/json-ld11-framing/">JSON-LD framing</a> to automate the production of a clean JSON(-LD) from RDF</strong>.</li>
</ol>
<p>There are 2 nice things about the solution with JSON-LD framing :</p>
<ol>
<li>it can be automated</li>
<li>you automatically retain the RDF compatibility &#8211; because your JSON will necessarily be JSON-LD. This means you can import your nice JSON directly in a triplestore.</li>
</ol>
<p>The principle of JSON-LD framing is that you provide a <a href="https://www.w3.org/TR/json-ld/#the-context">JSON-LD @context </a>with an additionnal <a href="https://www.w3.org/TR/json-ld11-framing/#framing">frame specification</a> that defines how the JSON should be structured (indented), which entity to include at each level (entities can be filtered based on some criteria), and also which properties to include in each entity.</p>
<p>To start with JSON-LD framing, what you need is JSON-LD. Any JSON-LD. Typically the raw JSON-LD serialization that any RDF library or triplestore will produce; that kind of ugly, messy, full-of-URIs-and-@language kind of JSON. So something like:</p>
<p><a href="http://blog.sparna.fr/wp-content/uploads/2022/07/Capture-d’écran-du-2022-07-19-17-30-45.png"><img class="aligncenter size-large wp-image-1465" src="http://blog.sparna.fr/wp-content/uploads/2022/07/Capture-d’écran-du-2022-07-19-17-30-45-1024x658.png" alt="Capture d’écran du 2022-07-19 17-30-45" width="650" height="418" /></a></p>
<p><em>(Brrr, scary, no ?)</em></p>
<p>And then what you need is the <a href="https://json-ld.org/playground/">JSON-LD playground</a> with the “Framed” tab. This will allow you to test your context and frame specification.</p>
<p>And when deployed in production, what you will need is a JSON-LD library that is capable of implementing the JSON-LD framing algorithm. Implementations are listed <a href="https://json-ld.org/#developers">here</a>, and you need an implementation compatible with JSON-LD 1.1.</p>
<h2>Example files</h2>
<p>As an example, I use a JSON-LD file from the French National Library, the one from Les Misérables here : <a href="https://data.bnf.fr/fr/13516296/victor_hugo_les_miserables/">https://data.bnf.fr/fr/13516296/victor_hugo_les_miserables/</a> (download link at the bottom of the page).</p>
<p><strong>You can download <a href="http://blog.sparna.fr/wp-content/uploads/2022/07/les_miserables_jsonld_framing_example.zip">the initial JSON example, the frame specification, and the result in a zip</a>.</strong> The zip also contains intermediate frame specifications.</p>
<h2>The @context</h2>
<p>We&rsquo;ll start by specifying the JSON-LD context part.</p>
<h3><b>Map @type to type and @id to id</b></h3>
<p>Average developer will wonder what are those @type and @id keys. Re-map them straight away to type and id:</p>
<blockquote><p><code>"type" : "@type",<br />
"id" : "@id",<br />
</code></p></blockquote>
<p>Schema.org and lot of other specifications do that.</p>
<h3><b>What about @graph ?<br />
</b></h3>
<p>If you have a named graph at the top, introduced by @graph, my suggestion would be to simply remap it to a fixed key, like &laquo;&nbsp;data&nbsp;&raquo;, or &laquo;&nbsp;entities&nbsp;&raquo; :</p>
<blockquote><p><code>"data" : "@graph",<br />
</code></p></blockquote>
<h3><b>Map RDF properties URIs to JSON keys</b></h3>
<p>Get rid of any trace of URI or short URIs in JSON keys. Declare a term for every property in your graph. The simplest way to do this is to use the local part of the URI (after last “#” or “/”) as the term. Order the context by the alphabetical order of the terms. Terms for properties will usually start with a lowercase letter.</p>
<p>In corner cases you may end up with the same term (such as in the example bnf-onto:subject and dcterms:subject), so in that case you need a different key, I chose “bnf-subject” here for bnf-onto:subject and kept “subject” for dcterms:subject.</p>
<blockquote><p><code>"creator" : "dcterms:creator",<br />
"date" : "dcterms:date",<br />
"dateOfWork" : "rdagroup1elements:dateOfWork",<br />
"depiction" : "foaf:depiction",<br />
"description" : "dcterms:description",<br />
</code></p></blockquote>
<h3><b>Map classes URIs to JSON terms</b></h3>
<p>Now you want to do the same thing to get rid of any trace of URIs in the “type” of entities. Declare a term for every class in your ontology/application profile. List the classes in a different section than the properties. Terms for classes will usually start with an uppercase.</p>
<blockquote><p><code>"Concept" : "skos:Concept",<br />
"Document" : "foaf:Document",<br />
"ExpositionVirtuelle" : "bnf-onto:ExpositionVirtuelle",<br />
</code></p></blockquote>
<h3> <b>Declare object properties with “@type”: “@id”</b></h3>
<p>Now you want to get rid of all those ugly “id”, we are only interested in listing the values. To do that, modify the mapping of the property (here “depiction”) to state its values are URIs. You need to change the mapping from</p>
<blockquote><p><code>"depiction" : "foaf:depiction",</code></p></blockquote>
<p>to</p>
<blockquote><p><code>"depiction" : { "@value" : "foaf:depiction", "@type":"@id" },</code></p></blockquote>
<p>And so parts like this :</p>
<blockquote><p><code>"depiction": [<br />
{<br />
"id": "https://gallica.bnf.fr/ark:/12148/btv1b8438568p.thumbnail"<br />
},<br />
{<br />
"id": "https://gallica.bnf.fr/ark:/12148/btv1b9004781d.thumbnail"<br />
},<br />
{<br />
"id": "https://gallica.bnf.fr/ark:/12148/bpt6k5545348q.thumbnail"<br />
}<br />
]</code></p></blockquote>
<p>Will be turned into</p>
<blockquote><p><code> "depiction": [<br />
"https://gallica.bnf.fr/ark:/12148/btv1b8438568p.thumbnail",<br />
"https://gallica.bnf.fr/ark:/12148/btv1b9004781d.thumbnail",<br />
"https://gallica.bnf.fr/ark:/12148/bpt6k5545348q.thumbnail",<br />
"https://gallica.bnf.fr/ark:/12148/btv1b8438570r.thumbnail"<br />
]</code></p></blockquote>
<h3><b>Map datatypes</b></h3>
<p>Now you want to get rid of the @datatype information for literals. If the value of a property always uses the same datatype, which is the case 99,9% of the time, then you can change the mapping from</p>
<blockquote><p><code>"property" : "http://myproperty",</code></p></blockquote>
<p>to</p>
<blockquote><p><code>"property" : { “@id”: "http://myproperty", “@type”:”xsd:date” }</code></p></blockquote>
<p>(The example used does not have datatype properties.)</p>
<h3><b>Map languages, with fixed language or when multilingual</b></h3>
<p>Now let’s get rid of the @language. For this you have 2 choices : when the language is always the same for the value, you can indicate it in the context, the same way that you would do for the datatype but with the @language key. So you change from</p>
<blockquote><p><code>"description" : "dcterms:description",</code></p></blockquote>
<p>to</p>
<blockquote><p><code>"description" : { “@id” : "dcterms:description", “@language” : “fr” }</code></p></blockquote>
<p>You could even have different terms for different languages, such as :</p>
<blockquote><p><code>"title_fr" : { "@id" : "dcterms:title", "@language" : "fr" },<br />
"title_en" : { "@id" : "dcterms:title", "@language" : "en" },<br />
"title" : { "@id" : "dcterms:title" },</code></p></blockquote>
<p>or when you have multilingual multiple values, you can make the property a language map by declaring it this way:</p>
<blockquote><p><code>"editorialNote" : { "@id" : "skos:editorialNote", "@container" : "@language" },</code></p></blockquote>
<p>Which will turn the language code as a key in the JSON output:</p>
<blockquote><p><code>"editorialNote": {<br />
"fr": [<br />
"BN Cat. gén. (sous : Hugo, comte Victor-Marie) : Les misérables. - . - BN Cat. gén. 1960-1969 (sous : Hugo, Victor) : idem. - . -",<br />
"Laffont-Bompiani, Oeuvres, 1994. - . - GDEL. - . -"<br />
]
},</code></p></blockquote>
<p>In that case, watch out for cases where there is a value without language, it will generate a @none key.</p>
<h3><b>Map controlled list values to JSON terms</b></h3>
<p>By now you already get a much cleaner JSON and almost all “unnecessary” URIs have disappeared. But we still have some URI references that we can clean up : the ones that are references to controlled lists with a finite number of values.</p>
<p>We can declare term mappings for those values just like we did to map properties and classes. BUT &#8211; and this is the trick, <strong>we need to change the property declaration from “@id” to “@vocab” for the replacement to happen</strong>. This is documented in the <a href="https://www.w3.org/TR/json-ld/#type-coercion">&laquo;&nbsp;Type coercion&nbsp;&raquo; section of the spec</a>.</p>
<p>In our example, the mapping to languages and subjects are good candidates to be mapped to JSON terms. So we change</p>
<blockquote><p><code>"language" : { "@id" : "dcterms:language", "@type":"@id" },<br />
"subject" : { "@id" : "dcterms:subject", "@type":"@id" },<br />
</code></p></blockquote>
<p>to</p>
<blockquote><p><code>"language" : { "@id" : "dcterms:language", "@type":"@vocab" },<br />
"subject" : { "@id" : "dcterms:subject", "@type":"@vocab" },<br />
“fre” : “http://id.loc.gov/vocabulary/iso639-2/fre”,<br />
“eng” : “http://id.loc.gov/vocabulary/iso639-2/eng”,</code></p></blockquote>
<h3><b>Shorten remaining URI references<br />
</b></h3>
<p>Now the only URIs left are the ids of the main entities in our graph, and references to those ids. Reference to controlled vocabularies with a limited number of values have been mapped to JSON terms. Although we cannot turn all the remaining URIs to JSON terms (because we can&rsquo;t declare all possible entity URIs in the context), we can shorten them by adding a prefix mapping in the context, in our case:</p>
<blockquote><p><code>"ark-https": "https://data.bnf.fr/ark:/12148/",<br />
</code></p></blockquote>
<p>(I note that there are http:// and https:// URIs in the data, I don&rsquo;t know why)</p>
<p>&nbsp;</p>
<h2>The frame specification</h2>
<p>So now we have clean values, no URIs, no fancy JSON-LD keys. But we still don’t have a structure indented the way the average developer would expect it; and this is where the frame specification comes into play.</p>
<h3><b>Define indentation and filters (and reverse properties if needed)<br />
</b></h3>
<p>The frame specification acts as both a filter/selection mechanism and as a structure definition. At each level you indicate the criterias for the object to be included. In our example we have a skos:Concept (the entry in the library catalog) that is foaf:focus a Work (the Book &laquo;&nbsp;in the real world&nbsp;&raquo;), and that skos:Concept is the subject of many virtual exhibits. We want to have the Concept and the Work at first level, and under the concept the exhibits. But there is a trick : it is the virtual exhibits that points to the concept with a dcterms:subject, and we want it the other direction : Concept is_subject_of Exhibit, so we need a @reverse property.</p>
<p>To do that, add the following reverse mapping declaration: (don&rsquo;t modify the existing one):</p>
<blockquote><p><code>"subject_of" : { "@reverse" : "dcterms:subject" },</code></p></blockquote>
<p>Note the use of &laquo;&nbsp;@reverse&nbsp;&raquo; to indicate that JSON key is to be interpreted from object to subject when turned into triples.</p>
<p>With that in place, we can write our frame specification, which goes right after the @context we have designed before:</p>
<blockquote><p><code>"type" : ["Concept", "Work"],<br />
"subject_of" : {<br />
"type" : "ExpositionVirtuelle"<br />
}</code></p></blockquote>
<p>Note how we use the terms defined in the context previously. This is to be understood the following way : <em>&laquo;&nbsp;at the first level, take any entity with a type of either Concept or Work, then insert a subject_of key and put inside any value that has a type ExpositionVirtuelle&nbsp;&raquo;</em>. This garantees the virtual exhibits objects will go under the Concept, and not above or at the same level. But this is not sufficient, as you will notice if you apply that framing that the Work is <strong>repeated</strong> under the &laquo;&nbsp;focus&nbsp;&raquo; property of the Concept, and at the root level. This is because of the default behavior of the JSON-LD playground regarding <a href="https://www.w3.org/TR/json-ld11-framing/#object-embed-flag">object embedding</a> (objects are always embeded when they are referenced)</p>
<h3><b>Avoid embedding</b></h3>
<p>To avoid embedding when it is undesired, we can set the &laquo;&nbsp;@embed&nbsp;&raquo; option to &laquo;&nbsp;@never&nbsp;&raquo; on the &laquo;&nbsp;focus&nbsp;&raquo; property, like so :</p>
<blockquote><p><code>"type" : ["Concept", "Work"],<br />
"subject_of" : {<br />
"type" : "ExpositionVirtuelle"<br />
},<br />
"focus" : {<br />
"@embed" : "@never",<br />
"@omitDefault": true<br />
}<br />
</code></p></blockquote>
<p>This tells the framing algorithm to never embed the complete entity inside the focus property, just reference the URI instead.</p>
<p>Also, you will notice the use of &laquo;&nbsp;@omitDefault&nbsp;&raquo; to true; this tells the framing algorithm to omit the focus property when it has no value. Otherwise, since the Work does not have a foaf:focus property (only the Concept), then it will get a &laquo;&nbsp;focus&nbsp;&raquo; key set to null.</p>
<h3><b>What about order of keys in the JSON ?</b></h3>
<p>Well, I am sure this can be controlled, either by specifying explicitely all the keys you want, in the order you want them, in the frame specification, or by using an &laquo;&nbsp;ordered&nbsp;&raquo; parameter to the JSON-LD API, but that is not available in the playground.</p>
<p>If you list all keys explicitely in the frame specification, don&rsquo;t forget to use wildcards so that any value will match; wildcards are empty objects with &laquo;&nbsp;{}&nbsp;&raquo;:</p>
<blockquote><p><code>"myProperty" : {}</code></p></blockquote>
<h2>The result</h2>
<p><a href="http://blog.sparna.fr/wp-content/uploads/2022/07/Capture-d’écran-du-2022-07-20-08-01-19.png"><img class="aligncenter size-full wp-image-1475" src="http://blog.sparna.fr/wp-content/uploads/2022/07/Capture-d’écran-du-2022-07-20-08-01-19.png" alt="Capture d’écran du 2022-07-20 08-01-19" width="806" height="360" /></a></p>
<p>Much nicer no ? This is something you can put into the hand of an average developer.</p>
<h2>Automate context generation from SHACL</h2>
<p>Do you have a SHACL specification of the structure of your graph ? wouldn&rsquo;t it be nice to automate the generation of the JSON-LD context from SHACL ? Maybe we could do that in <a href="https://shacl-play.sparna.fr/play/">SHACL-Play</a> ? stay tuned !</p>
<p>Probably what we can automate is the context part, which can be global and unique for all your graph, but the framing specification should probably be different for each different API you need; each framing specification will then reference the same context by its URL.</p>
<p><em>Image : <a href="https://gallica.bnf.fr/ark:/12148/btv1b53230250h/f1.item.r=encadrement.zoom#"><span class="title">[Encadrement ornemental] ([1er état]) / .Io. MIGon 1544. [Jean Mignon] ; [d&rsquo;après Le Primatice]</span></a> <a href="https://gallica.bnf.fr/ark:/12148/btv1b53230250h">https://gallica.bnf.fr/ark:/12148/btv1b53230250h</a></em></p>
<p>Cet article <a rel="nofollow" href="https://blog.sparna.fr/2022/07/20/clean-json-ld-from-rdf-using-framing/">Clean JSON(-LD) from RDF using Framing</a> est apparu en premier sur <a rel="nofollow" href="https://blog.sparna.fr">Sparna Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.sparna.fr/2022/07/20/clean-json-ld-from-rdf-using-framing/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Semantic Markdown Specifications</title>
		<link>https://blog.sparna.fr/2020/02/20/semantic-markdown/</link>
		<comments>https://blog.sparna.fr/2020/02/20/semantic-markdown/#comments</comments>
		<pubDate>Thu, 20 Feb 2020 14:46:34 +0000</pubDate>
		<dc:creator><![CDATA[Thomas Francart]]></dc:creator>
				<category><![CDATA[RDF]]></category>
		<category><![CDATA[Schema.org]]></category>
		<category><![CDATA[json-ld]]></category>
		<category><![CDATA[markdown]]></category>
		<category><![CDATA[rdfa]]></category>
		<category><![CDATA[schema.org]]></category>

		<guid isPermaLink="false">http://blog.sparna.fr/?p=1267</guid>
		<description><![CDATA[<p>Markdown (MD) has become the de facto standard syntax for writing on the web, pushed by Github and StackOverflow. It is heavily used everytime one need to enter a comment, or write a simple (document-style) HTML page. What if we could embed semantic annotations in a markdown document ? We would get Semantic Markdown !&#8230;</p>
<p>Cet article <a rel="nofollow" href="https://blog.sparna.fr/2020/02/20/semantic-markdown/">Semantic Markdown Specifications</a> est apparu en premier sur <a rel="nofollow" href="https://blog.sparna.fr">Sparna Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Markdown (MD) has become the de facto standard syntax for writing on the web, pushed by <a href="http://github.com">Github</a> and <a href="http://stackoverflow.com">StackOverflow</a>. It is heavily used everytime one need to enter a comment, or write a simple (document-style) HTML page. What if we could embed semantic annotations in a markdown document ? We would get <strong>Semantic Markdown</strong> ! imagine the best of both worlds between <em>human readable/writable documents and machine-readable/writable (RDF) structured data</em>. We could feed an RDF knowledge graph that is coupled with our set of MD documents, and we would have an <em>easy way to put structure in content</em>.</p>
<p>I see a lot of potential in this, and already see some use-cases. Unfortunately I don&rsquo;t have the bandwith, nor the full skills to make this happens. So I am just writing this in the hope that the idea is implemented by someone, or that someone tells me it is totally nonsense&#8230;</p>
<p>Here are the semantic annotations use-cases I see with such a Semantic Markdown :</p>
<ol>
<li>Annotate a span or title that corresponds to an entity ;</li>
<li>Annotate a piece of text with an existing URI for an entity;</li>
<li>Create some statements on an entity;</li>
</ol>
<p>Note that I am not necessarily looking for a way to produce RDFa annotations on the generated HTML, although that would be nice for a schema.org use-case. Any conversion route from the original semantically annotated markdown to a set of triples would be fine.</p>
<p>My source of inspiration is essentially <a href="https://kramdown.gettalong.org/syntax.html#span-ials">Span Inline Attribute Lists&nbsp;&raquo; from the Kramdown syntax</a>.</p>
<h2>Annotate a span that corresponds to an entity</h2>
<p>This piece of Semantic Markdown :</p>
<pre>Tomorrow I am travelling to _Berlin_ {.schema:Place}</pre>
<p>When interprered by a Semantic Markdown parser would produce this set of triples :</p>
<pre>_:1 a &lt;http://schema.org/Place&gt; .
_:1 rdfs:label “Berlin” .</pre>
<p>The span immediately preceding the &laquo;&nbsp;{.xxxx}&nbsp;&raquo; annotation is taken as the label of the entity. The use of rdfs:label to store the label of the entity could be subject to a parser configuration option.</p>
<p>One could imagine that a semantic markdown parser relies on the same <a href="https://www.w3.org/2011/rdfa-context/rdfa-1.1">RDFa Initial Context</a> to interpret the &laquo;&nbsp;schema:&nbsp;&raquo; prefix without further declaration. But what about other ontologies ? we would need some kind of prefixes / vocab declaration somewhere in the document, just like in RDFa.</p>
<p>Note also that Markdown parser supporting the &laquo;&nbsp;{.xxxxx}&nbsp;&raquo; syntax will also insert this value as a CSS class on the corresponding span, so we win both on the CSS level and the semantic level.</p>
<h2>Annotate a title</h2>
<p>Similarly, we could annotate a title</p>
<pre>### European Semantic Web Conference {.schema:Event}
Lorem ipsum...</pre>
<p>In that case, the full content of the title is interpreted as the label of the entity :</p>
<pre>_:1 a &lt;http://schema.org/Event&gt; .
_:1 rdfs:label “European Semantic Web Conference” .</pre>
<h2>Annotate with a known URI</h2>
<pre>Tomorrow I am travelling to [Berlin](https://www.wikidata.org/wiki/Q64) {.schema:Place}</pre>
<p>Would yield</p>
<pre>&lt;https://www.wikidata.org/wiki/Q64&gt; a &lt;http://schema.org/Place&gt; .
&lt;https://www.wikidata.org/wiki/Q64&gt; rdfs:label “Berlin” .</pre>
<h2>Describe an entity</h2>
<p>If a list follows an annotated entity, then it should be interpreted as a set of predicates with this entity as subject :</p>
<pre>### Specifications Meeting {.schema:Event}

* Date : _11/10_{.schema:startDate}
* Place {.schema:location} : Our office, Street name, 75014 Paris
* Meeting participants : 
  {.schema:attendee}
  * Thomas Francart{.schema:Person}
  * [Someone else](https://www.wikidata.org/wiki/Q80)
  * Tim Foo
* Description : Some information not annotated

### titre suivant
Lorem ipsum...
</pre>
<p>Should yield :</p>
<pre>_:1 a &lt;http://schema.org/Event&gt; .
_:1 rdfs:label “Specifications Meeting” .
_:1 &lt;http://schema.org/startDate&gt; "11/10" .
_:1 &lt;http://schema.org/location&gt; "Our office, Street name, 75014 Paris" .
_:1 &lt;http://schema.org/attendee&gt; _:2 , &lt;https://www.wikidata.org/wiki/Q80&gt;, _:3 .

# attendee that is annotated : we know a type and a name
_:2 a &lt;http://schema.org/Person&gt;
_:2 rdfs:label “Thomas Francart” .

# attendee that is annotated with a URI : we keep the URI and add a label to it (?)
&lt;https://www.wikidata.org/wiki/Q80&gt; rdfs:label "Someone else" .

# attendee that is not annotated - but we know he was an attendee
_:3 rdfs:label "Tim Foo" .
</pre>
<ol>
<li>If a list follows a title or a paragraph that contains an annotated entity&#8230;</li>
<li>Then items in this list correspond to a property of this entity&#8230;</li>
<li>And can be annotated with a property</li>
<li>The property annotation can be placed on an inline text, or right before or after a `:` or `=` character</li>
<li>If the property annotation immediatly precedes a list, then all items in this list would be considered values for that property, and in that case could be either : entities annotated with a type, or entities identified by a URI, or entites not annotated (and in that case we would consider them as blank nodes with only a label</li>
</ol>
<h2>Related works</h2>
<p><a href="https://blogs.pjjk.net/phil/metadata-for-markdown-mkdocs/">Metadata for Markdown</a>, a Python extension to generated JSON-LD from YAML section in a Markdown document.</p>
<p><strong>EDIT</strong> : PanDoc divs and spans : <a href="https://pandoc.org/MANUAL.html#divs-and-spans">https://pandoc.org/MANUAL.html#divs-and-spans</a></p>
<p>I like the &lt;span&gt; syntax :</p>
<p><code>[This is *some text*]{.class key="val"}</code></p>
<p>This is close ! but still would not produce triples, unless one writes explicitely RDFa :</p>
<p><code>My name is [Thomas Francart]{typeof="schema:Person"}</code></p>
<p>Cet article <a rel="nofollow" href="https://blog.sparna.fr/2020/02/20/semantic-markdown/">Semantic Markdown Specifications</a> est apparu en premier sur <a rel="nofollow" href="https://blog.sparna.fr">Sparna Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.sparna.fr/2020/02/20/semantic-markdown/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
	</channel>
</rss>
