1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-01-15 19:55:40 +00:00

Tweak semantics of JSON operators to match #6932

This allows us to later bring in the optimisations without breaking backwards compatibility.
This commit is contained in:
jeremy@jermolene.com 2022-09-02 17:14:50 +01:00
parent 54e1083dd6
commit d852123f61
7 changed files with 27 additions and 131 deletions

View File

@ -15,25 +15,11 @@ Filter operators for JSON operations
exports["jsonget"] = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,{});
var data = $tw.utils.parseJSONSafe(title,title);
if(data) {
var item = getDataItemValueAsStrings(data,operator.operands);
var item = getDataItemValueAsString(data,operator.operands);
if(item !== undefined) {
results.push.apply(results,item);
}
}
});
return results;
};
exports["jsonextract"] = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,{});
if(data) {
var item = getDataItem(data,operator.operands);
if(item !== undefined) {
results.push(JSON.stringify(item));
results.push(item);
}
}
});
@ -43,7 +29,7 @@ exports["jsonextract"] = function(source,operator,options) {
exports["jsonindexes"] = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,{});
var data = $tw.utils.parseJSONSafe(title,title);
if(data) {
var item = getDataItemKeysAsStrings(data,operator.operands);
if(item !== undefined) {
@ -57,7 +43,7 @@ exports["jsonindexes"] = function(source,operator,options) {
exports["jsontype"] = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,{});
var data = $tw.utils.parseJSONSafe(title,title);
if(data) {
var item = getDataItemType(data,operator.operands);
if(item !== undefined) {
@ -71,11 +57,11 @@ exports["jsontype"] = function(source,operator,options) {
/*
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) {
function getDataItemValueAsString(data,indexes) {
// Get the item
var item = getDataItem(data,indexes);
// Return the item as a string
return convertDataItemValueToStrings(item);
return convertDataItemValueToString(item);
}
/*
@ -91,29 +77,15 @@ function getDataItemKeysAsStrings(data,indexes) {
/*
Return an array of the string representation of the values of a data item, or "undefined" if the item is undefined
*/
function convertDataItemValueToStrings(item) {
function convertDataItemValueToString(item) {
// Return the item as a string
if(item === undefined) {
return item;
}
if(typeof item === "object") {
if(item === null) {
return ["null"];
return JSON.stringify(item);
}
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 item.toString();
}
/*

View File

@ -25,6 +25,10 @@ describe("json filter tests", function() {
title: "Second",
text: '["une","deux","trois"]',
type: "application/json"
},{
title: "Third",
text: "This is not JSON",
type: "text/vnd.tiddlywiki"
}];
wiki.addTiddlers(tiddlers);
@ -33,13 +37,15 @@ describe("json filter tests", function() {
});
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("[{Third}jsonget[]]")).toEqual(["This is not JSON"]);
expect(wiki.filterTiddlers("[{First}jsonget[]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["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[missing-property]]")).toEqual([]);
expect(wiki.filterTiddlers("[{First}jsonget[d]]")).toEqual(["four","five","six","true","false","null"]);
expect(wiki.filterTiddlers("[{First}jsonget[d]]")).toEqual(['{"e":"four","f":["five","six",true,false,null]}']);
expect(wiki.filterTiddlers("[{First}jsonget[d]jsonget[f]]")).toEqual(['["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]]")).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"]);
@ -47,21 +53,6 @@ describe("json filter tests", function() {
expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[4]]")).toEqual(["null"]);
});
it("should support the jsonextract operator", function() {
expect(wiki.filterTiddlers("[{First}jsonextract[]]")).toEqual([`{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}`]);
expect(wiki.filterTiddlers("[{First}jsonextract[a]]")).toEqual([`"one"`]);
expect(wiki.filterTiddlers("[{First}jsonextract[b]]")).toEqual([`""`]);
expect(wiki.filterTiddlers("[{First}jsonextract[d]]")).toEqual([`{"e":"four","f":["five","six",true,false,null]}`]);
expect(wiki.filterTiddlers("[{First}jsonextract[missing-property]]")).toEqual([]);
expect(wiki.filterTiddlers("[{First}jsonextract[d],[e]]")).toEqual([`"four"`]);
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f]]")).toEqual([`["five","six",true,false,null]`]);
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[0]]")).toEqual([`"five"`]);
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[1]]")).toEqual([`"six"`]);
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[2]]")).toEqual([`true`]);
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[3]]")).toEqual([`false`]);
expect(wiki.filterTiddlers("[{First}jsonextract[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"]);
@ -78,6 +69,7 @@ describe("json filter tests", function() {
});
it("should support the jsontype operator", function() {
expect(wiki.filterTiddlers("[{Third}jsontype[]]")).toEqual(["string"]);
expect(wiki.filterTiddlers("[{First}jsontype[]]")).toEqual(["object"]);
expect(wiki.filterTiddlers("[{First}jsontype[a]]")).toEqual(["string"]);
expect(wiki.filterTiddlers("[{First}jsontype[b]]")).toEqual(["string"]);
@ -92,7 +84,7 @@ describe("json filter tests", function() {
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[4]]")).toEqual(["null"]);
});
it("should support the jsontype operator", function() {
it("should support the format:json operator", function() {
expect(wiki.filterTiddlers("[{First}format:json[]]")).toEqual(["{\"a\":\"one\",\"b\":\"\",\"c\":1.618,\"d\":{\"e\":\"four\",\"f\":[\"five\",\"six\",true,false,null]}}"]);
expect(wiki.filterTiddlers("[{First}format:json[4]]")).toEqual(["{\n \"a\": \"one\",\n \"b\": \"\",\n \"c\": 1.618,\n \"d\": {\n \"e\": \"four\",\n \"f\": [\n \"five\",\n \"six\",\n true,\n false,\n null\n ]\n }\n}"]);
expect(wiki.filterTiddlers("[{First}format:json[ ]]")).toEqual(["{\n \"a\": \"one\",\n \"b\": \"\",\n \"c\": 1.618,\n \"d\": {\n \"e\": \"four\",\n \"f\": [\n \"five\",\n \"six\",\n true,\n false,\n null\n ]\n }\n}"]);
@ -101,4 +93,3 @@ describe("json filter tests", function() {
});
})();

View File

@ -1,58 +0,0 @@
created: 20220611104737314
modified: 20220611104737314
tags: [[Filter Operators]] [[JSON Operators]]
title: jsonextract Operator
caption: jsonextract
op-purpose: retrieve the JSON value of a property from JSON strings
op-input: a selection of JSON strings
op-parameter: one or more indexes of the property to retrieve
op-output: the JSON values of each of the retrieved properties
<<.from-version "5.3.0">> See [[JSON in TiddlyWiki]] for background.
The <<.op jsonextract>> operator is used to extract a smaller JSON object from within a larger one. It returns the properties from JSON data as a JSON string. See also the following related operators:
* <<.olink jsonget>> to retrieve the values of a property in JSON data
* <<.olink jsontype>> to retrieve the type of a JSON value
* <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array
Properties within a JSON object are identified by a sequence of indexes. In the following example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`.
```
{
"a": "one",
"b": "",
"c": "three",
"d": {
"e": "four",
"f": [
"five",
"six",
true,
false,
null
],
"g": {
"x": "max",
"y": "may",
"z": "maize"
}
}
}
```
The following examples assume that this JSON data is contained in a variable called `jsondata`.
The <<.op jsonextract>> operator uses multiple operands to specify the indexes of the property to retrieve:
```
[<jsondata>jsonextract[a]] --> "one"
[<jsondata>jsonextract[d],[f]] --> ["five","six",true,false,null]
[<jsondata>jsonextract[d],[g]] --> {"x": "max","y": "may","z": "maize"}
```
Indexes can be dynamically composed from variables and transclusions:
```
[<jsondata>jsonextract<variable>,{!!field},[0]]
```

View File

@ -12,7 +12,6 @@ op-output: the values of each of the retrieved properties
The <<.op jsonget>> operator is used to retrieve values from JSON data. See also the following related operators:
* <<.olink jsonextract>> to extract a subtree of JSON data
* <<.olink jsontype>> to retrieve the type of a JSON value
* <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array
@ -66,11 +65,11 @@ Boolean values and null are returned as normal strings. The <<.olink jsontype>>
[<jsondata>jsontype[d],[f],[2]] --> "boolean"
```
Using the <<.op jsonget>> operator to retrieve an object or an array returns a list of all the values. For example:
Using the <<.op jsonget>> operator to retrieve an object or an array returns a JSON string of the values. For example:
```
[<jsondata>jsonget[d],[f]] --> "five","six","true","false","null"
[<jsondata>jsonget[d],[g]] --> "max", "may", "maize"
[<jsondata>jsonget[d],[f]] --> `["five","six",true,false,null]`
[<jsondata>jsonget[d],[g]] --> `{"x": "max","y": "may","z": "maize"}`
```
The <<.olink jsonindexes>> operator retrieves the corresponding indexes:
@ -80,12 +79,6 @@ The <<.olink jsonindexes>> operator retrieves the corresponding indexes:
[<jsondata>jsonindexes[d],[g]] --> "x", "y", "z"
```
The behaviour when retrieving an object or array that contains values that are themselves objects or arrays is to recursively retrieve all the values:
```
[<jsondata>jsonget[d]] --> "four", "five", "six", "true", "false", "null", "max", "may", "maize"
```
A subtlety is that the special case of a single blank operand is used to identify the root object. Thus:
```

View File

@ -13,7 +13,6 @@ op-output: the values of each of the retrieved properties
The <<.op jsonindexes>> operator is used to retrieve the property names of JSON objects or the index names of JSON arrays. See also the following related operators:
* <<.olink jsonget>> to retrieve the values of a property in JSON data
* <<.olink jsonextract>> to extract a subtree of JSON data
* <<.olink jsontype>> to retrieve the type of a JSON value
Properties within a JSON object are identified by a sequence of indexes. In the following example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`.

View File

@ -13,7 +13,6 @@ op-output: the types of each of the retrieved properties
The <<.op jsontype>> operator is used to retrieve the type of a property in JSON data. See also the following related operators:
* <<.olink jsonget>> to retrieve the values of a property in JSON data
* <<.olink jsonextract>> to extract a subtree of JSON data
* <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array
JSON supports the following data types:

View File

@ -59,7 +59,7 @@ caption: parse tree
</td>
<td>
<$let type={{{ [<node>jsonget[attributes],<index>,[type]] }}}>
<$transclude $variable={{{ [<type>match[string]then[preview-node-attribute-string]] :else[<type>match[indirect]then[preview-node-attribute-indirect]] :else[<type>match[macro]then[preview-node-attribute-macro]] }}} attribute={{{ [<node>jsonextract[attributes],<index>] }}}/>
<$transclude $variable={{{ [<type>match[string]then[preview-node-attribute-string]] :else[<type>match[indirect]then[preview-node-attribute-indirect]] :else[<type>match[macro]then[preview-node-attribute-macro]] }}} attribute={{{ [<node>jsonget[attributes],<index>] }}}/>
</$let>
</td>
</tr>
@ -73,7 +73,7 @@ caption: parse tree
<div style="padding:4px 4px 0 4px;">
<$transclude $variable="preview-node-properties" node=<<node>>/>
<$transclude $variable="preview-node-attributes" node=<<node>>/>
<$transclude $variable="preview-node-list" nodeList={{{ [<node>jsonextract[children]] }}}/>
<$transclude $variable="preview-node-list" nodeList={{{ [<node>jsonget[children]] }}}/>
</div>
\end
@ -111,7 +111,7 @@ caption: parse tree
\procedure preview-node-list(nodeList)
<$list filter="[<nodeList>jsonindexes[]]" variable="index">
<$transclude $variable="preview-node" node={{{ [<nodeList>jsonextract<index>] }}}/>
<$transclude $variable="preview-node" node={{{ [<nodeList>jsonget<index>] }}}/>
</$list>
\end