JSON Manager
Manage dataset and apply JSON Patch.
Applying patch
Example 1
There is unknown number of products costing (N/A) each.
My first product is (unknown) and the original product was (unknown).
Patch an array of object:
View source code
HTML
<div data-wb-jsonmanager='{
"url": "demo/data-en.json",
"name": "example1",
"patches": [
{ "op": "wb-count", "path": "/products", "set": "/nbproduct" },
{ "op": "copy", "from": "/products/0", "path": "/product"},
{ "op": "wb-nbtolocal", "path": "/price", "prefix": "$" }
]
}'></div>
<p>There is <span data-json-replace="#[example1]/nbproduct">unknown number of</span> products costing <span data-json-replace="#[example1]/price">(N/A)</span> each.</p>
<p>My first product is <span data-json-replace="#[example1]/product">(unknown)</span> and the original product was <span data-json-replace="demo/data-en.json#/product">(unknown)</span>.</p>
<div data-wb-jsonmanager='{
"url": "demo/data-en.json#/level1/level2",
"name": "example2",
"patches": [
{ "op": "wb-toDateISO", "path": "/itm1" }
]
}'>
<p>Patch an array of object:</p>
<ul data-wb-json='{
"url": "#[example2]",
"mapping": [
{ "selector": "li", "value": "/itm1" }
]
}'>
<template>
<li></li>
</template>
</ul>
</div>
File demo/data-en.json
{
"product": "Hello world",
"products": [
"My first product",
"My second product",
"My third product",
"My forth product"
],
"level1": {
"level2": [
{
"itm1": "2016-11-30T07:53:56Z"
},
{
"itm1": "2017-02-01T16:52:00Z"
},
{
"itm1": "2013-09-08T14:14:14Z"
}
]
},
"price": 1234.98
}
Example 2
This example leverages the following patches: patches
, wb-swap
and wb-toDateISO
.
Language:
From to
Location:
Status:
Province(s):
Registered:
- ()
View source code
HTML
<div data-wb-jsonmanager='{
"name": "swapEx",
"url": [ "demo/events.json#/data/eventList", "demo/events-i18n.json" ],
"patches": [
{ "op": "patches", "path": "/items", "patches": [
{ "op": "wb-swap", "path": "/eventLanguage", "ref": "/reference/language/en" },
{ "op": "wb-swap", "path": "/province", "ref": "/reference/province/en" },
{ "op": "wb-swap", "path": "/registrationStatus", "ref": "/reference/registrationStatus/en" },
{ "op": "wb-toDateISO", "path": "/startDate" },
{ "op": "wb-toDateISO", "path": "/endDate" },
{ "op": "patches", "path": "/registeredPeople", "patches": [
{ "op": "wb-swap", "path": "/prefLang", "ref": "/reference/language/en" }
] }
] }
]
}'>
<div data-wb-json='{
"url": "#[swapEx]/items",
"mapping": [
{ "selector": "h4", "value": "/entitle" },
{ "selector": ".event-description", "value": "/endescription/html", "isHTML": "true" },
{ "selector": ".event-language", "value": "/eventLanguage" },
{ "selector": ".event-start", "value": "/startDate" },
{ "selector": ".event-end", "value": "/endDate" },
{ "selector": ".event-location", "value": "/city" },
{ "selector": ".event-status", "value": "/registrationStatus" },
{ "selector": ".event-provinces", "value": "/province", "queryall": "li" },
{ "selector": ".event-registered", "value": "/registeredPeople",
"mapping": [
{ "selector": "[data-language]", "value": "/prefLang" },
{ "selector": "[data-name]", "value": "/name" }
]
}
]
}'>
<template>
<div>
<h4></h4>
<div class="event-description"></div>
<p>Language: <span class="event-language"></span></p>
<p>From: <span class="event-start"></span> to <span class="event-end"></span></p>
<p>Location: <span class="event-location"></span></p>
<p>Status: <span class="event-status"></span></p>
<p>Province(s):</p>
<ul class="event-provinces">
<template>
<li></li>
</template>
</ul>
<p>Registered:</p>
<ul class="event-registered">
<template>
<li><span data-name></span> (<span data-language></span>)</li>
</template>
</ul>
</div>
</template>
</div>
</div>
File demo/events.json
{
"data": {
"eventList": {
"items": [
{
"entitle": "Event #1",
"frtitle": "Évènement #1",
"endescription": {
"html": "<p>This is a big description</p>\n"
},
"frdescription": {
"html": "<p>Ceci est une grosse description</p>\n"
},
"startDate": "2023-04-04T10:30:00.000-04:00",
"endDate": "2023-04-06T10:30:06.000-04:00",
"eventLanguage": "english",
"province": [
"alberta",
"manitoba",
"new_brunswick",
"ontario"
],
"city": "Online",
"registrationStatus": "open",
"registeredPeople": [
{
"name": "John Smith",
"prefLang": "english"
},
{
"name": "Jimmy Neutron",
"prefLang": "english"
}
]
},
{
"entitle": "Event #2",
"frtitle": "Évènement #2",
"endescription": {
"html": "<p>This is a big description</p>\n"
},
"frdescription": {
"html": "<p>Ceci est une grosse description</p>\n"
},
"startDate": "2023-04-04T10:30:00.000-04:00",
"endDate": "2023-04-06T10:30:06.000-04:00",
"eventLanguage": "english",
"province": [
"british_columbia"
],
"city": "Vancouver",
"registrationStatus": "open",
"registeredPeople": [
{
"name": "Debbie Stark",
"prefLang": "english"
}
]
},
{
"entitle": "Event #3",
"frtitle": "Évènement #3",
"endescription": {
"html": "<p>This is a big description</p>\n"
},
"frdescription": {
"html": "<p>Ceci est une grosse description</p>\n"
},
"startDate": "2023-04-04T10:30:00.000-04:00",
"endDate": "2023-04-06T10:30:06.000-04:00",
"eventLanguage": "french",
"province": [
"quebec"
],
"city": "Ottawa",
"registrationStatus": "closed",
"registeredPeople": [
{
"name": "Pierre Tremblay",
"prefLang": "french"
},
{
"name": "Marc Gagnon",
"prefLang": "french"
},
{
"name": "Robert Côté",
"prefLang": "french"
}
]
}
]
}
}
}
File demo/events-i18n.json
{
"reference": {
"language": {
"en": {
"english": "English",
"french": "French"
},
"fr": {
"english": "Anglais",
"french": "Français"
}
},
"province": {
"en": {
"alberta": "Alberta",
"british_columbia": "British Columbia",
"manitoba": "Manitoba",
"new_brunswick": "New Brunswick",
"newfoundland_labrador": "Newfoundland & Labrador",
"northwest_territories": "Northwest Territories",
"nova_scotia": "Nova Scotia",
"nunavut": "Nunavut",
"ontario": "Ontario",
"prince_edward_island": "Prince Edward Island",
"quebec": "Quebec",
"saskatchewan": "Saskatchewan",
"yukon": "Yukon"
},
"fr": {
"alberta": "Alberta",
"british_columbia": "Colombie-Britannique",
"prince_edward_island": "Île-du-Prince-Édouard",
"manitoba": "Manitoba",
"new_brunswick": "Nouveau-Brunswick",
"nova_scotia": "Nouvelle-Écosse",
"nunavut": "Nunavut",
"ontario": "Ontario",
"quebec": "Québec",
"saskatchewan": "Saskatchewan",
"newfoundland_labrador": "Terre-Neuve-et-Labrador",
"northwest_territories": "Territoires du Nord-Ouest",
"yukon": "Yukon"
}
},
"registrationStatus": {
"en": {
"open": "Open",
"closed": "Closed"
},
"fr": {
"open": "Ouvert",
"closed": "Fermé"
}
}
}
}
JSON Output
{
"items": [
{
"entitle": "Event #1",
"frtitle": "Évènement #1",
"endescription": {
"html": "<p>This is a big description</p>\n"
},
"frdescription": {
"html": "<p>Ceci est une grosse description</p>\n"
},
"startDate": "2023-04-04",
"endDate": "2023-04-06",
"eventLanguage": "English",
"province": [
"Alberta",
"Manitoba",
"New Brunswick",
"Ontario"
],
"city": "Online",
"registrationStatus": "Open"
},
{
"entitle": "Event #2",
"frtitle": "Évènement #2",
"endescription": {
"html": "<p>This is a big description</p>\n"
},
"frdescription": {
"html": "<p>Ceci est une grosse description</p>\n"
},
"startDate": "2023-04-04",
"endDate": "2023-04-06",
"eventLanguage": "English",
"province": [
"British Columbia"
],
"city": "Vancouver",
"registrationStatus": "Open"
},
{
"entitle": "Event #3",
"frtitle": "Évènement #3",
"endescription": {
"html": "<p>This is a big description</p>\n"
},
"frdescription": {
"html": "<p>Ceci est une grosse description</p>\n"
},
"startDate": "2023-04-04",
"endDate": "2023-04-06",
"eventLanguage": "French",
"province": [
"Quebec"
],
"city": "Ottawa",
"registrationStatus": "Closed"
}
],
"reference": {
"language": {
"en": {
"english": "English",
"french": "French"
},
"fr": {
"english": "Anglais",
"french": "Français"
}
},
"province": {
"en": {
"alberta": "Alberta",
"british_columbia": "British Columbia",
"manitoba": "Manitoba",
"new_brunswick": "New Brunswick",
"newfoundland_labrador": "Newfoundland & Labrador",
"northwest_territories": "Northwest Territories",
"nova_scotia": "Nova Scotia",
"nunavut": "Nunavut",
"ontario": "Ontario",
"prince_edward_island": "Prince Edward Island",
"quebec": "Quebec",
"saskatchewan": "Saskatchewan",
"yukon": "Yukon"
},
"fr": {
"alberta": "Alberta",
"british_columbia": "Colombie-Britannique",
"prince_edward_island": "Île-du-Prince-Édouard",
"manitoba": "Manitoba",
"new_brunswick": "Nouveau-Brunswick",
"nova_scotia": "Nouvelle-Écosse",
"nunavut": "Nunavut",
"ontario": "Ontario",
"quebec": "Québec",
"saskatchewan": "Saskatchewan",
"newfoundland_labrador": "Terre-Neuve-et-Labrador",
"northwest_territories": "Territoires du Nord-Ouest",
"yukon": "Yukon"
}
},
"registrationStatus": {
"en": {
"open": "Open",
"closed": "Closed"
},
"fr": {
"open": "Ouvert",
"closed": "Fermé"
}
}
}
}
Example 3
This example shows an advanced configuration of loading multiple data sources and inserting them into specific locations within the JSON manager dataset.
List of English products
List of French products
Source code
<div data-wb-jsonmanager='{
"name": "mappingDataSource",
"url": [
"demo/data-en.json",
{
"url": "demo/data-fr.json#/produits",
"path": "/français/liste"
}
]
}'></div>
<div class="row">
<div class="col-md-6">
<p>List of English products</p>
<ul data-wb-json='{
"url": "#[mappingDataSource]/products",
"mapping": [
{ "selector": "li" }
]
}'>
<template>
<li></li>
</template>
</ul>
</div>
<div class="col-md-6">
<p>List of French products</p>
<ul lang="fr" data-wb-json='{
"url": "#[mappingDataSource]/français/liste",
"mapping": [
{ "selector": "li" }
]
}'>
<template>
<li></li>
</template>
</ul>
</div>
</div>
Displaying result from a JSON RESTful API
View source code
Consult the JSON RESTful API github result: https://api.github.com/repos/wet-boew/wet-boew/issues?labels=Work%3A+Accessibility
<h3>Work Accessibility issue <small>WET-BOEW on GitHub</small></h3>
<div class="row">
<aside id="aside" class="col-md-8 col-md-push-2 well" data-wb-jsonmanager='{
"url": "https://api.github.com/repos/wet-boew/wet-boew/issues?labels=Work%3A+Accessibility",
"name": "github",
"patches": [
{ "op": "wb-count", "path": "/rootArray", "set": "/nbissues" },
{ "op": "wb-last", "path": "/rootArray", "set": "/last" },
{ "op": "wb-first", "path": "/rootArray", "set": "/first" },
{ "op": "wb-toDateISO", "path": "/last/created_at", "set": "/oldest" },
{ "op": "wb-toDateISO", "path": "/first/created_at", "set": "/recent" },
{ "op": "add", "path": "/unhideme", "value": "hidden" }
],
"wraproot": "rootArray"
}'>
<h4 class="mrgn-tp-sm">Overview</h4>
<ul class="list-unstyled">
<li>Number of issues: <span data-json-replace="#[github]/nbissues"></span></li>
<li>Most recent issue: <a class="hidden" data-wb-json='[
{
"url": "#[github]/first/title",
"type": "replace"
},
{
"url": "#[github]/first/html_url",
"type": "attr",
"attr": "href"
},
{
"url": "#[github]/unhideme",
"type": "removeclass"
}
]'></a> (<span data-json-replace="#[github]/recent">unknown</span>)</li>
<li>Oldest issue: <a class="hidden" data-wb-json='[
{
"url": "#[github]/last/title",
"type": "replace"
},
{
"url": "#[github]/last/html_url",
"type": "attr",
"attr": "href"
},
{
"url": "#[github]/unhideme",
"type": "removeclass"
}
]'></a> (<span data-json-replace="#[github]/oldest">unknown</span>)</li>
</ul>
<p><a href="https://github.com/wet-boew/wet-boew/issues?q=is%3Aopen+is%3Aissue+label%3A%22Work%3A+Accessibility%22">View open issues on GitHub</a></p>
</aside>
</div>
<p>List of issues:</p>
<div data-wb-jsonmanager='{
"url": "https://api.github.com/repos/wet-boew/wet-boew/issues?labels=General%3A+Accessibility",
"name": "github-src",
"patches": [
{ "op": "wb-toDateISO", "path": "/updated_at" }
]
}'></div>
<ul data-wb-json='{
"url": "#[github-src]",
"mapping": [
{ "selector": "a", "value": "/title" },
{ "selector": "a", "value": "/html_url", "attr": "href" },
{ "selector": "span", "value": "/updated_at" }
]
}'>
<template>
<li>(<span></span>) <a href=""></a></li>
</template>
</ul>
Work Accessibility issue WET-BOEW on GitHub
List of issues:
Content of a GitHub file
Calling a file through GitHub API has to be crafted as such:
https://api.github.com/repos/[Organisation-Name]/[Repository-Name]/contents/[Path-to-file]
Note: Take into consideration that GitHub has a rate limit for API usage, which is currently set to 60 per hour (without authentication).
Escaped HTML content
View source code
<div data-wb-jsonmanager='{
"url": "https://api.github.com/repos/wet-boew/wet-boew/contents/src/plugins/data-ajax/ajax/data-ajax-extra-en.html",
"name": "githubCode",
"patches": [
{ "op": "wb-decodeUTF8Base64", "path": "/content", "set": "/raw" },
{ "op": "copy", "from": "/raw", "path": "/escape" },
{ "op": "wb-escapeHTML", "path": "/escape" }
]
}'>
<div data-json-replace="#[githubCode]/raw">Content here...</div>
<h4>Escaped HTML version</h4>
<pre><code data-json-append="#[githubCode]/escape"></code></pre>
</div>
JSON patches operation
JSON operation is defined as per RFC6002 - JavaScript Object Notation (JSON) Patch with additional operation to fullfill the needs of the JSON manager. Described in the documentation, the following operation are supported by default: add
, remove
, replace
, move
, copy
, test
, wb-count
, wb-first
, wb-last
, wb-decodeUTF8Base64
, wb-escapeHTML
, wb-toDateISO
and wb-toDateTimeISO
.
Debugging
You can set the configuration option "debug": true
to show next to the json manager the modified JSON object with the applied patches. The JSON content are displayed without formating.
Number of products: Not available
View source code
<p data-wb-jsonmanager='{
"url": "demo/data-en.json",
"name": "example3",
"patches": { "op": "wb-count", "path": "/products", "set": "/nbproduct" },
"debug": true
}'>Number of products: <span data-json-replace="#[example3]/nbproduct">Not available</span></p>
Extract the data from the DOM nodes to build the data source JSON object
Use replace
and mapping
on the same JSON data source
The page title is: Not available
The first H2 is: Not available
All h4 from the page:
View source code
<div data-wb-jsonmanager='{
"name": "ex1",
"extractor": [
{ "selector": "title", "path": "pageTitle" },
{ "selector": "h2", "path": "firstH2" },
{ "selector": "h4", "path": "allH4", "selectAll": true }
],
"wraproot": "rootArray"
}'>
<p>The page title is: <span data-json-replace="#[ex1]/rootArray/pageTitle">Not available</span></p>
<p>The first H2 is: <span data-json-replace="#[ex1]/rootArray/firstH2">Not available</span></p>
<p>All H4 from the page:</p>
<ul data-wb-json='{
"url": "#[ex1]/rootArray/allH4",
"mapping": [
{ "selector": "li" }
]
}'>
<template>
<li></li>
</template>
</ul>
</div>
Grouping selectors on the same JSON data source
The description list below is the source of the combined selectors:
- Title 1
- Description 1
- Title 2
- Description 2
- Title 3
- Description 3
Output in a table the combined selectors values :
The page title is: Not available
The first H2 is: Not available
View source code
HTML
<div data-wb-jsonmanager='{
"name": "ex2",
"url": "demo/data-en.json",
"extractor": [
{ "selector": "title", "path": "pageTitle" },
{ "selector": "h2", "path": "firstH2" },
{
"selector": "dl[data-dl]",
"path": "myGroup",
"extractor": [
{
"selector": "dt",
"path": "title",
"attr":"data-td"
},
{
"selector": "dd",
"path": "desc"
}
]
}
]
}'>
<p>Output in a table the combined selectors values :</p>
<table class="table table-striped">
<thead>
<tr data-wb-json='{
"url": "#[ex2]/myGroup",
"mapping": [
{ "selector": "th", "value": "/title" }
]
}'>
<template>
<th></th>
</template>
</tr>
</thead>
<tbody>
<tr data-wb-json='{
"url": "#[ex2]/myGroup",
"mapping": [
{ "selector": "td", "value": "/desc" }
]
}'>
<template>
<td></td>
</template>
</tr>
</tbody>
</table>
<p> The page title is: <span data-json-replace="#[ex2]/pageTitle">Not available</span></p>
<p> The first H2 is: <span data-json-replace="#[ex2]/firstH2">Not available</span></p>
</div>
Extract values from tag attributes or extract all specific tags values:
All H2 from the page:
All H3 from the page (unknown instances):
Returning a null
value when the element is not found by the CSS selector. Test (expect to return 'working'):
working
View source code
HTML
<div data-wb-jsonmanager='{
"name": "ex3",
"extractor": [
{ "path": "/formfields/pageTitle", "selector": "title" },
{ "path": "/formfields/lang", "attr":"lang", "selector": "html" },
{ "path": "/formfields/externalReferer", "interface": "referer" },
{ "path": "/formfields/submissionPage", "interface": "locationHref" },
{ "selector": "h2", "path": "allH2", "selectAll":true },
{ "selector": "h3", "path": "allH3", "selectAll":true }
],
"patches": { "op": "wb-count", "path": "/allH3", "set": "/nbH3Total" }
}'>
<div data-wb-json='{
"url": "#[ex3]/formfields",
"mapping": [
{ "selector": "input", "attr": "name", "value": "/@id" },
{ "selector": "input", "attr": "name", "value": "/@value" }
]
}'>
<template>
<input type="text" name="" value="" >
</template>
</div>
<p>All H2 from the page:</p>
<ul data-wb-json='{
"url": "#[ex3]/allH2",
"mapping": [
{ "selector": "li" }
]
}'>
<template>
<li></li>
</template>
</ul>
<p>All H3 from the page (<span data-json-replace="#[ex3]/nbH3Total">unknown</span> instances):</p>
<ul data-wb-json='{
"url": "#[ex3]/allH3",
"mapping": [
{ "selector": "li" }
]
}'>
<template>
<li></li>
</template>
</ul>
<p>Returning a <code>null</code> value when the element is not found by the CSS selector. Test (expect to return 'working'):
<span data-wb-json='{
"url": "#[ex3]",
"streamline": true,
"mapping": [
{
"template": "[data-not-found-result]",
"test": "fn:guessType",
"assess": "/notfound",
"expect": "undefined",
"mapping": null
}
]
}'>
<template data-not-found-result>
working
</template>
</span>
</p>
</div>
Special test case examples
Consult the special test case examples webpage
- Date modified: