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.

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).

Content here...

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'):

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: