1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-22 19:04:38 +00:00

Compare commits

...

7 Commits

Author SHA1 Message Date
jeremy@jermolene.com
162e4be9f2 Docs updates 2022-05-23 09:21:12 +01:00
jeremy@jermolene.com
1ef9d11ca3 Merge branch 'master' into json-ops 2022-05-23 09:16:44 +01:00
jeremy@jermolene.com
28f1d587b6 Rename JSON operators
See https://github.com/Jermolene/TiddlyWiki5/pull/6522#issuecomment-1111206619
2022-04-27 17:35:45 +01:00
jeremy@jermolene.com
096b30f43b WIP 2022-04-27 17:28:02 +01:00
jeremy@jermolene.com
1d4418a777 Merge branch 'master' into json-ops 2022-04-27 17:27:43 +01:00
jeremy@jermolene.com
093cda65fb Merge branch 'master' into json-ops 2022-04-06 17:37:21 +01:00
jeremy@jermolene.com
5f4dc2a5fe Initial Commit 2022-03-11 10:25:16 +00:00
4 changed files with 434 additions and 0 deletions

View File

@@ -0,0 +1,167 @@
/*\
title: $:/core/modules/filters/json-ops.js
type: application/javascript
module-type: filteroperator
Filter operators for JSON operations
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports["jsonget"] = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var data = options.wiki.getTiddlerDataCached(title);
if(data) {
var item = getDataItemValueAsStrings(data,operator.operands);
if(item !== undefined) {
results.push.apply(results,item);
}
}
});
return results;
};
exports["jsonindexes"] = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var data = options.wiki.getTiddlerDataCached(title);
if(data) {
var item = getDataItemKeysAsStrings(data,operator.operands);
if(item !== undefined) {
results.push.apply(results,item);
}
}
});
return results;
};
exports["jsontype"] = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var data = options.wiki.getTiddlerDataCached(title);
if(data) {
var item = getDataItemType(data,operator.operands);
if(item !== undefined) {
results.push(item);
}
}
});
return results;
};
/*
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
*/
function getDataItemValueAsStrings(data,indexes) {
// Get the item
var item = getDataItem(data,indexes);
// Return the item as a string
return convertDataItemValueToStrings(item);
}
/*
Given a JSON data structure and an array of index strings, return an array of the string representation of the keys of the item at the end of the index chain, or "undefined" if any of the index strings are invalid
*/
function getDataItemKeysAsStrings(data,indexes) {
// Get the item
var item = getDataItem(data,indexes);
// Return the item keys as a string
return convertDataItemKeysToStrings(item);
}
/*
Return an array of the string representation of the values of a data item, or "undefined" if the item is undefined
*/
function convertDataItemValueToStrings(item) {
// Return the item as a string
if(item === undefined) {
return item;
}
if(typeof item === "object") {
if(item === null) {
return ["null"];
}
var results = [];
if($tw.utils.isArray(item)) {
$tw.utils.each(item,function(value) {
results.push.apply(results,convertDataItemValueToStrings(value));
});
return results;
} else {
$tw.utils.each(Object.keys(item).sort(),function(key) {
results.push.apply(results,convertDataItemValueToStrings(item[key]));
});
return results;
}
}
return [item.toString()];
}
/*
Return an array of the string representation of the keys of a data item, or "undefined" if the item is undefined
*/
function convertDataItemKeysToStrings(item) {
// Return the item as a string
if(item === undefined) {
return item;
} else if(typeof item === "object") {
if(item === null) {
return [];
}
var results = [];
if($tw.utils.isArray(item)) {
for(var i=0; i<item.length; i++) {
results.push(i.toString());
}
return results;
} else {
$tw.utils.each(Object.keys(item).sort(),function(key) {
results.push(key);
});
return results;
}
}
return [];
}
function getDataItemType(data,indexes) {
// Get the item
var item = getDataItem(data,indexes);
// Return the item type
if(item === undefined) {
return item;
} else if(item === null) {
return "null";
} else if($tw.utils.isArray(item)) {
return "array";
} else if(typeof item === "object") {
return "object";
} else {
return typeof item;
}
}
/*
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
*/
function getDataItem(data,indexes) {
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return data;
}
// Get the item
var item = data;
for(var i=0; i<indexes.length; i++) {
if(item !== undefined) {
item = item[indexes[i]];
}
}
return item;
}
})();

View File

@@ -0,0 +1,82 @@
/*\
title: test-json-filters.js
type: application/javascript
tags: [[$:/tags/test-spec]]
Tests the JSON filters.
\*/
(function(){
/* jslint node: true, browser: true */
/* eslint-env node, browser, jasmine */
/* eslint no-mixed-spaces-and-tabs: ["error", "smart-tabs"]*/
/* global $tw, require */
"use strict";
describe("json filter tests", function() {
var wiki = new $tw.Wiki();
var tiddlers = [{
title: "First",
text: '{"a":"one","b":"","c":1.618,"d": {"e": "four","f": ["five","six",true,false,null]}}',
type: "application/json"
},{
title: "Second",
text: '["une","deux","trois"]',
type: "application/json"
}];
wiki.addTiddlers(tiddlers);
it("should support the getindex operator", function() {
expect(wiki.filterTiddlers("[[First]getindex[b]]")).toEqual([]);
});
it("should support the jsonget operator", function() {
expect(wiki.filterTiddlers("[[First]jsonget[]]")).toEqual(["one","","1.618","four","five","six","true","false","null"]);
expect(wiki.filterTiddlers("[[First]jsonget[a]]")).toEqual(["one"]);
expect(wiki.filterTiddlers("[[First]jsonget[b]]")).toEqual([""]);
expect(wiki.filterTiddlers("[[First]jsonget[d]]")).toEqual(["four","five","six","true","false","null"]);
expect(wiki.filterTiddlers("[[First]jsonget[d],[e]]")).toEqual(["four"]);
expect(wiki.filterTiddlers("[[First]jsonget[d],[f]]")).toEqual(["five","six","true","false","null"]);
expect(wiki.filterTiddlers("[[First]jsonget[d],[f],[0]]")).toEqual(["five"]);
expect(wiki.filterTiddlers("[[First]jsonget[d],[f],[1]]")).toEqual(["six"]);
expect(wiki.filterTiddlers("[[First]jsonget[d],[f],[2]]")).toEqual(["true"]);
expect(wiki.filterTiddlers("[[First]jsonget[d],[f],[3]]")).toEqual(["false"]);
expect(wiki.filterTiddlers("[[First]jsonget[d],[f],[4]]")).toEqual(["null"]);
});
it("should support the jsonindexes operator", function() {
expect(wiki.filterTiddlers("[[Second]jsonindexes[]]")).toEqual(["0","1","2"]);
expect(wiki.filterTiddlers("[[First]jsonindexes[]]")).toEqual(["a","b","c","d"]);
expect(wiki.filterTiddlers("[[First]jsonindexes[a]]")).toEqual([]);
expect(wiki.filterTiddlers("[[First]jsonindexes[b]]")).toEqual([]);
expect(wiki.filterTiddlers("[[First]jsonindexes[d]]")).toEqual(["e","f"]);
expect(wiki.filterTiddlers("[[First]jsonindexes[d],[e]]")).toEqual([]);
expect(wiki.filterTiddlers("[[First]jsonindexes[d],[f]]")).toEqual(["0","1","2","3","4"]);
expect(wiki.filterTiddlers("[[First]jsonindexes[d],[f],[0]]")).toEqual([]);
expect(wiki.filterTiddlers("[[First]jsonindexes[d],[f],[1]]")).toEqual([]);
expect(wiki.filterTiddlers("[[First]jsonindexes[d],[f],[2]]")).toEqual([]);
expect(wiki.filterTiddlers("[[First]jsonindexes[d],[f],[3]]")).toEqual([]);
expect(wiki.filterTiddlers("[[First]jsonindexes[d],[f],[4]]")).toEqual([]);
});
it("should support the jsontype operator", function() {
expect(wiki.filterTiddlers("[[First]jsontype[]]")).toEqual(["object"]);
expect(wiki.filterTiddlers("[[First]jsontype[a]]")).toEqual(["string"]);
expect(wiki.filterTiddlers("[[First]jsontype[b]]")).toEqual(["string"]);
expect(wiki.filterTiddlers("[[First]jsontype[c]]")).toEqual(["number"]);
expect(wiki.filterTiddlers("[[First]jsontype[d]]")).toEqual(["object"]);
expect(wiki.filterTiddlers("[[First]jsontype[d],[e]]")).toEqual(["string"]);
expect(wiki.filterTiddlers("[[First]jsontype[d],[f]]")).toEqual(["array"]);
expect(wiki.filterTiddlers("[[First]jsontype[d],[f],[0]]")).toEqual(["string"]);
expect(wiki.filterTiddlers("[[First]jsontype[d],[f],[1]]")).toEqual(["string"]);
expect(wiki.filterTiddlers("[[First]jsontype[d],[f],[2]]")).toEqual(["boolean"]);
expect(wiki.filterTiddlers("[[First]jsontype[d],[f],[3]]")).toEqual(["boolean"]);
expect(wiki.filterTiddlers("[[First]jsontype[d],[f],[4]]")).toEqual(["null"]);
});
});
})();

View File

@@ -0,0 +1,87 @@
created: 20220406163813574
modified: 20220408112300157
tags: Features
title: JSON in TiddlyWiki
type: text/vnd.tiddlywiki
!! Introduction
JSON (~JavaScript Object Notation) is a standardised text representation for data structures that is widely used for the storage and transfer of data.
JSON is used in several different contexts in TiddlyWiki. For example:
* Tiddlers are represented as JSON data within TiddlyWiki HTML files
* Groups of tiddlers can be [[exported|How to export tiddlers]] and [[imported|Importing Tiddlers]] as JSON files
* Plugin tiddlers store their constituent shadow tiddlers as JSON data
* The client-server configuration uses JSON messages to communicate between the client and the server
* Arbitrary JSON data within DataTiddlers can be processed and manipulated using a set of filter operators and action widgets
!! About JSON
The technical description of JSON at the official website https://json.org/ is terse. Here we summarise the main features.
JSON supports two basic data structures:
''Arrays'' are lists of items. The items are identified by their numeric index (starting at zero)
An example of an array is:
```
["one","two","three\"four"]
```
Note the following features of arrays:
* The array is signified by square brackets surrounding the list of items
* Each item is a string in double quotes. Double quotes can be included within the strings by preceding them with a backslash (`\`)
* The items are separated by commas
''Objects'' are collections of name/value pairs. Each item is a value that is identified by a unique name
An example of an object is:
```
{
"first": "This is the first value",
"second": "This is the second value",
"third": "This is the third value"
}
```
Note the following features of objects:
* The object is signified by curly braces surrounding the list of name/value pairs
* Each name/value pair consists of the name in double quotes, a colon, and then the value
* The name/value pairs are separated by commas
The examples above all show string values. JSON actually supports several different types of value. Any of these types can be used as a value:
* String values, as shown above
* Numeric values, represented as signed decimals such as `1`, `3.14`. Exponential notation can also be used e.g. `-1E10`
* Boolean values, represented by the keywords `true` and `false`
* The special value `null`, which is often used to represent data that is missing or incomplete
* Objects and arrays are also values, allowing complex nested structures to be represented
!! Examples
```
```
!! Working with Data Tiddlers
* Reading data from JSON tiddlers
* Constructing JSON tiddlers
* Modifying JSON tiddlers
[[jsonstringify Operator]]
[[jsontiddler Macro]]
[[jsontiddlers Macro]]
JSONTiddlerWidget
[[JSONTiddlers]]
[[TiddlyWeb JSON tiddler format]]

View File

@@ -6,6 +6,33 @@ type: text/vnd.tiddlywiki
See [[JSON in TiddlyWiki]] for an overview of using JSON in TiddlyWiki.
!! Introduction
The following examples assume the following JSON data structure is stored in a data tiddler called "foobar" with the type `application/json`:
```json
{
"a": "one",
"b": "",
"c": "three",
"d": {
"e": "four",
"f": [
"five",
"six",
true,
false,
null
],
"g": {
"x": "max",
"y": "may",
"z": "maize"
}
}
}
```
!! Text References for Accessing JSON Data
[[Text references|TextReference]] are a simple shortcut syntax to look up the value of a named property. For example, if a [[DictionaryTiddler|DictionaryTiddlers]] called `MonthDays` contains:
@@ -23,3 +50,74 @@ The same is true if `MonthDays` is a [[JSONTiddler|JSONTiddlers]] with the follo
```
{"oct":31,"nov":30,"dec":31}
```
!! Operators for Reading JSON Data
Values are identified by a sequence of indexes. For example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`.
The new operators use multiple operands to specify the indexes:
```
[[foobar]jsonget[a]] --> "one"
[[foobar]jsonget[d],[e]] --> "four"
[[foobar]jsonget[d],[f],[0]] --> "five"
```
Indexes can be dynamically composed from variables and transclusions:
```
[[foobar]jsonget<variable>,{!!field},[0]]
```
Boolean values and null are returned as normal strings. The [[jsontype Operator]] returns a string identifying the original type. Thus:
```
[[foobar]jsontype[a]] --> "string"
[[foobar]jsontype[d]] --> "object"
[[foobar]jsontype[d],[f]] --> "array"
[[foobar]jsontype[d],[f],[2]] --> "boolean"
```
Using the [[jsonget Operator]] to retrieve an object or an array returns a list of all the values. For example:
```
[[foobar]jsonget[d],[f]] --> "five","six","true","false","null"
[[foobar]jsonget[d],[g]] --> "max", "may", "maize"
```
The [[jsonindexes Operator]] retrieves the corresponding indexes:
```
[[foobar]jsonindexes[d],[f]] --> "0", "1", "2", "3", "4"
[[foobar]jsonindexes[d],[g]] --> "x", "y", "z"
```
The behaviour when retrieving an object or array that contains values that are themselves objects or arrays may be confusing: the object is read recursively as a list of values. For example:
```
[[foobar]jsonget[d]] --> "four", "five", "six", "true", "false", "null", "max", "may", "maize"
```
A further subtlety is that the special case of a single blank operand is used to identify the root object. Thus:
```
[[foobar]jsonindexes[]] --> "a", "b", "c", "d"
```
An example of using a list widget to iterate through the properties of an array within a JSON object:
```
<$list filter="[[foobar]jsonindexes[d][f]]">
<div>
<$text text=<<currentTiddler>>/>: <$text text={{{ [[foobar]jsonget[d][f]<currentTiddler>] }}}/>
</div>
</$list>
Prints:
0: five
1: six
2: true
3: false
4: null
```