]
>
Installation
hello mum");
doc.close();
}
// find current slide based upon location
// first find target anchor and then look
// for associated div element enclosing it
// finally map that to slide number
function findSlideNumber(uri)
{
// first get anchor from page location
var i = uri.indexOf("#");
// check if anchor is entire page
if (i < 0)
return 0; // yes
var anchor = unescape(uri.substr(i+1));
// now use anchor as XML ID to find target
var target = document.getElementById(anchor);
if (!target)
{
// does anchor look like "(2)" for slide 2 ??
// where first slide is (1)
var re = /\((\d)+\)/;
if (anchor.match(re))
{
var num = parseInt(anchor.substring(1, anchor.length-1));
if (num > slides.length)
num = 1;
if (--num < 0)
num = 0;
return num;
}
// accept [2] for backwards compatibility
re = /\[(\d)+\]/;
if (anchor.match(re))
{
var num = parseInt(anchor.substring(1, anchor.length-1));
if (num > slides.length)
num = 1;
if (--num < 0)
num = 0;
return num;
}
// oh dear unknown anchor
return 0;
}
// search for enclosing slide
while (true)
{
// browser coerces html elements to uppercase!
if (target.nodeName.toLowerCase() == "div" &&
hasClass(target, "slide"))
{
// found the slide element
break;
}
// otherwise try parent element if any
target = target.parentNode;
if (!target)
{
return 0; // no luck!
}
};
for (i = 0; i < slides.length; ++i)
{
if (slides[i] == target)
return i; // success
}
// oh dear still no luck
return 0;
}
// find slide name from first h1 element
// default to document title + slide number
function slideName(index)
{
var name = null;
var slide = slides[index];
var heading = findHeading(slide);
if (heading)
name = extractText(heading);
if (!name)
name = title + "(" + (index + 1) + ")";
name.replace(/\&/g, "&");
name.replace(/\/g, ">");
return name;
}
// find first h1 element in DOM tree
function findHeading(node)
{ if (!node || node.nodeType != 1)
return null;
if (node.nodeName == "H1" || node.nodeName == "h1")
return node;
var child = node.firstChild;
while (child)
{
node = findHeading(child);
if (node)
return node;
child = child.nextSibling;
}
return null;
}
// recursively extract text from DOM tree
function extractText(node)
{
if (!node)
return "";
// text nodes
if (node.nodeType == 3)
return node.nodeValue;
// elements
if (node.nodeType == 1)
{
node = node.firstChild;
var text = "";
while (node)
{
text = text + extractText(node);
node = node.nextSibling;
}
return text;
}
return "";
}
// find copyright text from meta element
function findCopyright()
{
var name, content;
var meta = document.getElementsByTagName("meta");
for (var i = 0; i < meta.length; ++i)
{
name = meta[i].getAttribute("name");
content = meta[i].getAttribute("content");
if (name == "copyright")
return content;
}
return null;
}
function findSizeAdjust()
{
var name, content, offset;
var meta = document.getElementsByTagName("meta");
for (var i = 0; i < meta.length; ++i)
{
name = meta[i].getAttribute("name");
content = meta[i].getAttribute("content");
if (name == "font-size-adjustment")
return 1 * content;
}
return 1;
}
function addToolbar()
{
var slideCounter, page;
var toolbar = createElement("div");
toolbar.setAttribute("class", "toolbar");
if (ns_pos) // a reasonably behaved browser
{
var right = document.createElement("div");
right.setAttribute("style", "float: right; text-align: right");
slideCounter = document.createElement("div")
slideCounter.innerHTML = "slide".localize() + " n/m";
right.appendChild(slideCounter);
toolbar.appendChild(right);
var left = document.createElement("div");
left.setAttribute("style", "text-align: left");
// global end of slide indicator
eos = document.createElement("span");
eos.innerHTML = "* ";
left.appendChild(eos);
var help = document.createElement("a");
help.setAttribute("href", helpPage);
help.setAttribute("title", helpText.localize());
help.innerHTML = " ".localize();
left.appendChild(help);
helpAnchor = help; // save for focus hack
var gap1 = document.createTextNode(" ");
left.appendChild(gap1);
var contents = document.createElement("a");
contents.setAttribute("href", "javascript:toggleTableOfContents()");
contents.setAttribute("title", "table of contents".localize());
contents.innerHTML = "Table of Contents ".localize();
left.appendChild(contents);
var gap2 = document.createTextNode("..............................................");
left.appendChild(gap2);
var start = document.createElement("a");
start.setAttribute("href", "javascript:firstSlide()");
start.setAttribute("title", "restart presentation".localize());
start.innerHTML = " ".localize();
// start.setAttribute("href", "javascript:printSlides()");
// start.setAttribute("title", "print all slides".localize());
// start.innerHTML = "print!".localize();
left.appendChild(start);
// var identity = document.createTextNode("");
// left.appendChild(identity);
var copyright = findCopyright();
if (copyright)
{
var span = document.createElement("span");
span.innerHTML = copyright;
span.style.color = "black";
span.style.marginLeft = "4em";
left.appendChild(span);
}
toolbar.appendChild(left);
}
else // IE so need to work around its poor CSS support
{
toolbar.style.position = (ie7 ? "fixed" : "absolute");
toolbar.style.zIndex = "200";
toolbar.style.width = "99.9%";
toolbar.style.height = "1.2em";
toolbar.style.top = "auto";
toolbar.style.bottom = "0";
toolbar.style.left = "0";
toolbar.style.right = "0";
toolbar.style.textAlign = "left";
toolbar.style.fontSize = "60%";
toolbar.style.color = "red";
toolbar.borderWidth = 0;
toolbar.style.background = "rgb(240,240,240)";
// would like to have help text left aligned
// and page counter right aligned, floating
// div's don't work, so instead use nested
// absolutely positioned div's.
var sp = document.createElement("span");
sp.innerHTML = " * ";
toolbar.appendChild(sp);
eos = sp; // end of slide indicator
var help = document.createElement("a");
help.setAttribute("href", helpPage);
help.setAttribute("title", helpText.localize());
help.innerHTML = "help?".localize();
toolbar.appendChild(help);
helpAnchor = help; // save for focus hack
var gap1 = document.createTextNode(" ");
toolbar.appendChild(gap1);
var contents = document.createElement("a");
contents.setAttribute("href", "javascript:toggleTableOfContents()");
contents.setAttribute("title", "table of contents".localize());
contents.innerHTML = "contents?".localize();
toolbar.appendChild(contents);
var gap2 = document.createTextNode(" ");
toolbar.appendChild(gap2);
var start = document.createElement("a");
start.setAttribute("href", "javascript:firstSlide()");
start.setAttribute("title", "restart presentation".localize());
start.innerHTML = "restart?".localize();
// start.setAttribute("href", "javascript:printSlides()");
// start.setAttribute("title", "print all slides".localize());
// start.innerHTML = "print!".localize();
toolbar.appendChild(start);
var copyright = findCopyright();
if (copyright)
{
var span = document.createElement("span");
span.innerHTML = copyright;
span.style.color = "black";
span.style.marginLeft = "2em";
toolbar.appendChild(span);
}
slideCounter = document.createElement("div")
slideCounter.style.position = "absolute";
slideCounter.style.width = "auto"; //"20%";
slideCounter.style.height = "1.2em";
slideCounter.style.top = "auto";
slideCounter.style.bottom = 0;
slideCounter.style.right = "0";
slideCounter.style.textAlign = "right";
slideCounter.style.color = "red";
slideCounter.style.background = "rgb(240,240,240)";
slideCounter.innerHTML = "slide".localize() + " n/m";
toolbar.appendChild(slideCounter);
}
// ensure that click isn't passed through to the page
toolbar.onclick = stopPropagation;
document.body.appendChild(toolbar);
slideNumElement = slideCounter;
setEosStatus(false);
return toolbar;
}
function isShownToc()
{
if (toc && toc.style.visible == "visible")
return true;
return false;
}
function showTableOfContents()
{
if (toc)
{
if (toc.style.visibility != "visible")
{
toc.style.visibility = "visible";
toc.style.display = "block";
toc.focus();
if (ie7 && slidenum == 0)
setTimeout("ieHack()", 100);
}
else
hideTableOfContents();
}
}
function hideTableOfContents()
{
if (toc && toc.style.visibility != "hidden")
{
toc.style.visibility = "hidden";
toc.style.display = "none";
try
{
if (!opera)
helpAnchor.focus();
}
catch (e)
{
}
}
}
function toggleTableOfContents()
{
if (toc)
{
if (toc.style.visible != "visible")
showTableOfContents();
else
hideTableOfContents();
}
}
// called on clicking toc entry
function gotoEntry(e)
{
var target;
if (!e)
var e = window.event;
if (e.target)
target = e.target;
else if (e.srcElement)
target = e.srcElement;
// work around Safari bug
if (target.nodeType == 3)
target = target.parentNode;
if (target && target.nodeType == 1)
{
var uri = target.getAttribute("href");
if (uri)
{
//alert("going to " + uri);
var slide = slides[slidenum];
hideSlide(slide);
slidenum = findSlideNumber(uri);
slide = slides[slidenum];
lastShown = null;
setLocation();
setVisibilityAllIncremental("hidden");
setEosStatus(!nextIncrementalItem(lastShown));
showSlide(slide);
//target.focus();
try
{
if (!opera)
helpAnchor.focus();
}
catch (e)
{
}
}
}
hideTableOfContents(e);
if (ie7) ieHack();
stopPropagation(e);
return cancel(e);
}
// called onkeydown for toc entry
function gotoTocEntry(event)
{
var key;
if (!event)
var event = window.event;
// kludge around NS/IE differences
if (window.event)
key = window.event.keyCode;
else if (event.which)
key = event.which;
else
return true; // Yikes! unknown browser
// ignore event if key value is zero
// as for alt on Opera and Konqueror
if (!key)
return true;
// check for concurrent control/command/alt key
// but are these only present on mouse events?
if (event.ctrlKey || event.altKey)
return true;
if (key == 13)
{
var uri = this.getAttribute("href");
if (uri)
{
//alert("going to " + uri);
var slide = slides[slidenum];
hideSlide(slide);
slidenum = findSlideNumber(uri);
slide = slides[slidenum];
lastShown = null;
setLocation();
setVisibilityAllIncremental("hidden");
setEosStatus(!nextIncrementalItem(lastShown));
showSlide(slide);
//target.focus();
try
{
if (!opera)
helpAnchor.focus();
}
catch (e)
{
}
}
hideTableOfContents();
if (ie7) ieHack();
return cancel(event);
}
if (key == 40 && this.next)
{
this.next.focus();
return cancel(event);
}
if (key == 38 && this.previous)
{
this.previous.focus();
return cancel(event);
}
return true;
}
function isTitleSlide(slide)
{
return hasClass(slide, "title");
}
// create div element with links to each slide
function tableOfContents()
{
var toc = document.createElement("div");
addClass(toc, "toc");
//toc.setAttribute("tabindex", "0");
var heading = document.createElement("div");
addClass(heading, "toc-heading");
heading.innerHTML = "Table of Contents".localize();
heading.style.textAlign = "center";
heading.style.width = "100%";
heading.style.margin = "0";
heading.style.marginBottom = "1em";
heading.style.borderBottomStyle = "solid";
heading.style.borderBottomColor = "rgb(180,180,180)";
heading.style.borderBottomWidth = "1px";
toc.appendChild(heading);
var previous = null;
for (var i = 0; i < slides.length; ++i)
{
var title = hasClass(slides[i], "title");
var num = document.createTextNode((i + 1) + ". ");
toc.appendChild(num);
var a = document.createElement("a");
a.setAttribute("href", "#(" + (i+1) + ")");
if (title)
addClass(a, "titleslide");
var name = document.createTextNode(slideName(i));
a.appendChild(name);
a.onclick = gotoEntry;
a.onkeydown = gotoTocEntry;
a.previous = previous;
if (previous)
previous.next = a;
toc.appendChild(a);
if (i == 0)
toc.first = a;
if (i < slides.length - 1)
{
var br = document.createElement("br");
toc.appendChild(br);
}
previous = a;
}
toc.focus = function () {
if (this.first)
this.first.focus();
}
toc.onmouseup = mouseButtonUp;
toc.onclick = function (e) {
e||(e=window.event);
if (selectedTextLen <= 0)
hideTableOfContents();
stopPropagation(e);
if (e.cancel != undefined)
e.cancel = true;
if (e.returnValue != undefined)
e.returnValue = false;
return false;
};
toc.style.position = "absolute";
toc.style.zIndex = "300";
toc.style.width = "60%";
toc.style.maxWidth = "30em";
toc.style.height = "30em";
toc.style.overflow = "auto";
toc.style.top = "auto";
toc.style.right = "auto";
toc.style.left = "4em";
toc.style.bottom = "4em";
toc.style.padding = "1em";
toc.style.background = "rgb(240,240,240)";
toc.style.borderStyle = "solid";
toc.style.borderWidth = "2px";
toc.style.fontSize = "60%";
document.body.insertBefore(toc, document.body.firstChild);
return toc;
}
function replaceByNonBreakingSpace(str)
{
for (var i = 0; i < str.length; ++i)
str[i] = 160;
}
function initOutliner()
{
var items = document.getElementsByTagName("LI");
for (var i = 0; i < items.length; ++i)
{
var target = items[i];
if (!hasClass(target.parentNode, "outline"))
continue;
target.onclick = outlineClick;
if (!ns_pos)
{
target.onmouseover = hoverOutline;
target.onmouseout = unhoverOutline;
}
if (foldable(target))
{
target.foldable = true;
target.onfocus = function () {outline = this;};
target.onblur = function () {outline = null;};
if (!target.getAttribute("tabindex"))
target.setAttribute("tabindex", "0");
if (hasClass(target, "expand"))
unfold(target);
else
fold(target);
}
else
{
addClass(target, "nofold");
target.visible = true;
target.foldable = false;
}
}
}
function foldable(item)
{
if (!item || item.nodeType != 1)
return false;
var node = item.firstChild;
while (node)
{
if (node.nodeType == 1 && isBlock(node))
return true;
node = node.nextSibling;
}
return false;
}
function fold(item)
{
if (item)
{
removeClass(item, "unfolded");
addClass(item, "folded");
}
var node = item ? item.firstChild : null;
while (node)
{
if (node.nodeType == 1 && isBlock(node)) // element
{
// note that getElementStyle won't work for Safari 1.3
node.display = getElementStyle(node, "display", "display");
node.style.display = "none";
node.style.visibility = "hidden";
}
node = node.nextSibling;
}
item.visible = false;
}
function unfold(item)
{
if (item)
{
addClass(item, "unfolded");
removeClass(item, "folded");
}
var node = item ? item.firstChild : null;
while (node)
{
if (node.nodeType == 1 && isBlock(node)) // element
{
// with fallback for Safari, see above
node.style.display = (node.display ? node.display : "block");
node.style.visibility = "visible";
}
node = node.nextSibling;
}
item.visible = true;
}
function outlineClick(e)
{
var rightclick = false;
var target;
if (!e)
var e = window.event;
if (e.target)
target = e.target;
else if (e.srcElement)
target = e.srcElement;
// work around Safari bug
if (target.nodeType == 3)
target = target.parentNode;
while (target && target.visible == undefined)
target = target.parentNode;
if (!target)
return true;
if (e.which)
rightclick = (e.which == 3);
else if (e.button)
rightclick = (e.button == 2);
if (!rightclick && target.visible != undefined)
{
if (target.foldable)
{
if (target.visible)
fold(target);
else
unfold(target);
}
stopPropagation(e);
e.cancel = true;
e.returnValue = false;
}
return false;
}
function hoverOutline(e)
{
var target;
if (!e)
var e = window.event;
if (e.target)
target = e.target;
else if (e.srcElement)
target = e.srcElement;
// work around Safari bug
if (target.nodeType == 3)
target = target.parentNode;
while (target && target.visible == undefined)
target = target.parentNode;
if (target && target.foldable)
target.style.cursor = "pointer";
return true;
}
function unhoverOutline(e)
{
var target;
if (!e)
var e = window.event;
if (e.target)
target = e.target;
else if (e.srcElement)
target = e.srcElement;
// work around Safari bug
if (target.nodeType == 3)
target = target.parentNode;
while (target && target.visible == undefined)
target = target.parentNode;
if (target)
target.style.cursor = "default";
return true;
}
function stopPropagation(e)
{
if (window.event)
{
window.event.cancelBubble = true;
//window.event.returnValue = false;
}
else if (e)
{
e.cancelBubble = true;
e.stopPropagation();
//e.preventDefault();
}
}
/* can't rely on display since we set that to none to hide things */
function isBlock(elem)
{
var tag = elem.nodeName;
return tag == "OL" || tag == "UL" || tag == "P" ||
tag == "LI" || tag == "TABLE" || tag == "PRE" ||
tag == "H1" || tag == "H2" || tag == "H3" ||
tag == "H4" || tag == "H5" || tag == "H6" ||
tag == "BLOCKQUOTE" || tag == "ADDRESS";
}
function getElementStyle(elem, IEStyleProp, CSSStyleProp)
{
if (elem.currentStyle)
{
return elem.currentStyle[IEStyleProp];
}
else if (window.getComputedStyle)
{
var compStyle = window.getComputedStyle(elem, "");
return compStyle.getPropertyValue(CSSStyleProp);
}
return "";
}
// works with text/html and text/xhtml+xml with thanks to Simon Willison
function createElement(element)
{
if (typeof document.createElementNS != 'undefined')
{
return document.createElementNS('http://www.w3.org/1999/xhtml', element);
}
if (typeof document.createElement != 'undefined')
{
return document.createElement(element);
}
return false;
}
// designed to work with both text/html and text/xhtml+xml
function getElementsByTagName(name)
{
if (typeof document.getElementsByTagNameNS != 'undefined')
{
return document.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', name);
}
if (typeof document.getElementsByTagName != 'undefined')
{
return document.getElementsByTagName(name);
}
return null;
}
/*
// clean alternative to innerHTML method, but on IE6
// it doesn't work with named entities like
// which need to be replaced by numeric entities
function insertText(element, text)
{
try
{
element.textContent = text; // DOM3 only
}
catch (e)
{
if (element.firstChild)
{
// remove current children
while (element.firstChild)
element.removeChild(element.firstChild);
}
element.appendChild(document.createTextNode(text));
}
}
// as above, but as method of all element nodes
// doesn't work in IE6 which doesn't allow you to
// add methods to the HTMLElement prototype
if (HTMLElement != undefined)
{
HTMLElement.prototype.insertText = function(text) {
var element = this;
try
{
element.textContent = text; // DOM3 only
}
catch (e)
{
if (element.firstChild)
{
// remove current children
while (element.firstChild)
element.removeChild(element.firstChild);
}
element.appendChild(document.createTextNode(text));
}
};
}
*/
function getSelectedText()
{
try
{
if (window.getSelection)
return window.getSelection().toString();
if (document.getSelection)
return document.getSelection().toString();
if (document.selection)
return document.selection.createRange().text;
}
catch (e)
{
return "";
}
return "";
}
//]]>
JSONiq
The XQuery Vision in 2000
The World Wide Web promises to transform human society by making
virtually all types of information instantly available
everywhere. Two prerequisites for this promise to be realized are
a universal markup language and a universal query
language. The power and flexibility of XML make it the
leading candidate for a universal markup language.
Don Chamberlin, Jonathan Robie, Daniela Florescu, 2000
www.almaden.ibm.com/cs/people/chamberlin/quilt_lncs.pdf
The XQuery Vision in 2000
XML provides a way to label information from diverse data
sources including structured and semi-structured documents,
relational databases, and object repositories.
The Extensible Markup Language, XML, is having a profoundly
unifying effect on diverse forms of information. For the first
time, XML provides an information interchange format that is
editable, easily parsed, and capable of representing nearly any
kind of structured or semi-structured information.
Don Chamberlin, Jonathan Robie, Daniela Florescu, 2000
www.almaden.ibm.com/cs/people/chamberlin/quilt_lncs.pdf
The JSON Vision
Unfortunately, XML is not well suited to data-interchange, much
as a wrench is not well-suited to driving nails. It carries a lot of baggage, and it doesn't match
the data model of most programming languages. When most
programmers saw XML for the first time, they were shocked at how
ugly and inefficient it was. It turns out that that first
reaction was the correct one. There is another
text notation that has all of the advantages of XML, but is much
better suited to data-interchange. That notation is
JavaScript Object Notation (JSON).
Douglas Crockford
http://www.json.org/xml.html
The JSON Vision
JSON is a better data exchange format. XML is a better document
exchange format. Use the right tool for the right job.
Douglas Crockford
http://www.json.org/xml.html
JSON, XML, and Semi-Structured Data
The good news and the bad news:
Semi-structured data is wildly successful.
There are at least two markup languages.
JSON is replacing or supplementing XML in many
new environments, APIs, and applications.
XML remains the best choice for documents, when schemas are needed, for existing standards.
XML ecosystem is ubiquitous and mature, JSON tools are evolving.
JSONiq: XQuery for JSON
A query language for JSON
XQuery is well suited for hierarchical semi-structured data. Many implementations exist, and can easily be adapted to add JSON support.
Goal: A powerful query language for JSON, without the complexity of XML.
JSONiq XQ--
The fat-free XQuery — drop XML, use JSON instead.
Goal: A data integration query language that can query and create JSON, XML, or HTML.
JSONiq XQ++
The fatter XQuery that can also do JSON.
JSONiq: JSON for XQuery
JSON is often much simpler for intermediate results in a query, passing parameters to functions, returning results from functions..
XML is the only complex data structure in XQuery
XML does not have references
Example: returning full-text results
XML makes the programmer think harder
XML makes optimization harder
Document order, namespace handling, whitespace handling, identity, axes ...
JSONiq in a nutshell
Adds JSON constructs to XQuery Data Model
Object and Array Constructors
{
"name" : "Sarah",
"age" : 13,
"gender" : "female",
"friends" : [ "Jim", "Mary", "Jennifer"]
}
Member Accessors
$sarah("friends")(1)
JSONiq in a nutshell (2)
Composes with XQuery expressions
let $sarah := collection("users")[.("name") = "Sarah"]
return {
"name" : "Amanda",
"age" : $sarah("age") + 1,
"gender" : "female",
"friends" : $sarah("friends")
}
Wherever a value can occur, an expression can also occur — the composability SQL never had.
A Grouping Query in JSONiq (Data)
Data — collection("sales"):
{ "product" : "broiler", "store number" : 1, "quantity" : 20 },
{ "product" : "toaster", "store number" : 2, "quantity" : 100 },
{ "product" : "toaster", "store number" : 2, "quantity" : 50 },
{ "product" : "toaster", "store number" : 3, "quantity" : 50 },
{ "product" : "blender", "store number" : 3, "quantity" : 100 },
{ "product" : "blender", "store number" : 3, "quantity" : 150 },
{ "product" : "socks", "store number" : 1, "quantity" : 500 },
{ "product" : "socks", "store number" : 2, "quantity" : 10 },
{ "product" : "shirt", "store number" : 3, "quantity" : 10 }
Group sales by product, across stores.
A Grouping Query in JSONiq
Query: group sales by product, across stores.
{
for $sales in collection("sales")
let $pname := $sales("product")
group by $pname
return $pname : sum(for $s in $sales return $s("quantity"))
}
Result:
{
"blender" : 250,
"broiler" : 20,
"shirt" : 10,
"socks" : 510,
"toaster" : 200
}
More complex grouping (Data)
Group sales by state, category, product
Data — collection("products"):
{ "name" : "broiler", "category" : "kitchen", "price" : 100, "cost" : 70 },
{ "name" : "toaster", "category" : "kitchen", "price" : 30, "cost" : 10 },
{ "name" : "blender", "category" : "kitchen", "price" : 50, "cost" : 25 },
{ "name" : "socks", "category" : "clothes", "price" : 5, "cost" : 2 },
{ "name" : "shirt", "category" : "clothes", "price" : 10, "cost" : 3 }
Data — collection("stores"):
{ "store number" : 1, "state" : CA },
{ "store number" : 2, "state" : CA },
{ "store number" : 3, "state" : MA },
{ "store number" : 4, "state" : MA }
More complex grouping
Query: Group sales by state, category, product
{
for $store in collection("stores")
let $state := $store("state")
group by $state
return
$state : {
for $product in collection("products")
let $category := $product("category")
group by $category
return
$category : {
for $sales in collection("sales")
where $sales("store number") = $store("store number")
and $sales("product") = $product("name")
let $pname := $sales("product")
group by $pname
return $pname : sum( for $s in $sales return $s("quantity") )
}
}
}
More complex grouping
Query synopsis:
{
$state : {
$category : {
$pname : sum( for $s in $sales return $s("quantity") )
}
}
}
Result:
{
"CA" : {
"clothes" : {
"socks" : 510
},
"kitchen" : {
"broiler" : 20,
"toaster" : 150
}
},
"MA" : {
"clothes" : {
"shirt" : 10
},
"kitchen" : {
"blender" : 250,
"toaster" : 50
}
}
}
Views of Relational Data
userid
|
ticker
|
shares
|
---|
W0342
|
DIS
|
153212312
|
M0535
|
DIS
|
10
|
M0535
|
AIG
|
23412
|
[
{ "userid" : "W0342", "ticker" : "DIS", "shares" : 153212312 },
{ "userid" : "M0535", "ticker" : "DIS", "shares" : 10 },
{ "userid" : "M0535", "ticker" : "AIG", "shares" : 23412 }
]
JSON is better than XML for this - XML is more complex for humans and for query optimizers.
Converting JSON <=> XML <=> HTML
JSON:
{
"col labels" : ["singular", "plural"],
"row labels" : ["1p", "2p", "3p"],
"data" :
[
["spinne", "spinnen"],
["spinnst", "spinnt"],
["spinnt", "spinnen"]
]
}
Query:
(: Column headings :)
{
| ,
for $th in values(json("table.json")("col labels"))
return { $th } |
}
{ (: Data for each row :)
for $r at $i in values(json("table.json")("data"))
return
{
{ values(json("table.json")("row labels")[$i]) } | ,
for $c in $r
return { $c } |
}
}
]]>
Sample Use Cases
Some of the use cases at http://jsoniq.org:
Joins
Grouping
Windowing and Events
Updates
JSON views in middleware
Data transformations
Convert XML to JSON
Convert JSON to XML
Convert either to HTML
Python API
from jsoniq import Connection
connection = Connection("probono", "94306")
connection.set(sasl_mechs="GSSAPI", ssl="true", ssl_cert_alias="cert1")
try:
connection.open()
dbase = connection.risotto
collection = dbase.developers
a = {"name": {"last": "brantner", "first": "matthias" }, "role": "wizard" }
b = {"name": {"last": "westmann", "first": "till" }, "role": "demigod" }
collection.insert(a)
collection.insert(b)
q = """
for $d in collection("developers")
where $d("role") = "wizard"
return $d
"""
# A simple query.
#
# query() prepares a query and executes it
cursor = dbase.query(q)
for c in cursor:
json.dumps(c)
# A prepared query that binds variables
q = """
for $d in collection("developers")
where $d("role") = $role
return $d
"""
query = dbase.prepare(q)
query.bind(role="wizard")
cursor = query.execute()
for c in cursor:
json.dumps(c)
# Inspecting free variables and bindings
q = """
for $d in collection("developers")
where $d("role") = $role
return $d
"""
query = dbase.prepare(q)
for f in query.free(): # returns all free variables
if f:isbound:
print f.name(), f.value()
else
print f.name(), " is not bound!"
query.bind(f.name(), "comedian")
# The API expects an implementation to be smart enough to keep track
# of all open databases for a connection, all open queries for a
# database, etc., and to close them appropriately.
except ConnectionError,c:
print c
finally:
connection.close()
JSONiq and XQuery: quo vadis?
XSLT developed a maps proposal that didn't satisfy our use cases
... and the XQuery people gave no feedback for a long time ...
JSONiq developed use cases that were not accepted, then developed their own language
XSL and XQuery have now developed a set of use cases and initial requirements from both proposals
At least two implementations of JSONiq are under development.
jsoniq.org
Available at http://jsoniq.org:
Language Specification
Use Cases
XQ++ BNF
XQ-- BNF