Fixes for JSON operators (#7105)

This commit is contained in:
Jeremy Ruston 2022-12-09 18:31:23 +00:00 committed by GitHub
parent 1118de319e
commit 0ce5788747
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 161 additions and 27 deletions

View File

@ -17,9 +17,23 @@ exports["jsonget"] = function(source,operator,options) {
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,title);
if(data) {
var item = getDataItemValueAsString(data,operator.operands);
var items = getDataItemValueAsStrings(data,operator.operands);
if(items !== undefined) {
results.push.apply(results,items);
}
}
});
return results;
};
exports["jsonextract"] = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,title);
if(data) {
var item = getDataItem(data,operator.operands);
if(item !== undefined) {
results.push(item);
results.push(JSON.stringify(item));
}
}
});
@ -31,9 +45,9 @@ exports["jsonindexes"] = function(source,operator,options) {
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,title);
if(data) {
var item = getDataItemKeysAsStrings(data,operator.operands);
if(item !== undefined) {
results.push.apply(results,item);
var items = getDataItemKeysAsStrings(data,operator.operands);
if(items !== undefined) {
results.push.apply(results,items);
}
}
});
@ -57,11 +71,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 getDataItemValueAsString(data,indexes) {
function getDataItemValueAsStrings(data,indexes) {
// Get the item
var item = getDataItem(data,indexes);
// Return the item as a string
return convertDataItemValueToString(item);
// Return the item as a string list
return convertDataItemValueToStrings(item);
}
/*
@ -77,15 +91,34 @@ 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 convertDataItemValueToString(item) {
function convertDataItemValueToStrings(item) {
// Return the item as a string
if(item === undefined) {
return item;
return undefined;
} else if(item === null) {
return ["null"]
} else if(typeof item === "object") {
var results = [],i,t;
if($tw.utils.isArray(item)) {
// Return all the items in arrays recursively
for(i=0; i<item.length; i++) {
t = convertDataItemValueToStrings(item[i])
if(t !== undefined) {
results.push.apply(results,t);
}
}
} else {
// Return all the values in objects recursively
$tw.utils.each(Object.keys(item).sort(),function(key) {
t = convertDataItemValueToStrings(item[key]);
if(t !== undefined) {
results.push.apply(results,t);
}
});
}
return results;
}
if(typeof item === "object") {
return JSON.stringify(item);
}
return item.toString();
return [item.toString()];
}
/*

View File

@ -19,7 +19,7 @@ New [ext[Twitter Archivist|./editions/twitter-archivist]] plugin to import the t
<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/6961">> new GenesisWidget that allows the dynamic construction of another widget, where the name and attributes of the new widget can be dynamically determined, without needing to be known in advance
<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/6936">> new operators for reading and formatting JSON data: [[jsonget Operator]], [[jsonindexes Operator]], [[jsontype Operator]] and [[format Operator]]
<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/6936">> new operators for reading and formatting JSON data: [[jsonget Operator]], [[jsonindexes Operator]], [[jsontype Operator]], [[jsonextract Operator]] and [[format Operator]]
! Translation improvement

View File

@ -23,7 +23,7 @@ describe("json filter tests", function() {
type: "application/json"
},{
title: "Second",
text: '["une","deux","trois"]',
text: '["une","deux","trois",["quatre","cinq"]]',
type: "application/json"
},{
title: "Third",
@ -38,14 +38,14 @@ describe("json filter tests", function() {
it("should support the jsonget operator", function() {
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("[{Second}jsonget[]]")).toEqual(["une","deux","trois","quatre","cinq"]);
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[missing-property]]")).toEqual([]);
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]]")).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]]")).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"]);
@ -53,8 +53,25 @@ 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("[{Third}jsonextract[]]")).toEqual(['"This is not JSON"']);
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[missing-property]]")).toEqual([]);
expect(wiki.filterTiddlers("[{First}jsonextract[d]]")).toEqual(['{"e":"four","f":["five","six",true,false,null]}']);
expect(wiki.filterTiddlers("[{First}jsonextract[d]jsonextract[f]]")).toEqual(['["five","six",true,false,null]']);
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("[{Second}jsonindexes[]]")).toEqual(["0","1","2","3"]);
expect(wiki.filterTiddlers("[{First}jsonindexes[]]")).toEqual(["a","b","c","d"]);
expect(wiki.filterTiddlers("[{First}jsonindexes[a]]")).toEqual([]);
expect(wiki.filterTiddlers("[{First}jsonindexes[b]]")).toEqual([]);

View File

@ -2,7 +2,7 @@ title: JSON in TiddlyWiki
tags: Features
type: text/vnd.tiddlywiki
created: 20220427174702859
modified: 20220427174702859
modified: 20220611104737314
!! Introduction

View File

@ -0,0 +1,66 @@
created: 20220611104737314
modified: 20220611104737314
tags: [[Filter Operators]] [[JSON Operators]]
title: jsonextract Operator
caption: jsonextract
op-purpose: retrieve the JSON string 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 string values of each of the retrieved properties
<<.from-version "5.2.4">> See [[JSON in TiddlyWiki]] for background.
The <<.op jsonextract>> operator is used to retrieve values from JSON data as JSON substrings. 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. Values are returned as literal JSON strings:
```
[<jsondata>jsonextract[a]] --> "one"
[<jsondata>jsonextract[d],[e]] --> "four"
[<jsondata>jsonextract[d],[f],[0]] --> "five"
[<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]]
```
A subtlety is that the special case of a single blank operand is used to identify the root object. Thus:
```
[<jsondata>jsonextract[]] --> {"a":"one","b":"","c":"three","d":{"e":"four","f":["five","six",true,false,null],"g":{"x":"max","y":"may","z":"maize"}}}
```

View File

@ -10,10 +10,11 @@ op-output: the values of each of the retrieved properties
<<.from-version "5.2.4">> See [[JSON in TiddlyWiki]] for background.
The <<.op jsonget>> operator is used to retrieve values from JSON data. See also the following related operators:
The <<.op jsonget>> operator is used to retrieve values from JSON data as strings. See also the following related operators:
* <<.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
* <<.olink jsonextract>> to retrieve a JSON value as a string of JSON
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`.
@ -65,11 +66,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 JSON string of the values. For example:
Using the <<.op jsonget>> operator to retrieve an object or an array returns a list of the values. For example:
```
[<jsondata>jsonget[d],[f]] --> `["five","six",true,false,null]`
[<jsondata>jsonget[d],[g]] --> `{"x": "max","y": "may","z": "maize"}`
[<jsondata>jsonget[d],[f]] --> "five","six","true","false","null"
[<jsondata>jsonget[d],[g]] --> "max","may","maize"
```
The <<.olink jsonindexes>> operator retrieves the corresponding indexes:
@ -79,6 +80,12 @@ The <<.olink jsonindexes>> operator retrieves the corresponding indexes:
[<jsondata>jsonindexes[d],[g]] --> "x", "y", "z"
```
If the object or array contains nested child objects or arrays then the values are retrieved recursively and returned flattened into a list. For example:
```
[<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

@ -14,6 +14,7 @@ The <<.op jsonindexes>> operator is used to retrieve the property names of JSON
* <<.olink jsonget>> to retrieve the values of a property in JSON data
* <<.olink jsontype>> to retrieve the type of a JSON value
* <<.olink jsonextract>> to retrieve a JSON value as a string of JSON
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

@ -14,6 +14,7 @@ The <<.op jsontype>> operator is used to retrieve the type of a property in JSON
* <<.olink jsonget>> to retrieve the values of a property in JSON data
* <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array
* <<.olink jsonextract>> to retrieve a JSON value as a string of JSON
JSON supports the following data types:

View File

@ -1,11 +1,20 @@
created: 20220427174702859
modified: 20220427171449102
modified: 20220611104737314
tags: [[JSON in TiddlyWiki]] Learning
title: Reading data from JSON tiddlers
type: text/vnd.tiddlywiki
See [[JSON in TiddlyWiki]] for an overview of using JSON in TiddlyWiki.
!! Filter Operators for Accessing JSON Data
The following filter operators allow values to be read from JSON data:
* <<.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
* <<.olink jsonextract>> to retrieve a JSON value as a string of JSON
!! 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: