Repository: jsonata-js/jsonata
Pull request #356 (2019-08-26 13:02:07)

Add prettify option to $string() function.
Implements proposal discussed in #334

coveralls: 
[![Coverage Status](https://coveralls.io/builds/25344839/badge)](https://coveralls.io/builds/25344839)

Coverage remained the same at 100.0% when pulling **48252d146533ea70e876d8e07046767ebf86a4d2 on elasticio:add-prettify-option** into **26584f52b52dbea649365b945f7e7e09662f3d37 on jsonata-js:master**.


Patch:
diff --git a/docs/string-functions.md b/docs/string-functions.md
index 06dcd01..50b1142 100644
--- a/docs/string-functions.md
+++ b/docs/string-functions.md
@@ -5,7 +5,7 @@ sidebar_label: String Functions
 ---
 
 ## `$string()`
-__Signature:__ `$string(arg)`
+__Signature:__ `$string(arg, prettify)`
 
 Casts the `arg` parameter to a string using the following casting rules
 
@@ -16,9 +16,11 @@ Casts the `arg` parameter to a string using the following casting rules
 
 If `arg` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `arg`.
 
+If `prettify` is true, then "prettified" JSON is produced. i.e One line per field and lines will be indented based on the field depth.
+
 __Examples__
 
-- `$string(5)` => `"5"`  
+- `$string(5)` => `"5"`
 - `[1..5].$string()` => `["1", "2", "3", "4", "5"]`
 
 ## `$length()`
@@ -35,15 +37,15 @@ __Signature:__ `$substring(str, start[, length])`
 
 Returns a string containing the characters in the first parameter `str` starting at position `start` (zero-offset).  If `str` is not specified (i.e. this function is invoked with only the numeric argument(s)), then the context value is used as the value of `str`.  An error is thrown if `str` is not a string.
 
-If `length` is specified, then the substring will contain maximum `length` characters.  
+If `length` is specified, then the substring will contain maximum `length` characters.
 
 If `start` is negative then it indicates the number of characters from the end of `str`.  See [substr](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr) for full definition.
 
 __Examples__
 
-- `$substring("Hello World", 3)` => `"lo World"`  
-- `$substring("Hello World", 3, 5)` => `"lo Wo"`  
-- `$substring("Hello World", -4)` => `"orld"`  
+- `$substring("Hello World", 3)` => `"lo World"`
+- `$substring("Hello World", 3, 5)` => `"lo Wo"`
+- `$substring("Hello World", -4)` => `"orld"`
 - `$substring("Hello World", -4, 2)` => `"or"`
 
 ## `$substringBefore()`
@@ -122,17 +124,17 @@ The `pattern` parameter can either be a string or a regular expression (regex).
 
 __Examples__
 
-- `$contains("abracadabra", "bra")` => `true`  
-- `$contains("abracadabra", /a.*a/)` => `true`  
-- `$contains("abracadabra", /ar.*a/)` => `false`  
-- `$contains("Hello World", /wo/)` => `false`  
-- `$contains("Hello World", /wo/i)` => `true`  
+- `$contains("abracadabra", "bra")` => `true`
+- `$contains("abracadabra", /a.*a/)` => `true`
+- `$contains("abracadabra", /ar.*a/)` => `false`
+- `$contains("Hello World", /wo/)` => `false`
+- `$contains("Hello World", /wo/i)` => `true`
 - `Phone[$contains(number, /^077/)]` => `{ "type": "mobile", "number": "077 7700 1234" }`
 
 ## `$split()`
 __Signature:__ `$split(str, separator [, limit])`
 
-Splits the `str` parameter into an array of substrings.  If `str` is not specified, then the context value is used as the value of `str`.  It is an error if `str` is not a string.  
+Splits the `str` parameter into an array of substrings.  If `str` is not specified, then the context value is used as the value of `str`.  It is an error if `str` is not a string.
 
 The `separator` parameter can either be a string or a regular expression (regex).  If it is a string, it specifies the characters within `str` about which it should be split.  If it is the empty string, `str` will be split into an array of single characters.  If it is a regex, it splits the string around any sequence of characters that match the regex.
 
@@ -140,8 +142,8 @@ The optional `limit` parameter is a number that specifies the maximum number of
 
 __Examples__
 
-- `$split("so many words", " ")` => `[ "so", "many", "words" ]`  
-- `$split("so many words", " ", 2)` => `[ "so", "many" ]`  
+- `$split("so many words", " ")` => `[ "so", "many", "words" ]`
+- `$split("so many words", " ", 2)` => `[ "so", "many" ]`
 - `$split("too much, punctuation. hard; to read", /[ ,.;]+/)` => `["too", "much", "punctuation", "hard", "to", "read"]`
 
 ## `$join()`
@@ -155,7 +157,7 @@ If `separator` is not specified, then it is assumed to be the empty string, i.e.
 
 __Examples__
 
-- `$join(['a','b','c'])` => `"abc"`  
+- `$join(['a','b','c'])` => `"abc"`
 - `$split("too much, punctuation. hard; to read", /[ ,.;]+/, 3) ~> $join(', ')` => `"too, much, punctuation"`
 
 ## `$match()`
@@ -278,7 +280,7 @@ __Signature:__ `$base64encode()`
 
 Converts an ASCII string to a base 64 representation. Each each character in the string is treated as a byte of binary data. This requires that all characters in the string are in the 0x00 to 0xFF range, which includes all characters in URI encoded strings. Unicode characters outside of that range are not supported.
 
-__Examples__  
+__Examples__
 
 - `$base64encode("myuser:mypass")` => `"bXl1c2VyOm15cGFzcw=="`
 
@@ -288,6 +290,6 @@ __Signature:__ `$base64decode()`
 
 Converts base 64 encoded bytes to a string, using a UTF-8 Unicode codepage.
 
-__Examples__  
+__Examples__
 
 - `$base64decode("bXl1c2VyOm15cGFzcw==")` => `"myuser:mypass"`
diff --git a/src/functions.js b/src/functions.js
index b4754b7..c44a6d7 100644
--- a/src/functions.js
+++ b/src/functions.js
@@ -97,11 +97,12 @@ const functions = (() => {
     }
 
     /**
-     * Stingify arguments
+     * Stringify arguments
      * @param {Object} arg - Arguments
+     * @param {boolean} [prettify] - Pretty print the result
      * @returns {String} String from arguments
      */
-    function string(arg) {
+    function string(arg, prettify = false) {
         // undefined inputs always return undefined
         if (typeof arg === 'undefined') {
             return undefined;
@@ -121,11 +122,13 @@ const functions = (() => {
                 value: arg,
                 stack: (new Error()).stack
             };
-        } else
+        } else {
+            var space = prettify ? 2 : 0;
             str = JSON.stringify(arg, function (key, val) {
                 return (typeof val !== 'undefined' && val !== null && val.toPrecision && isNumeric(val)) ? Number(val.toPrecision(15)) :
                     (val && isFunction(val)) ? '' : val;
-            });
+            }, space);
+        }
         return str;
     }
 
diff --git a/src/jsonata.js b/src/jsonata.js
index 35ba504..3d2e145 100644
--- a/src/jsonata.js
+++ b/src/jsonata.js
@@ -1658,7 +1658,7 @@ var jsonata = (function() {
     staticFrame.bind('max', defineFunction(fn.max, '<a<n>:n>'));
     staticFrame.bind('min', defineFunction(fn.min, '<a<n>:n>'));
     staticFrame.bind('average', defineFunction(fn.average, '<a<n>:n>'));
-    staticFrame.bind('string', defineFunction(fn.string, '<x-:s>'));
+    staticFrame.bind('string', defineFunction(fn.string, '<x-b?:s>'));
     staticFrame.bind('substring', defineFunction(fn.substring, '<s-nn?:s>'));
     staticFrame.bind('substringBefore', defineFunction(fn.substringBefore, '<s-s:s>'));
     staticFrame.bind('substringAfter', defineFunction(fn.substringAfter, '<s-s:s>'));


Test patch:
diff --git a/test/test-suite/groups/function-string/case023.json b/test/test-suite/groups/function-string/case023.json
new file mode 100644
index 0000000..f09ef16
--- /dev/null
+++ b/test/test-suite/groups/function-string/case023.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$string({\"string\": \"hello\"}, false)",
+    "dataset": null,
+    "bindings": {},
+    "result": "{\"string\":\"hello\"}"
+}
diff --git a/test/test-suite/groups/function-string/case024.json b/test/test-suite/groups/function-string/case024.json
new file mode 100644
index 0000000..89a58c8
--- /dev/null
+++ b/test/test-suite/groups/function-string/case024.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$string({\"string\": \"hello\"}, true)",
+    "dataset": null,
+    "bindings": {},
+    "result": "{\n  \"string\": \"hello\"\n}"
+}
diff --git a/test/test-suite/groups/function-string/case025.json b/test/test-suite/groups/function-string/case025.json
new file mode 100644
index 0000000..8a7d514
--- /dev/null
+++ b/test/test-suite/groups/function-string/case025.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$string([\"string\", 5], false)",
+    "dataset": null,
+    "bindings": {},
+    "result": "[\"string\",5]"
+}
diff --git a/test/test-suite/groups/function-string/case026.json b/test/test-suite/groups/function-string/case026.json
new file mode 100644
index 0000000..f0940f3
--- /dev/null
+++ b/test/test-suite/groups/function-string/case026.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$string([\"string\", 5], true)",
+    "dataset": null,
+    "bindings": {},
+    "result": "[\n  \"string\",\n  5\n]"
+}
diff --git a/test/test-suite/groups/function-string/case027.json b/test/test-suite/groups/function-string/case027.json
new file mode 100644
index 0000000..412c009
--- /dev/null
+++ b/test/test-suite/groups/function-string/case027.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$string({  \"string\": \"hello\",  \"number\": 78.8 / 2,  \"null\":null,  \"boolean\": false,  \"function\": $sum,  \"lambda\": function(){true},  \"object\": {    \"str\": \"another\",    \"lambda2\": function($n){$n}  },  \"array\": []}, false)",
+    "dataset": null,
+    "bindings": {},
+    "result": "{\"string\":\"hello\",\"number\":39.4,\"null\":null,\"boolean\":false,\"function\":\"\",\"lambda\":\"\",\"object\":{\"str\":\"another\",\"lambda2\":\"\"},\"array\":[]}"
+}
diff --git a/test/test-suite/groups/function-string/case028.json b/test/test-suite/groups/function-string/case028.json
new file mode 100644
index 0000000..e837494
--- /dev/null
+++ b/test/test-suite/groups/function-string/case028.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$string({  \"string\": \"hello\",  \"number\": 78.8 / 2,  \"null\":null,  \"boolean\": false,  \"function\": $sum,  \"lambda\": function(){true},  \"object\": {    \"str\": \"another\",    \"lambda2\": function($n){$n}  },  \"array\": []}, true)",
+    "dataset": null,
+    "bindings": {},
+    "result": "{\n  \"string\": \"hello\",\n  \"number\": 39.4,\n  \"null\": null,\n  \"boolean\": false,\n  \"function\": \"\",\n  \"lambda\": \"\",\n  \"object\": {\n    \"str\": \"another\",\n    \"lambda2\": \"\"\n  },\n  \"array\": []\n}"
+}