changeset 28615:5da49e37a6c9

New functions jsondecode and jsonencode (bug #53100). * NEWS: Add to list of new functions. * configure.ac: Look for RapidJSON library. * libinterp/corefcn/jsondecode.cc: New function. * libinterp/corefcn/jsonencode.cc: New function. * libinterp/corefcn/module.mk: Add new functions to build system. * scripts/help/__unimplemented__.m: Remove the function. * test/json/jsondecodetest.tst: New test files. * test/json/jsonencodetest.tst: New test files. * test/json/module.mk: Add new functions to build system. * test/module.mk: Add new functions to build system. This is the result of GSoC 2020 by Abdallah Elshamy.
author Abdallah Elshamy <abdallah.k.elshamy@gmail.com>
date Thu, 13 Aug 2020 23:57:07 +0900
parents 652a675c0916
children cd7bbca7eae3
files NEWS configure.ac libinterp/corefcn/jsondecode.cc libinterp/corefcn/jsonencode.cc libinterp/corefcn/module.mk scripts/help/__unimplemented__.m test/json/jsondecodetest.tst test/json/jsonencodetest.tst test/json/module.mk test/module.mk
diffstat 10 files changed, 2320 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Mon Aug 03 11:13:26 2020 -0400
+++ b/NEWS	Thu Aug 13 23:57:07 2020 +0900
@@ -47,6 +47,9 @@
 and if a function uses varargout then the check is skipped for function
 outputs.
 
+- As part of GSoC 2020, Abdallah K. Elshamy implemented the 
+`jsondecode` and `jsonencode` functions to read and write JSON data.
+
 - The function `griddata` now implements the "v4" Biharmonic Spline
 Interpolation method.  In adddition, the function now accepts 3-D inputs
 by passing the data to `griddata3`.
@@ -133,6 +136,8 @@
 
 * `getpixelposition`
 * `endsWith`
+* `jsondecode`
+* `jsonencode`
 * `listfonts`
 * `memory`
 * `rng`
--- a/configure.ac	Mon Aug 03 11:13:26 2020 -0400
+++ b/configure.ac	Thu Aug 13 23:57:07 2020 +0900
@@ -1331,6 +1331,22 @@
   ],
   [libpcre], [REQUIRED])
 
+### Check for RapidJSON header only library.
+
+AC_LANG_PUSH([C++])
+AC_CHECK_HEADER([rapidjson/rapidjson.h], [have_rapidjson=yes], [have_rapidjson=no
+  rapid_json_warning="RapidJSON library not found.  Octave will not be able to read or write JSON files."])
+if test "$have_rapidjson" = yes; then
+  AC_CHECK_HEADER([rapidjson/cursorstreamwrapper.h], [have_rapidjson=yes], [have_rapidjson=no
+    rapid_json_warning="RapidJSON 1.1.0 or older found, but latest development version needed.  Octave will not be able to read or write JSON files."])
+fi
+if test "$have_rapidjson" = yes; then
+  AC_DEFINE(HAVE_RAPIDJSON, 1, [Define to 1 if RapidJSON is available.])
+else
+  OCTAVE_CONFIGURE_WARNING([rapid_json_warning])
+fi
+AC_LANG_POP([C++])
+
 ### Check for readline library.
 
 OCTAVE_ENABLE_READLINE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/jsondecode.cc	Thu Aug 13 23:57:07 2020 +0900
@@ -0,0 +1,572 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "defun.h"
+#include "error.h"
+#include "errwarn.h"
+#include "ovl.h"
+#include "parse.h"
+
+#if defined (HAVE_RAPIDJSON)
+#  include <rapidjson/document.h>
+#  include <rapidjson/error/en.h>
+#endif
+
+#if defined (HAVE_RAPIDJSON)
+
+octave_value
+decode (const rapidjson::Value& val, const octave_value_list& options);
+
+//! Checks if two instances of @ref string_vector are equal.
+//!
+//! @param a The first @ref string_vector.
+//! @param b The second @ref string_vector.
+//!
+//! @return @c bool that indicates if they are equal.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! string_vector a ({"foo", "bar"});
+//! string_vector b ({"foo", "baz"});
+//! bool is_equal = equals (a, b);
+//! @endcode
+
+bool
+equals (const string_vector& a, const string_vector& b)
+{
+  // FIXME: move to string_vector class
+  octave_idx_type n = a.numel ();
+  if (n != b.numel ())
+    return false;
+  for (octave_idx_type i = 0; i < n; ++i)
+    if (a(i) != b(i))
+      return false;
+
+  return true;
+}
+
+//! Decodes a numerical JSON value into a scalar number.
+//!
+//! @param val JSON value that is guaranteed to be a numerical value.
+//!
+//! @return @ref octave_value that contains the numerical value of @p val.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("123");
+//! octave_value num = decode_number (d);
+//! @endcode
+
+octave_value
+decode_number (const rapidjson::Value& val)
+{
+  if (val.IsUint ())
+    return octave_value (val.GetUint ());
+  else if (val.IsInt ())
+    return octave_value (val.GetInt ());
+  else if (val.IsUint64 ())
+    return octave_value (val.GetUint64 ());
+  else if (val.IsInt64 ())
+    return octave_value (val.GetInt64 ());
+  else if (val.IsDouble ())
+    return octave_value (val.GetDouble ());
+  else
+    error ("jsondecode.cc: Unidentified type.");
+}
+
+//! Decodes a JSON object into a scalar struct.
+//!
+//! @param val JSON value that is guaranteed to be a JSON object.
+//! @param options @c ReplacementStyle and @c Prefix options with their values.
+//!
+//! @return @ref octave_value that contains the equivalent scalar struct of @p val.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("{\"a\": 1, \"b\": 2}");
+//! octave_value struct = decode_object (d, octave_value_list ());
+//! @endcode
+
+octave_value
+decode_object (const rapidjson::Value& val, const octave_value_list& options)
+{
+  octave_scalar_map retval;
+  for (const auto& pair : val.GetObject ())
+    {
+      std::string fcn_name = "matlab.lang.makeValidName";
+      octave_value_list args = octave_value_list (pair.name.GetString ());
+      args.append (options);
+      std::string validName = octave::feval (fcn_name,args)(0).string_value ();
+      retval.assign (validName, decode (pair.value, options));
+    }
+  return octave_value (retval);
+}
+
+//! Decodes a JSON array that contains only numerical or null values
+//! into an NDArray.
+//!
+//! @param val JSON value that is guaranteed to be a numeric array.
+//!
+//! @return @ref octave_value that contains the equivalent NDArray of @p val.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[1, 2, 3, 4]");
+//! octave_value numeric_array = decode_numeric_array (d);
+//! @endcode
+
+octave_value
+decode_numeric_array (const rapidjson::Value& val)
+{
+  NDArray retval (dim_vector (val.Size (), 1));
+  octave_idx_type index = 0;
+  for (const auto& elem : val.GetArray ())
+    retval(index++) = elem.IsNull () ? octave_NaN
+                                     : decode_number (elem).double_value ();
+  return retval;
+}
+
+//! Decodes a JSON array that contains only boolean values into a boolNDArray.
+//!
+//! @param val JSON value that is guaranteed to be a boolean array.
+//!
+//! @return @ref octave_value that contains the equivalent boolNDArray of @p val.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[true, false, true]");
+//! octave_value boolean_array = decode_boolean_array (d);
+//! @endcode
+
+octave_value
+decode_boolean_array (const rapidjson::Value& val)
+{
+  boolNDArray retval (dim_vector (val.Size (), 1));
+  octave_idx_type index = 0;
+  for (const auto& elem : val.GetArray ())
+    retval(index++) = elem.GetBool ();
+  return retval;
+}
+
+//! Decodes a JSON array that contains different types
+//! or string values only into a Cell.
+//!
+//! @param val JSON value that is guaranteed to be a mixed or string array.
+//! @param options @c ReplacementStyle and @c Prefix options with their values.
+//!
+//! @return @ref octave_value that contains the equivalent Cell of @p val.
+//!
+//! @b Example (decoding a string array):
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[\"foo\", \"bar\", \"baz\"]");
+//! octave_value cell = decode_string_and_mixed_array (d, octave_value_list ());
+//! @endcode
+//!
+//! @b Example (decoding a mixed array):
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[\"foo\", 123, true]");
+//! octave_value cell = decode_string_and_mixed_array (d, octave_value_list ());
+//! @endcode
+
+octave_value
+decode_string_and_mixed_array (const rapidjson::Value& val,
+                               const octave_value_list& options)
+{
+  Cell retval (dim_vector (val.Size (), 1));
+  octave_idx_type index = 0;
+  for (const auto& elem : val.GetArray ())
+    retval(index++) = decode (elem, options);
+  return retval;
+}
+
+//! Decodes a JSON array that contains only objects into a Cell or a struct array
+//! depending on the similarity of the objects' keys.
+//!
+//! @param val JSON value that is guaranteed to be an object array.
+//! @param options @c ReplacementStyle and @c Prefix options with their values.
+//!
+//! @return @ref octave_value that contains the equivalent Cell
+//! or struct array of @p val.
+//!
+//! @b Example (returns a struct array):
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[{\"a\":1,\"b\":2},{\"a\":3,\"b\":4}]");
+//! octave_value object_array = decode_object_array (d, octave_value_list ());
+//! @endcode
+//!
+//! @b Example (returns a Cell):
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[{\"a\":1,\"b\":2},{\"b\":3,\"a\":4}]");
+//! octave_value object_array = decode_object_array (d, octave_value_list ());
+//! @endcode
+
+octave_value
+decode_object_array (const rapidjson::Value& val,
+                     const octave_value_list& options)
+{
+  Cell struct_cell = decode_string_and_mixed_array (val, options).cell_value ();
+  string_vector field_names = struct_cell(0).scalar_map_value ().fieldnames ();
+  bool same_field_names = 1;
+  for (octave_idx_type i = 1; i < struct_cell.numel (); ++i)
+    if (! equals (field_names, struct_cell(i).scalar_map_value ().fieldnames ()))
+      {
+        same_field_names = 0;
+        break;
+      }
+  if (same_field_names)
+    {
+      octave_map struct_array;
+      Cell value (dim_vector (struct_cell.numel (), 1));
+      for (octave_idx_type i = 0; i < field_names.numel (); ++i)
+        {
+          for (octave_idx_type k = 0; k < struct_cell.numel (); ++k)
+            value(k) = struct_cell(k).scalar_map_value ().getfield (field_names(i));
+          struct_array.assign (field_names(i), value);
+        }
+      return octave_value (struct_array);
+    }
+  else
+    return struct_cell;
+}
+
+//! Decodes a JSON array that contains only arrays into a Cell or an NDArray
+//! depending on the dimensions and the elements' type of the sub arrays.
+//!
+//! @param val JSON value that is guaranteed to be an array of arrays.
+//! @param options @c ReplacementStyle and @c Prefix options with their values.
+//!
+//! @return @ref octave_value that contains the equivalent Cell
+//! or NDArray of @p val.
+//!
+//! @b Example (returns an NDArray):
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[[1, 2], [3, 4]]");
+//! octave_value array = decode_array_of_arrays (d, octave_value_list ());
+//! @endcode
+//!
+//! @b Example (returns a Cell):
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[[1, 2], [3, 4, 5]]");
+//! octave_value cell = decode_array_of_arrays (d, octave_value_list ());
+//! @endcode
+
+octave_value
+decode_array_of_arrays (const rapidjson::Value& val,
+                        const octave_value_list& options)
+{
+  // Some arrays should be decoded as NDArrays and others as cell arrays
+  Cell cell = decode_string_and_mixed_array(val, options).cell_value ();
+  // Only arrays with sub arrays of booleans and numericals will return NDArray
+  bool is_bool = cell(0).is_bool_matrix ();
+  dim_vector sub_array_dims = cell(0).dims ();
+  octave_idx_type sub_array_ndims = cell(0).ndims ();
+  octave_idx_type cell_numel = cell.numel ();
+  for (octave_idx_type i = 0; i < cell_numel; ++i)
+    {
+      // If one element is cell return the cell array as at least one of
+      // the sub arrays area either an array of: strings, objects or mixed array
+      if (cell(i).iscell ())
+        return cell;
+      // If not the same dim of elements or dim = 0 return cell array
+      if (cell(i).dims () != sub_array_dims || sub_array_dims == dim_vector ())
+        return cell;
+      // If not numeric sub arrays only or bool
+      // sub arrays only return cell array
+      if(cell(i).is_bool_matrix () != is_bool)
+        return cell;
+    }
+  // Calculate the dims of the output array
+  dim_vector array_dims;
+	array_dims.resize (sub_array_ndims + 1);
+	array_dims(0) = cell_numel;
+	for (auto i = 1; i < sub_array_ndims + 1; i++)
+  	array_dims(i) = sub_array_dims(i-1);
+  NDArray array (array_dims);
+
+  // Populate the array with specific order to generate MATLAB-identical output
+  octave_idx_type array_index = 0;
+  for (octave_idx_type i = 0; i < array.numel () / cell_numel; ++i)
+    for (octave_idx_type k = 0; k < cell_numel; ++k)
+      array(array_index++) = cell(k).array_value ()(i);
+  return array;
+}
+
+//! Decodes any type of JSON arrays. This function only serves as an interface
+//! by choosing which function to call from the previous functions.
+//!
+//! @param val JSON value that is guaranteed to be an array.
+//! @param options @c ReplacementStyle and @c Prefix options with their values.
+//!
+//! @return @ref octave_value that contains the output of decoding @p val.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[[1, 2], [3, 4, 5]]");
+//! octave_value array = decode_array (d, octave_value_list ());
+//! @endcode
+
+octave_value
+decode_array (const rapidjson::Value& val, const octave_value_list& options)
+{
+  // Handle empty arrays
+  if (val.Empty ())
+    return NDArray (dim_vector (0,0));
+
+  // Compare with other elements to know if the array has multiple types
+  rapidjson::Type array_type = val[0].GetType ();
+  // Check if the array is numeric and if it has multible types
+  bool same_type = 1, is_numeric = 1;
+  for (const auto& elem : val.GetArray ())
+    {
+      rapidjson::Type current_elem_type = elem.GetType ();
+      if (is_numeric && ! (current_elem_type == rapidjson::kNullType
+          || current_elem_type == rapidjson::kNumberType))
+        is_numeric = 0;
+      if (same_type && (current_elem_type != array_type))
+        // RapidJSON doesn't have kBoolean Type it has kTrueType and kFalseType
+        if (! ((current_elem_type == rapidjson::kTrueType
+                && array_type == rapidjson::kFalseType)
+            || (current_elem_type == rapidjson::kFalseType
+                && array_type == rapidjson::kTrueType)))
+          same_type = 0;
+    }
+  if (is_numeric)
+    return decode_numeric_array (val);
+  if (same_type && (array_type != rapidjson::kStringType))
+    {
+      if (array_type == rapidjson::kTrueType
+          || array_type == rapidjson::kFalseType)
+        return decode_boolean_array (val);
+      else if (array_type == rapidjson::kObjectType)
+        return decode_object_array (val, options);
+      else if (array_type == rapidjson::kArrayType)
+        return decode_array_of_arrays (val, options);
+      else
+        error ("jsondecode.cc: Unidentified type.");
+    }
+  else
+    return decode_string_and_mixed_array (val, options);
+}
+
+//! Decodes any JSON value. This function only serves as an interface
+//! by choosing which function to call from the previous functions.
+//!
+//! @param val JSON value.
+//! @param options @c ReplacementStyle and @c Prefix options with their values.
+//!
+//! @return @ref octave_value that contains the output of decoding @p val.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! rapidjson::Document d;
+//! d.Parse ("[{\"a\":1,\"b\":2},{\"b\":3,\"a\":4}]");
+//! octave_value value = decode (d, octave_value_list ());
+//! @endcode
+
+octave_value
+decode (const rapidjson::Value& val, const octave_value_list& options)
+{
+  if (val.IsBool ())
+    return val.GetBool ();
+  else if (val.IsNumber ())
+    return decode_number (val);
+  else if (val.IsString ())
+    return val.GetString ();
+  else if (val.IsObject ())
+    return decode_object (val, options);
+  else if (val.IsNull ())
+    return NDArray (dim_vector (0,0));
+  else if (val.IsArray ())
+    return decode_array (val, options);
+  else
+    error ("jsondecode.cc: Unidentified type.");
+}
+
+#endif
+
+DEFUN (jsondecode, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{object} =} jsondecode (@var{json})
+@deftypefnx {} {@var{object} =} jsondecode (@var{json}, "ReplacementStyle", @var{rs})
+@deftypefnx {} {@var{object} =} jsondecode (@var{json}, "Prefix", @var{pfx})
+@deftypefnx {} {@var{object} =} jsondecode (@var{json}, @dots{})
+
+Decode text that is formatted in JSON.
+
+The input @var{json} is a string that contains JSON text.
+The output @var{object} is an Octave object that contains the result
+of decoding @var{json}.
+
+For more information about the options @qcode{"ReplacementStyle"} and
+@qcode{"Prefix"}, see @ref{matlab.lang.makeValidName}.
+
+-NOTE: It is not guaranteed to get the same JSON text if you decode
+and then encode it as some names may change by @ref{matlab.lang.makeValidName}.
+
+This table shows the conversions from JSON data types to Octave data types:
+
+@table @asis
+@item @qcode{"Boolean"}
+Scalar @qcode{"logical"}
+
+@item @qcode{"Number"}
+Scalar @qcode{"double"}
+
+@item @qcode{"String"}
+@qcode{"Vector"} of chars
+
+@item JSON @qcode{"Object"}
+Scalar @qcode{"struct"} (field names of the struct may be different from
+the keys of the JSON object due to @ref{matlab.lang.makeValidName})
+
+@item @qcode{"Array"} of different data types
+@qcode{"Cell"} array
+
+@item @qcode{"Array"} of booleans
+@qcode{"Array"} of logicals
+
+@item @qcode{"Array"} of numbers
+@qcode{"Array"} of doubles
+
+@item @qcode{"Array"} of strings
+@qcode{"Cell"} array of vectors of chars
+
+@item @qcode{"Array"} of JSON objects (All objects have the same field names)
+@qcode{"Struct array"}
+
+@item @qcode{"Array"} of JSON objects (Objects have different field names)
+@qcode{"Cell"} array of scalar structs
+
+@item @qcode{"null"} inside a numeric array
+@qcode{"NaN"}
+
+@item @qcode{"null"} inside a non-numeric array
+Empty @qcode{"Array"} of doubles (@qcode{"[]"})
+@end table
+
+Examples:
+
+@example
+@group
+jsondecode ('[1, 2, null, 3]')
+    @result{} 1  2  NaN  3
+@end group
+
+@group
+jsondecode ('["foo", "bar", ["foo", "bar"]]')
+    @result{} ans =
+                {
+                  [1,1] = foo
+                  [2,1] = bar
+                  [3,1] =
+                  {
+                    [1,1] = foo
+                    [2,1] = bar
+                  }
+
+                }
+@end group
+
+@group
+jsondecode ('{"nu#m#ber": 7, "s#tr#ing": "hi"}', 'ReplacementStyle', 'delete')
+    @result{} scalar structure containing the fields:
+                number =  7
+                string = hi
+@end group
+
+@group
+jsondecode ('{"1": "one", "2": "two"}', 'Prefix', 'm_')
+    @result{} scalar structure containing the fields:
+                m_1 = one
+                m_2 = two
+@end group
+@end example
+
+@seealso{jsonencode, matlab.lang.makeValidName}
+@end deftypefn */)
+{
+#if defined (HAVE_RAPIDJSON)
+
+  int nargin = args.length ();
+  // makeValidName options must be in pairs
+  // The number of arguments must be odd
+  if (! (nargin % 2))
+    print_usage ();
+
+  if(! args(0).is_string ())
+    error ("jsondecode: The input must be a character string");
+
+  std::string json = args (0).string_value ();
+  rapidjson::Document d;
+  // DOM is chosen instead of SAX as SAX publishes events to a handler that
+  // decides what to do depending on the event only. This will cause a problem
+  // in decoding JSON arrays as the output may be an array or a cell and that
+  // doesn't only depend on the event (startArray) but also on the types of
+  // the elements inside the array
+  d.Parse <rapidjson::kParseNanAndInfFlag>(json.c_str ());
+
+  if (d.HasParseError ())
+    error("jsondecode: Parse error at offset %u: %s\n",
+          d.GetErrorOffset (),
+          rapidjson::GetParseError_En (d.GetParseError ()));
+  return decode (d, args.slice (1, nargin-1));
+
+#else
+
+  octave_unused_parameter (args);
+
+  err_disabled_feature ("jsondecode",
+                        "RapidJSON is required for JSON encoding\\decoding");
+
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/jsonencode.cc	Thu Aug 13 23:57:07 2020 +0900
@@ -0,0 +1,632 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "builtin-defun-decls.h"
+#include "defun.h"
+#include "error.h"
+#include "errwarn.h"
+#include "ovl.h"
+#include "oct-string.h"
+
+#if defined (HAVE_RAPIDJSON)
+#  include <rapidjson/writer.h>
+#  include <rapidjson/prettywriter.h>
+#  include <rapidjson/stringbuffer.h>
+#endif
+
+#if defined (HAVE_RAPIDJSON)
+
+//! Encodes a scalar Octave value into a numerical JSON value.
+//!
+//! @param writer RapidJSON's writer that is responsible for generating json.
+//! @param obj scalar Octave value.
+//! @param ConvertInfAndNaN @c bool that converts @c Inf and @c NaN to @c null.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! octave_value obj (7);
+//! encode_numeric (writer, obj,true);
+//! @endcode
+
+template <typename T> void
+encode_numeric (T& writer, const octave_value& obj, const bool& ConvertInfAndNaN)
+{
+  double value =  obj.scalar_value ();
+  if (obj.is_bool_scalar ())
+    writer.Bool (obj.bool_value ());
+  // Any numeric input from the interpreter will be in double type so in order
+  // to detect ints, we will check if the floor of the input and the input are
+  // equal using fabs(A - B) < epsilon method as it is more accurate.
+  // If value > 999999, MATLAB will encode it in scientific notation (double)
+  else if (fabs (floor (value) - value) < std::numeric_limits<double>::epsilon ()
+           && value <= 999999 && value >= -999999)
+    writer.Int64 (value);
+  // NA values doesn't exist in MATLAB, so I will decode it as null instead
+  else if (((octave::math::isnan (value) || std::isinf (value)
+            || std::isinf (-value)) && ConvertInfAndNaN)
+           || obj.isna ().bool_value ())
+    writer.Null ();
+  else if (obj.is_double_type ())
+    writer.Double (value);
+  else
+    error ("jsonencode: Unsupported type.");
+}
+
+//! Encodes character vectors and arrays into JSON strings.
+//!
+//! @param writer RapidJSON's writer that is responsible for generating json.
+//! @param obj character vectors or character arrays.
+//! @param original_dims The original dimensions of the array being encoded.
+//! @param level The level of recursion for the function.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! octave_value obj ("foo");
+//! encode_string (writer, obj,true);
+//! @endcode
+
+template <typename T> void
+encode_string (T& writer, const octave_value& obj,
+               const dim_vector& original_dims, int level = 0)
+{
+  charNDArray array = obj.char_array_value ();
+  if (array.isempty ())
+    writer.String ("");
+  else if (array.isvector ())
+    {
+      // Handle the special case when the input is a vector with more than
+      // 2 dimensions (e.g. cat (8, ['a'], ['c'])). In this case, we don't
+      // split the inner vectors of the input. we merge them into one.
+      if (level == 0)
+        {
+          std::string char_vector = "";
+          for (octave_idx_type i = 0; i < array.numel (); ++i)
+            char_vector += array(i);
+          writer.String (char_vector.c_str ());
+        }
+      else
+        for (octave_idx_type i = 0; i < array.numel () / original_dims(1); ++i)
+          {
+            std::string char_vector = "";
+            for (octave_idx_type k = 0; k < original_dims(1); ++k)
+              char_vector += array(i * original_dims(1) + k);
+            writer.String (char_vector.c_str ());
+          }
+    }
+  else
+    {
+      octave_idx_type idx;
+      octave_idx_type ndims = array.ndims ();
+      dim_vector dims = array.dims ();
+
+      // In this case, we already have a vector. So,  we transform it to 2-D
+      // vector in order to be detected by "isvector" in the recursive call
+      if (dims.num_ones () == ndims - 1)
+      {
+        // Handle the special case when the input is a vector with more than
+        // 2 dimensions (e.g. cat (8, ['a'], ['c'])). In this case, we don't
+        // add dimension brackets and treat it as if it is a vector
+        if (level != 0)
+          // Place an opening and a closing bracket (represents a dimension)
+          // for every dimension that equals 1 till we reach the 2-D vector
+          for (int i = level; i < ndims - 1; ++i)
+            writer.StartArray ();
+
+        encode_string (writer, array.as_row (), original_dims, level);
+
+        if (level != 0)
+          for (int i = level; i < ndims - 1; ++i)
+            writer.EndArray ();
+      }
+      else
+        {
+          // We place an opening and a closing bracket for each dimension
+          // that equals 1 to preserve the number of dimensions when decoding
+          // the array after encoding it.
+          if (original_dims (level) == 1 && level != 1)
+          {
+            writer.StartArray ();
+            encode_string (writer, array, original_dims, level + 1);
+            writer.EndArray ();
+          }
+          else
+            {
+              // The second dimension contains the number of the chars in
+              // the char vector. We want to treat them as a one object,
+              // so we replace it with 1
+              dims(1) = 1;
+
+              for (idx = 0; idx < ndims; ++idx)
+                if (dims(idx) != 1)
+                  break;
+              // Create the dimensions that will be used to call "num2cell"
+              // We called "num2cell" to divide the array to smaller sub arrays
+              // in order to encode it recursively.
+              // The recursive encoding is necessary to support encoding of
+              // higher-dimensional arrays.
+              RowVector conversion_dims;
+              conversion_dims.resize (ndims - 1);
+              for (octave_idx_type i = 0; i < idx; ++i)
+                conversion_dims(i) = i + 1;
+              for (octave_idx_type i = idx ; i < ndims - 1; ++i)
+                conversion_dims(i) = i + 2;
+
+              octave_value_list args (obj);
+              args.append (conversion_dims);
+
+              Cell sub_arrays = Fnum2cell (args)(0).cell_value ();
+
+              writer.StartArray ();
+
+              for (octave_idx_type i = 0; i < sub_arrays.numel (); ++i)
+                encode_string (writer, sub_arrays(i), original_dims,
+                               level + 1);
+
+              writer.EndArray ();
+            }
+        }
+    }
+}
+
+//! Encodes a struct Octave value into a JSON object or a JSON array depending
+//! on the type of the struct (scalar struct or struct array.)
+//!
+//! @param writer RapidJSON's writer that is responsible for generating json.
+//! @param obj struct Octave value.
+//! @param ConvertInfAndNaN @c bool that converts @c Inf and @c NaN to @c null.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! octave_value obj (octave_map ());
+//! encode_struct (writer, obj,true);
+//! @endcode
+
+template <typename T> void
+encode_struct (T& writer, const octave_value& obj, const bool& ConvertInfAndNaN)
+{
+  octave_map struct_array = obj.map_value ();
+  octave_idx_type numel = struct_array.numel ();
+  string_vector keys = struct_array.keys ();
+
+  if (numel > 1)
+    writer.StartArray ();
+
+  for (octave_idx_type i = 0; i < numel; ++i)
+    {
+      writer.StartObject ();
+      for (octave_idx_type k = 0; k < keys.numel (); ++k)
+        {
+          writer.Key (keys(k).c_str ());
+          encode (writer, struct_array(i).getfield (keys(k)), ConvertInfAndNaN);
+        }
+      writer.EndObject ();
+    }
+
+  if (numel > 1)
+    writer.EndArray ();
+}
+
+//! Encodes a Cell Octave value into a JSON array
+//!
+//! @param writer RapidJSON's writer that is responsible for generating json.
+//! @param obj Cell Octave value.
+//! @param ConvertInfAndNaN @c bool that converts @c Inf and @c NaN to @c null.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! octave_value obj (cell ());
+//! encode_cell (writer, obj,true);
+//! @endcode
+
+template <typename T> void
+encode_cell (T& writer, const octave_value& obj, const bool& ConvertInfAndNaN)
+{
+  Cell cell = obj.cell_value ();
+
+  writer.StartArray ();
+
+  for (octave_idx_type i = 0; i < cell.numel (); ++i)
+    encode (writer, cell(i), ConvertInfAndNaN);
+
+  writer.EndArray ();
+}
+
+//! Encodes a numeric or logical Octave array into a JSON array
+//!
+//! @param writer RapidJSON's writer that is responsible for generating json.
+//! @param obj numeric or logical Octave array.
+//! @param ConvertInfAndNaN @c bool that converts @c Inf and @c NaN to @c null.
+//! @param original_dims The original dimensions of the array being encoded.
+//! @param level The level of recursion for the function.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! octave_value obj (NDArray ());
+//! encode_array (writer, obj,true);
+//! @endcode
+
+template <typename T> void
+encode_array (T& writer, const octave_value& obj, const bool& ConvertInfAndNaN,
+              const dim_vector& original_dims, int level = 0)
+{
+  NDArray array = obj.array_value ();
+  if (array.isempty ())
+    {
+      writer.StartArray ();
+      writer.EndArray ();
+    }
+  else if (array.isvector ())
+    {
+      writer.StartArray ();
+      for (octave_idx_type i = 0; i < array.numel (); ++i)
+        {
+          if (obj.islogical ())
+            encode_numeric (writer, bool (array(i)), ConvertInfAndNaN);
+          else
+            encode_numeric (writer, array(i), ConvertInfAndNaN);
+        }
+      writer.EndArray ();
+    }
+  else
+    {
+      octave_idx_type idx;
+      octave_idx_type ndims = array.ndims ();
+      dim_vector dims = array.dims ();
+
+      // In this case, we already have a vector. So,  we transform it to 2-D
+      // vector in order to be detected by "isvector" in the recursive call
+      if (dims.num_ones () == ndims - 1)
+        {
+          // Handle the special case when the input is a vector with more than
+          // 2 dimensions (e.g. ones ([1 1 1 1 1 6])). In this case, we don't
+          // add dimension brackets and treat it as if it is a vector
+          if (level != 0)
+            // Place an opening and a closing bracket (represents a dimension)
+            // for every dimension that equals 1 till we reach the 2-D vector
+            for (int i = level; i < ndims - 1; ++i)
+              writer.StartArray ();
+
+          encode_array (writer, array.as_row (), ConvertInfAndNaN,
+                        original_dims);
+
+          if (level != 0)
+            for (int i = level; i < ndims - 1; ++i)
+              writer.EndArray ();
+        }
+      else
+        {
+          // We place an opening and a closing bracket for each dimension
+          // that equals 1 to preserve the number of dimensions when decoding
+          // the array after encoding it.
+          if (original_dims (level) == 1)
+          {
+            writer.StartArray ();
+            encode_array (writer, array, ConvertInfAndNaN,
+                          original_dims, level + 1);
+            writer.EndArray ();
+          }
+          else
+            {
+              for (idx = 0; idx < ndims; ++idx)
+                if (dims(idx) != 1)
+                  break;
+
+              // Create the dimensions that will be used to call "num2cell"
+              // We called "num2cell" to divide the array to smaller sub arrays
+              // in order to encode it recursively.
+              // The recursive encoding is necessary to support encoding of
+              // higher-dimensional arrays.
+              RowVector conversion_dims;
+              conversion_dims.resize (ndims - 1);
+              for (octave_idx_type i = 0; i < idx; ++i)
+                conversion_dims(i) = i + 1;
+              for (octave_idx_type i = idx ; i < ndims - 1; ++i)
+                conversion_dims(i) = i + 2;
+
+              octave_value_list args (obj);
+              args.append (conversion_dims);
+
+              Cell sub_arrays = Fnum2cell (args)(0).cell_value ();
+
+              writer.StartArray ();
+
+              for (octave_idx_type i = 0; i < sub_arrays.numel (); ++i)
+                encode_array (writer, sub_arrays(i), ConvertInfAndNaN,
+                              original_dims, level + 1);
+
+              writer.EndArray ();
+            }
+        }
+    }
+}
+
+//! Encodes any Octave object. This function only serves as an interface
+//! by choosing which function to call from the previous functions.
+//!
+//! @param writer RapidJSON's writer that is responsible for generating json.
+//! @param obj any @ref octave_value that is supported.
+//! @param ConvertInfAndNaN @c bool that converts @c Inf and @c NaN to @c null.
+//!
+//! @b Example:
+//!
+//! @code{.cc}
+//! octave_value obj (true);
+//! encode (writer, obj,true);
+//! @endcode
+
+template <typename T> void
+encode (T& writer, const octave_value& obj, const bool& ConvertInfAndNaN)
+{
+  if (obj.is_real_scalar ())
+    encode_numeric (writer, obj, ConvertInfAndNaN);
+  // As I checked for scalars, this will detect numeric & logical arrays
+  else if (obj.isnumeric () || obj.islogical ())
+    encode_array (writer, obj, ConvertInfAndNaN, obj.dims ());
+  else if (obj.is_string ())
+    encode_string (writer, obj, obj.dims ());
+  else if (obj.isstruct ())
+    encode_struct (writer, obj, ConvertInfAndNaN);
+  else if (obj.iscell ())
+    encode_cell (writer, obj, ConvertInfAndNaN);
+  else if (obj.class_name () == "containers.Map")
+    // To extract the data in containers.Map, Convert it to a struct.
+    // The struct will have a "map" field that its value is a struct that
+    // contains the desired data.
+    // In order to convert it we will need to disable the
+    // "Octave:classdef-to-struct" warning and re-enable it.
+    {
+      set_warning_state ("Octave:classdef-to-struct", "off");
+      encode_struct (writer, obj.scalar_map_value ().getfield ("map"),
+                     ConvertInfAndNaN);
+      set_warning_state ("Octave:classdef-to-struct", "on");
+    }
+  else if (obj.isobject ())
+    {
+      set_warning_state ("Octave:classdef-to-struct", "off");
+      encode_struct (writer, obj.scalar_map_value (), ConvertInfAndNaN);
+      set_warning_state ("Octave:classdef-to-struct", "on");
+    }
+  else
+    error ("jsonencode: Unsupported type.");
+}
+
+#endif
+
+DEFUN (jsonencode, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{json} =} jsonencode (@var{object})
+@deftypefnx {} {@var{json} =} jsonencode (@var{object}, "ConvertInfAndNaN", @var{conv})
+@deftypefnx {} {@var{json} =} jsonencode (@var{object}, "PrettyWriter", @var{pretty})
+@deftypefnx {} {@var{json} =} jsonencode (@var{object}, @dots{})
+
+Encode Octave's data types into JSON text.
+
+The input @var{object} is the Octave's object we want to encode.
+The output @var{json} is the JSON text that contains the result
+of encoding @var{object}.
+
+If the value of the option @qcode{"ConvertInfAndNaN"} is true, @qcode{"NaN"},
+@qcode{"Inf"} and @qcode{"-Inf"} values will be converted to @qcode{"null"}
+value in the output. If it is false, they will remain with their
+original values. The default value for this option is true.
+
+If the value of the option @qcode{"PrettyWriter"} is true, the output text will
+have indentations and line feeds. If it is false, the output will be condensed
+and without any white-spaces. The default value for this option is false.
+
+-NOTES:
+@itemize @bullet
+@item
+Complex numbers are not supported.
+
+@item
+@qcode{"classdef"} objects and @qcode{"containers.Map"} objects are converted
+into structs then encoded as structs.
+
+@item
+To preserve the escape characters (e.g. "\n"), use double-quote strings.
+
+@item
+It is not guaranteed to get the same dimensions for arrays if you encode
+and then decode it. For example, If you encoded a row vector then decoded it,
+you will get a column vector.
+
+@item
+It is not guaranteed to get the same data type if you encode and then decode
+an Octave value as Octave supports more data types than JSON. For example, If
+you encoded an @qcode{"int32"} then decoded it, you will get a @qcode{"double"}.
+@end itemize
+
+
+This table shows the conversions from Octave data types to JSON data types:
+
+@table @asis
+@item Scalar @qcode{"logical"}
+@qcode{"Boolean"}
+
+@item @qcode{"NaN"}, @qcode{"Inf"}, @qcode{"-Inf"}
+@qcode{"null"}
+
+@item Scalar numeric
+@qcode{"Number"}
+
+@item @qcode{"Numeric vector"}
+@qcode{"Numeric array"}
+
+@item @qcode{"Numeric array"}
+Nested @qcode{"numeric array"}
+
+@item @qcode{"Logical vector"}
+@qcode{"Boolean array"}
+
+@item @qcode{"Logical array"}
+Nested @qcode{"boolean array"}
+
+@item @qcode{"Char vector"}
+@qcode{"String"}
+
+@item @qcode{"Char array"}
+Nested @qcode{"string array"}
+
+@item Scalar @qcode{"cell"}
+@qcode{"array"} with a single element
+
+@item @qcode{"Cell vector"}
+@qcode{"Array"}
+
+@item @qcode{"Cell array"}
+single dimensional @qcode{"array"}
+
+@item Scalar @qcode{"struct"}
+@qcode{"JSON Object"}
+
+@item @qcode{"Struct vector"}
+@qcode{"JSON objects array"}
+
+@item @qcode{"Struct array"}
+Nested @qcode{"JSON objects array"}
+
+@item @qcode{"classdef"} objects
+@qcode{"JSON object"}
+
+@item @qcode{"containers.Map"}
+@qcode{"JSON object"}
+@end table
+
+Examples:
+
+@example
+@group
+jsonencode ([1 NaN; 3 4])
+@result{}  [[1,null],[3,4]]
+@end group
+
+@group
+jsonencode ([1 NaN; 3 4], "ConvertInfAndNaN", false)
+@result{}  [[1,NaN],[3,4]]
+@end group
+
+@group
+jsonencode ([true; false], "ConvertInfAndNaN", false, "PrettyWriter", true)
+@result{} ans = [
+                    true,
+                    false
+                ]
+@end group
+
+@group
+jsonencode (['foo', 'bar'; 'foo', 'bar'])
+@result{} ["foobar","foobar"]
+@end group
+
+@group
+jsonencode (struct ('a', Inf, 'b', [], 'c', struct ()))
+@result{} {"a":null,"b":[],"c":{}}
+@end group
+
+@group
+jsonencode (struct ('structarray', struct ('a', {1; 3}, 'b', {2; 4})))
+@result{} {"structarray":[{"a":1,"b":2},{"a":3,"b":4}]}
+@end group
+
+@group
+jsonencode ({'foo'; 'bar'; {'foo'; 'bar'}})
+@result{} ["foo","bar",["foo","bar"]]
+@end group
+
+@group
+jsonencode (containers.Map({'foo'; 'bar'; 'baz'}, [1, 2, 3]))
+@result{} {"bar":2,"baz":3,"foo":1}
+@end group
+@end example
+
+@seealso{jsondecode}
+@end deftypefn */)
+{
+#if defined (HAVE_RAPIDJSON)
+
+  int nargin = args.length ();
+  // jsonencode has two options 'ConvertInfAndNaN' and 'PrettyWriter'
+  if (! (nargin == 1 || nargin == 3 || nargin == 5))
+    print_usage ();
+
+  // Initialize options with their default values
+  bool ConvertInfAndNaN = true;
+  bool PrettyWriter = false;
+
+  for (octave_idx_type i = 1; i < nargin; ++i)
+    {
+      if (! args(i).is_string ())
+        error ("jsonencode: Option must be character vector");
+      if (! args(i+1).is_bool_scalar ())
+        error ("jsonencode: Value for options must be logical scalar");
+
+      std::string option_name = args(i++).string_value ();
+      if (octave::string::strcmpi(option_name, "ConvertInfAndNaN"))
+        ConvertInfAndNaN = args(i).bool_value ();
+      else if (octave::string::strcmpi(option_name, "PrettyWriter"))
+        PrettyWriter = args(i).bool_value ();
+      else
+        error ("jsonencode: Valid options are \'ConvertInfAndNaN\'"
+               " and \'PrettyWriter\'");
+    }
+  rapidjson::StringBuffer json;
+  if (PrettyWriter)
+    // In order to use the "PrettyWriter" option, you must use the development
+    // version of RapidJSON. The release causes an error in compilation.
+    {
+      rapidjson::PrettyWriter<rapidjson::StringBuffer, rapidjson::UTF8<>,
+                              rapidjson::UTF8<>, rapidjson::CrtAllocator,
+                              rapidjson::kWriteNanAndInfFlag> writer (json);
+      encode (writer, args(0), ConvertInfAndNaN);
+    }
+  else
+    {
+      rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>,
+                        rapidjson::UTF8<>, rapidjson::CrtAllocator,
+                        rapidjson::kWriteNanAndInfFlag> writer (json);
+      encode (writer, args(0), ConvertInfAndNaN);
+    }
+
+  return octave_value (json.GetString ());
+
+#else
+
+  octave_unused_parameter (args);
+
+  err_disabled_feature ("jsonencode",
+                        "RapidJSON is required for JSON encoding\\decoding");
+
+#endif
+}
--- a/libinterp/corefcn/module.mk	Mon Aug 03 11:13:26 2020 -0400
+++ b/libinterp/corefcn/module.mk	Thu Aug 13 23:57:07 2020 +0900
@@ -185,6 +185,8 @@
   %reldir%/interpreter-private.cc \
   %reldir%/interpreter.cc \
   %reldir%/inv.cc \
+  %reldir%/jsondecode.cc \
+  %reldir%/jsonencode.cc \
   %reldir%/kron.cc \
   %reldir%/load-path.cc \
   %reldir%/load-save.cc \
--- a/scripts/help/__unimplemented__.m	Mon Aug 03 11:13:26 2020 -0400
+++ b/scripts/help/__unimplemented__.m	Thu Aug 13 23:57:07 2020 +0900
@@ -958,8 +958,6 @@
   "javaMethodEDT",
   "javaObjectEDT",
   "join",
-  "jsondecode",
-  "jsonencode",
   "juliandate",
   "labeledge",
   "labelnode",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/json/jsondecodetest.tst	Thu Aug 13 23:57:07 2020 +0900
@@ -0,0 +1,507 @@
+% test jsondecode
+
+% Note: This script is intended to be a script-based unit test
+%       for MATLAB to test compatibility.  Don't break that!
+
+%% Test 1: decode null values
+
+% null, in nonnumeric arrays to Empty double []
+%!testif HAVE_RAPIDJSON
+%! json = '["str", 5, null, true]';
+%! exp  = {'str'; 5; []; true};
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% null, in numeric arrays to NaN
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '[1, 2, null, 3]';
+%! exp  = [1; 2; NaN; 3];
+%! act  = jsondecode (json);
+%! assert (isequaln (exp, act));
+
+% corner case: array of null values
+%!testif HAVE_RAPIDJSON
+%! json = '[null, null, null]';
+%! exp  = [NaN; NaN; NaN];
+%! act  = jsondecode (json);
+%! assert (isequaln (exp, act));
+
+%% Test 2: decode Boolean, Number and String values
+
+%!testif HAVE_RAPIDJSON
+%! assert (jsondecode ('true'));
+%! assert (~ jsondecode ('false'));
+%! assert (isequal (123.45, jsondecode ('123.45')));
+%! assert (isequal ('hello there', jsondecode ('"hello there"')));
+
+%% Test 3: decode Array of Booleans, Numbers and Strings values
+%!testif HAVE_RAPIDJSON
+%! json = '[true, true, false, true]';
+%! exp  = [1; 1; 0; 1];
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! json = '["true", "true", "false", "true"]';
+%! exp  = {'true'; 'true'; 'false'; 'true'};
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! json = '["foo", "bar", ["foo", "bar"]]';
+%! exp  = {'foo'; 'bar'; {'foo'; 'bar'}};
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! json = '[15000, 5, 12.25, 1502302.3012]';
+%! exp  = [15000; 5; 12.25; 1502302.3012];
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '[[1,2]]';
+%! exp  = [1 2];
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% If they have the same dimensions -> transform to an array
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '[[1, 2], [3, 4]]';
+%! exp  = [1 2; 3 4];
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]';
+%! exp  = cat (3, [1, 3; 5, 7], [2, 4; 6, 8]);
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% try different dimensions for the array
+%!testif HAVE_RAPIDJSON
+%! json = '[[[1, 2, -1], [3, 4, null]], [[5, 6, Inf], [7, 8, -Inf]]]';
+%! exp  = cat (3, [1, 3; 5, 7], [2, 4; 6, 8], [-1, NaN; Inf, -Inf]);
+%! act  = jsondecode (json);
+%! assert (isequaln (exp, act));
+
+% try different dimensions for the array
+%!testif HAVE_RAPIDJSON
+%! json = '[[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]]';
+%! exp  = cat (3, [1, 3; 5, 7; 9, 11], [2, 4; 6, 8; 10, 12]);
+%! act  = jsondecode (json);
+%! assert (isequaln (exp, act));
+
+% try higher dimensions for the array
+%!testif HAVE_RAPIDJSON
+%! json = ['[[[[1,-1], [2,-2]],[[3,-3],[4,-4]]],[[[5,-5],[6,-6]],[[7,-7],', ...
+%!         '[8,-8]]],[[[9,-9], [10,-10]],[[11,-11],[12,-12]]],', ...
+%!         '[[[13,-13],[14,-14]],[[15,-15],[16,-16]]]]'];
+%! tmp1 = cat (3, [1, 3; 5, 7; 9, 11; 13, 15], [2, 4; 6, 8; 10, 12; 14, 16]);
+%! tmp2 = cat (3, [-1, -3; -5, -7; -9, -11; -13, -15], ...
+%!             [-2, -4; -6, -8; -10, -12; -14, -16]);
+%! exp  = cat (4, tmp1, tmp2);
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! json = '[[true, false], [true, false], [true, false]]';
+%! exp  = [1 0; 1 0; 1 0];
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% If they have different dimensions -> transform to a cell array
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '[[1, 2], [3, 4, 5]]';
+%! exp  = {[1; 2]; [3; 4; 5]};
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '[1, 2, [3, 4]]';
+%! exp  = {1; 2; [3; 4]};
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! json = '[true, false, [true, false, false]]';
+%! exp  = {1; 0; [1; 0; 0]};
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+%% Test 4: decode JSON objects
+
+% check the decoding of Boolean, Number and String values inside an object
+%!testif HAVE_RAPIDJSON
+%! json = '{"number": 3.14, "string": "string", "boolean": false}';
+%! exp  = struct ('number', 3.14, 'string', 'string', 'boolean', 0);
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% check the decoding of null values and arrays inside an object & makeValidName
+%!testif HAVE_RAPIDJSON
+%! json = '{"nonnumeric array": ["str", 5, null], "numeric array": [1, 2, null]}';
+%! exp  = struct ('nonnumericArray',{{'str'; 5; []}}, 'numericArray', {[1; 2; NaN]});
+%! act  = jsondecode (json);
+%! assert (isequaln (exp, act));
+
+% check the decoding of objects inside an object & makeValidName
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '{"object": {"  field 1   ": 1, "field-   2": 2, "3field": 3, "": 1}}';
+%! exp  = struct ('object', struct ('field1', 1, 'field_2', 2, 'x3field', 3, 'x', 1));
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% check the decoding of empty objects, empty arrays and Inf inside an object
+%!testif HAVE_RAPIDJSON
+%! json = '{"a": Inf, "b": [], "c": {}}';
+%! exp  = struct ('a', Inf, 'b', [], 'c', struct ());
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% check the decoding of string arrays inside an object & makeValidName
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '{"%string.array": ["Statistical","Parametric","Mapping"]}';
+%! exp  = struct ('x_string_array', {{'Statistical'; 'Parametric'; 'Mapping'}});
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% a big test
+% extracted from jsonlab
+%!testif HAVE_RAPIDJSON
+%! json = ['{' , ...
+%!     '"glossary": { ', ...
+%!         '"title": "example glossary",', ...
+%! 		'"GlossDiv": {', ...
+%!             '"title": "S",', ...
+%! 			'"GlossList": {', ...
+%!                 '"GlossEntry": {', ...
+%!                     '"ID": "SGML",', ...
+%! 					'"SortAs": "SGML",', ...
+%! 					'"GlossTerm": "Standard Generalized Markup Language",', ...
+%! 					'"Acronym": "SGML",', ...
+%! 					'"Abbrev": "ISO 8879:1986",', ...
+%! 					'"GlossDef": {', ...
+%!                         '"para": "A meta-markup language, ', ...
+%!                         'used to create markup languages such as DocBook.",', ...
+%! 						'"GlossSeeAlso": ["GML", "XML"]', ...
+%!                     '},', ...
+%! 					'"GlossSee": "markup"', ...
+%!                 '}', ...
+%!             '}', ...
+%!         '}', ...
+%!     '}', ...
+%! '}'];
+%! tmp1 = struct ('para', ['A meta-markup language, used to create markup languages', ...
+%!                ' such as DocBook.'],'GlossSeeAlso', {{'GML'; 'XML'}});
+%! tmp2 = struct ('ID', 'SGML', 'SortAs', 'SGML', 'GlossTerm', ...
+%!                'Standard Generalized Markup Language', 'Acronym', 'SGML', ...
+%!                'Abbrev', 'ISO 8879:1986', 'GlossDef', tmp1, 'GlossSee', 'markup');
+%! exp  = struct ('glossary', struct ('title', 'example glossary', 'GlossDiv', ...
+%!                 struct ('title', 'S', 'GlossList', struct ('GlossEntry', tmp2))));
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+%% Test 5: decode Array of JSON objects
+
+% arrays with the same field names in the same order
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '{"structarray": [{"a":1,"b":2},{"a":3,"b":4}]}';
+%! exp  = struct ('structarray', struct ('a', {1; 3}, 'b', {2; 4}));
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% different field names before calling makeValidName but the same after calling it
+%! json = [ '[', ...
+%!       '{', ...
+%!         '"i*d": 0,', ...
+%!         '"12name": "Osborn"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"i/d": 1,', ...
+%!         '"12name": "Mcdowell"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"i+d": 2,', ...
+%!         '"12name": "Jewel"', ...
+%!       '}', ...
+%!     ']'];
+%! exp  = struct ('i_d', {0; 1; 2}, 'x12name', {'Osborn'; 'Mcdowell'; 'Jewel'});
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% arrays with the same field names in the same order and a big test
+% json text is generated from json-generator.com
+%!testif HAVE_RAPIDJSON
+%! json = ['[', ...
+%!   '{', ...
+%!     '"x_id": "5ee28980fc9ab3",', ...
+%!     '"index": 0,', ...
+%!     '"guid": "b229d1de-f94a",', ...
+%!     '"latitude": -17.124067,', ...
+%!     '"longitude": -61.161831,', ...
+%!     '"friends": [', ...
+%!       '{', ...
+%!         '"id": 0,', ...
+%!         '"name": "Collins"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 1,', ...
+%!         '"name": "Hays"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 2,', ...
+%!         '"name": "Griffin"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '},', ...
+%!   '{', ...
+%!     '"x_id": "5ee28980dd7250",', ...
+%!     '"index": 1,', ...
+%!     '"guid": "39cee338-01fb",', ...
+%!     '"latitude": 13.205994,', ...
+%!     '"longitude": -37.276231,', ...
+%!     '"friends": [', ...
+%!       '{', ...
+%!         '"id": 0,', ...
+%!         '"name": "Osborn"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 1,', ...
+%!         '"name": "Mcdowell"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 2,', ...
+%!         '"name": "Jewel"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '},', ...
+%!   '{', ...
+%!     '"x_id": "5ee289802422ac",', ...
+%!     '"index": 2,', ...
+%!     '"guid": "3db8d55a-663e",', ...
+%!     '"latitude": -35.453456,', ...
+%!     '"longitude": 14.080287,', ...
+%!     '"friends": [', ...
+%!       '{', ...
+%!         '"id": 0,', ...
+%!         '"name": "Socorro"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 1,', ...
+%!         '"name": "Darla"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 2,', ...
+%!         '"name": "Leanne"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '}', ...
+%! ']'];
+%! tmp1 = struct ('id', {0; 1; 2}, 'name', {'Collins'; 'Hays'; 'Griffin'});
+%! tmp2 = struct ('id', {0; 1; 2}, 'name', {'Osborn'; 'Mcdowell'; 'Jewel'});
+%! tmp3 = struct ('id', {0; 1; 2}, 'name', {'Socorro'; 'Darla'; 'Leanne'});
+%! exp  = struct ('x_id', {'5ee28980fc9ab3'; '5ee28980dd7250'; '5ee289802422ac'}, ...
+%!                'index', {0; 1; 2}, 'guid', {'b229d1de-f94a'; '39cee338-01fb'; '3db8d55a-663e'}, ...
+%!                'latitude', {-17.124067; 13.205994; -35.453456}, 'longitude', ...
+%!                {-61.161831; -37.276231; 14.080287}, 'friends', {tmp1; tmp2; tmp3});
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% arrays with the same field names in different order
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '{"cellarray": [{"a":1,"b":2},{"b":3,"a":4}]}';
+%! exp  = struct ('cellarray', {{struct('a', 1, 'b', 2); struct('b', 3, 'a', 4)}});
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% arrays with different field names
+% extracted from JSONio
+%!testif HAVE_RAPIDJSON
+%! json = '{"cellarray": [{"a":1,"b":2},{"a":3,"c":4}]}';
+%! exp  = struct ('cellarray', {{struct('a', 1, 'b', 2); struct('a', 3, 'c', 4)}});
+%! act  = jsondecode (json);
+%! assert (isequal (exp, act));
+
+% arrays with different field names and a big test
+%!testif HAVE_RAPIDJSON
+%! json = ['[', ...
+%!   '{', ...
+%!     '"x_id": "5ee28980fc9ab3",', ...
+%!     '"index": 0,', ...
+%!     '"guid": "b229d1de-f94a",', ...
+%!     '"latitude": -17.124067,', ...
+%!     '"longitude": -61.161831,', ...
+%!     '"friends": [', ...
+%!       '{', ...
+%!         '"id": 0,', ...
+%!         '"name": "Collins"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 1,', ...
+%!         '"name": "Hays"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 2,', ...
+%!         '"name": "Griffin"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '},', ...
+%!   '{"numeric array": ["str", 5, null], "nonnumeric array": [1, 2, null]},', ...
+%!   '{', ...
+%!      '"firstName": "John",', ...
+%!      '"lastName": "Smith",', ...
+%!      '"age": 25,', ...
+%!      '"address":', ...
+%!      '{', ...
+%!          '"streetAddress": "21 2nd Street",', ...
+%!          '"city": "New York",', ...
+%!          '"state": "NY"', ...
+%!      '},', ...
+%!      '"phoneNumber":', ...
+%!          '{', ...
+%!            '"type": "home",', ...
+%!            '"number": "212 555-1234"', ...
+%!          '}', ...
+%!  '}]'];
+%! tmp1 =  struct ('x_id', '5ee28980fc9ab3', 'index', 0, 'guid', 'b229d1de-f94a', ...
+%!                 'latitude', -17.124067, 'longitude', -61.161831, 'friends', ...
+%!                struct ('id', {0; 1; 2}, 'name', {'Collins'; 'Hays'; 'Griffin'}));
+%! tmp2 = struct ('numericArray',{{'str'; 5; []}}, 'nonnumericArray', {[1; 2; NaN]});
+%! tmp3 = struct ('firstName', 'John','lastName', 'Smith', 'age', 25, 'address', ...
+%!                struct('streetAddress', '21 2nd Street', 'city', 'New York', 'state', 'NY'), ...
+%!                'phoneNumber', struct ('type', 'home', 'number', '212 555-1234'));
+%! exp = {tmp1; tmp2; tmp3};
+%! act  = jsondecode (json);
+%! assert (isequaln (exp, act));
+
+%% Test 6: decode Array of different JSON data types
+%!testif HAVE_RAPIDJSON
+%! json = ['[null, true, Inf, 2531.023, "hello there", ', ...
+%!   '{', ...
+%!     '"x_id": "5ee28980dd7250",', ...
+%!     '"index": 1,', ...
+%!     '"guid": "39cee338-01fb",', ...
+%!     '"latitude": 13.205994,', ...
+%!     '"longitude": -37.276231,', ...
+%!     '"friends": [', ...
+%!       '{', ...
+%!         '"id": 0,', ...
+%!         '"name": "Osborn"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 1,', ...
+%!         '"name": "Mcdowell"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id": 2,', ...
+%!         '"name": "Jewel"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '}]'];
+%! tmp =  struct ('x_id', '5ee28980dd7250', 'index', 1, 'guid', '39cee338-01fb', ...
+%!                 'latitude', 13.205994, 'longitude', -37.276231, 'friends', ...
+%!                 struct ('id', {0; 1; 2}, 'name', {'Osborn'; 'Mcdowell'; 'Jewel'}));
+%! exp = {[]; 1; Inf; 2531.023; 'hello there'; tmp};
+%! act  = jsondecode (json);
+%! assert (isequaln (exp, act));
+
+% Array of arrays
+%!testif HAVE_RAPIDJSON
+%! json = ['[["str", Inf, null], [1, 2, null], ["foo", "bar", ["foo", "bar"]],', ...
+%!   '[[[1, 2], [3, 4]], [[5, 6], [7, 8]]],' , ...
+%!   '[', ...
+%!     '{', ...
+%!       '"x_id": "5ee28980fc9ab3",', ...
+%!       '"index": 0,', ...
+%!       '"guid": "b229d1de-f94a",', ...
+%!       '"latitude": -17.124067,', ...
+%!       '"longitude": -61.161831,', ...
+%!       '"friends": [', ...
+%!         '{', ...
+%!           '"id": 0,', ...
+%!           '"name": "Collins"', ...
+%!         '},', ...
+%!         '{', ...
+%!           '"id": 1,', ...
+%!           '"name": "Hays"', ...
+%!         '},', ...
+%!         '{', ...
+%!           '"id": 2,', ...
+%!           '"name": "Griffin"', ...
+%!         '}', ...
+%!       ']', ...
+%!     '},', ...
+%!     '{"numeric array": ["str", 5, null], "nonnumeric array": [1, 2, null]},', ...
+%!     '{', ...
+%!        '"firstName": "John",', ...
+%!        '"lastName": "Smith",', ...
+%!        '"age": 25,', ...
+%!        '"address":', ...
+%!        '{', ...
+%!            '"streetAddress": "21 2nd Street",', ...
+%!            '"city": "New York",', ...
+%!            '"state": "NY"', ...
+%!        '},', ...
+%!        '"phoneNumber":', ...
+%!            '{', ...
+%!              '"type": "home",', ...
+%!              '"number": "212 555-1234"', ...
+%!            '}', ...
+%!    '}]]'];
+%! tmp1 = struct ('x_id', '5ee28980fc9ab3', 'index', 0, 'guid', 'b229d1de-f94a', ...
+%!                'latitude', -17.124067, 'longitude', -61.161831, 'friends', ...
+%!                struct ('id', {0; 1; 2}, 'name', {'Collins'; 'Hays'; 'Griffin'}));
+%! tmp2 = struct ('numericArray',{{'str'; 5; []}}, 'nonnumericArray', {[1; 2; NaN]});
+%! tmp3 = struct ('firstName', 'John','lastName', 'Smith', 'age', 25, 'address', ...
+%!                   struct('streetAddress', '21 2nd Street', 'city', 'New York', 'state', 'NY'), ...
+%!                   'phoneNumber', struct ('type', 'home', 'number', '212 555-1234'));
+%! exp = {{'str'; Inf; []}; [1; 2; NaN]; {'foo'; 'bar'; {'foo'; 'bar'}};
+%!        cat(3, [1, 3; 5, 7], [2, 4; 6, 8]); {tmp1; tmp2 ;tmp3}};
+%! act  = jsondecode (json);
+%! assert (isequaln (exp, act));
+
+%% Test 7: Forward "ReplacementStyle" and "Prefix" options
+
+%!testif HAVE_RAPIDJSON
+%! json = '{"1a": {"1*a": {"1+*/-a": {"1#a": {}}}}}';
+%! exp  = struct ('n1a', struct ('n1a', struct ('n1a', struct ('n1a', struct ()))));
+%! act  = jsondecode (json, "ReplacementStyle", "delete", "Prefix", "_", "Prefix", "n");
+%! assert (isequal (exp, act));
+
+% Check the forwarding of "ReplacementStyle" and "Prefix" options inside arrays
+%!testif HAVE_RAPIDJSON
+%! json = [ '[', ...
+%!       '{', ...
+%!         '"i*d": 0,', ...
+%!         '"12name": "Osborn"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"i*d": 1,', ...
+%!         '"12name": "Mcdowell"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"i*d": 2,', ...
+%!         '"12name": "Jewel"', ...
+%!       '}', ...
+%!     ']'];
+%! exp  = struct ('i0x2Ad', {0; 1; 2}, 'm_12name', {'Osborn'; 'Mcdowell'; 'Jewel'});
+%! act  = jsondecode (json, "ReplacementStyle", "hex", "Prefix", "m_");
+%! assert (isequal (exp, act));
+
+% Check the forwarding of "ReplacementStyle" and "Prefix" options inside arrays
+%!testif HAVE_RAPIDJSON
+%! json = '{"cell*array": [{"1a":1,"b*1":2},{"1a":3,"b/2":4}]}';
+%! exp  = struct ('cell_array', {{struct('x_1a', 1, 'b_1', 2); struct('x_1a', 3, 'b_2', 4)}});
+%! act  = jsondecode (json, "ReplacementStyle", "underscore", "Prefix", "x_");
+%! assert (isequal (exp, act));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/json/jsonencodetest.tst	Thu Aug 13 23:57:07 2020 +0900
@@ -0,0 +1,580 @@
+% test jsonencode
+
+% Note: This script is intended to be a script-based unit test
+%       for MATLAB to test compatibility.  Don't break that!
+
+% some tests here are just the reverse of tests in jsondecode with some modifiactions
+%% Test 1: encode logical and numeric scalars, NaN and Inf
+%!testif HAVE_RAPIDJSON
+%! assert (isequal (jsonencode (true), 'true'));
+%! assert (isequal (jsonencode (50.025), '50.025'));
+%! assert (isequal (jsonencode (NaN), 'null'));
+%! assert (isequal (jsonencode (Inf), 'null'));
+%! assert (isequal (jsonencode (-Inf), 'null'));
+
+% Customized encoding of Nan, Inf, -Inf
+%!testif HAVE_RAPIDJSON
+%! assert (isequal (jsonencode (NaN, 'ConvertInfAndNaN', true), 'null'));
+%! assert (isequal (jsonencode (Inf, 'ConvertInfAndNaN', true), 'null'));
+%! assert (isequal (jsonencode (-Inf, 'ConvertInfAndNaN', true), 'null'));
+
+%!testif HAVE_RAPIDJSON
+%! assert (isequal (jsonencode (NaN, 'ConvertInfAndNaN', false), 'NaN'));
+%! assert (isequal (jsonencode (Inf, 'ConvertInfAndNaN', false), 'Infinity'));
+%! assert (isequal (jsonencode (-Inf, 'ConvertInfAndNaN', false), '-Infinity'));
+
+%% Test 2: encode character vectors and arrays
+%!testif HAVE_RAPIDJSON
+%! assert (isequal (jsonencode (''), '""'));
+%! assert (isequal (jsonencode ('hello there'), '"hello there"'));
+%! assert (isequal (jsonencode (['foo'; 'bar']), '["foo","bar"]'));
+%! assert (isequal (jsonencode (['foo', 'bar'; 'foo', 'bar']), '["foobar","foobar"]'));
+
+%!testif HAVE_RAPIDJSON
+%! data = [[['foo'; 'bar']; ['foo'; 'bar']], [['foo'; 'bar']; ['foo'; 'bar']]];
+%! exp  = '["foofoo","barbar","foofoo","barbar"]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! data = cat (3, ['a', 'b'; 'c', 'd'], ['e', 'f'; 'g', 'h']);
+%! exp  = '[["ab","ef"],["cd","gh"]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try different dimensions for the array
+%!testif HAVE_RAPIDJSON
+%! data = cat (3, ['a', 'b'; 'c', 'd'; '1', '2'], ['e', 'f'; 'g', 'h'; '3', '4']);
+%! exp  = '[["ab","ef"],["cd","gh"],["12","34"]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array
+%!testif HAVE_RAPIDJSON
+%! tmp1 = cat (3, ['1', '3'; '5', '7'; '9', 'e'; 'f', 'g'], ...
+%!                ['2', '4'; '6', '8'; 'a', 'b'; 'c', 'd']);
+%! tmp2 = cat (3, ['1', '3'; '5', '7'; '9', 'e'; 'f', 'g'], ...
+%!             ['2', '4'; '6', '8'; 'a', 'b'; 'c', 'd']);
+%! data = cat (4, tmp1, tmp2);
+%! exp  = ['[[["13","13"],["24","24"]],[["57","57"],["68","68"]],', ...
+%!         '[["9e","9e"],["ab","ab"]],[["fg","fg"],["cd","cd"]]]'];
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try different dimensions for the array with one of its dimensions equals one
+%!testif HAVE_RAPIDJSON
+%! data = cat (4, ['a'; 'b'], ['c'; 'd']);
+%! exp  = '[[["a","c"]],[["b","d"]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try different dimensions for the array with one of its dimensions equals one
+%!testif HAVE_RAPIDJSON
+%! data = cat (8, ['a'], ['c']);
+%! exp  = '"ac"';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try different dimensions for the array with one of its dimensions equals one
+%!testif HAVE_RAPIDJSON
+%! data = cat (8, ['a'; 'b'; '1'], ['c'; 'd'; '2']);
+%! exp  = '[[[[[[["a","c"]]]]]],[[[[[["b","d"]]]]]],[[[[[["1","2"]]]]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+%% Test 3: encode numeric and logical arrays (with NaN and Inf)
+% test simple vectors
+%!testif HAVE_RAPIDJSON
+%! assert (isequal (jsonencode ([]), '[]'));
+%! assert (isequal (jsonencode ([1, 2, 3, 4]), '[1,2,3,4]'));
+%! assert (isequal (jsonencode ([true; false; true]), '[true,false,true]'));
+
+% test arrays
+%!testif HAVE_RAPIDJSON
+%! data = [1 NaN; 3 4];
+%! exp  = '[[1,null],[3,4]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! data = cat (3, [NaN, 3; 5, Inf], [2, 4; -Inf, 8]);
+%! exp  = '[[[null,2],[3,4]],[[5,null],[null,8]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% Customized encoding of Nan, Inf, -Inf
+%!testif HAVE_RAPIDJSON
+%! data = cat (3, [1, NaN; 5, 7], [2, Inf; 6, -Inf]);
+%! exp  = '[[[1,2],[NaN,Infinity]],[[5,6],[7,-Infinity]]]';
+%! act  = jsonencode (data, 'ConvertInfAndNaN', false);
+%! assert (isequal (exp, act));
+
+% try different dimensions for the array
+%!testif HAVE_RAPIDJSON
+%! data = cat (3, [1, 3; 5, 7], [2, 4; 6, 8], [-1, NaN; Inf, -Inf]);
+%! exp  = '[[[1,2,-1],[3,4,NaN]],[[5,6,Infinity],[7,8,-Infinity]]]';
+%! act  = jsonencode (data, 'ConvertInfAndNaN', false);
+%! assert (isequal (exp, act));
+
+% try different dimensions for the array with one of its dimensions equals one
+%!testif HAVE_RAPIDJSON
+%! data = cat (3, [1; 7; 11], [4; 8; 12]);
+%! exp  = '[[[1,4]],[[7,8]],[[11,12]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with one of its dimensions equals one
+%!testif HAVE_RAPIDJSON
+%! tmp1 = cat (3, [5, 7], [2, 4]);
+%! tmp2 = cat (3, [-1, -3], [-2, -4]);
+%! data = cat (4, tmp1, tmp2);
+%! exp  = '[[[[5,-1],[2,-2]],[[7,-3],[4,-4]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with one of its dimensions equals one
+%!testif HAVE_RAPIDJSON
+%! tmp1 = cat (3, [5; 7], [2; 4]);
+%! tmp2 = cat (3, [-1; -3], [-2; -4]);
+%! data = cat (4, tmp1, tmp2);
+%! exp  = '[[[[5,-1],[2,-2]]],[[[7,-3],[4,-4]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with one of its dimensions equals one
+%!testif HAVE_RAPIDJSON
+%! data = cat (4, [1, 3; 5, 7], [-1, -3; -5, -7]);
+%! exp  = '[[[[1,-1]],[[3,-3]]],[[[5,-5]],[[7,-7]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with one of its dimensions equals one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 1 1 1 1 6]);
+%! exp  = '[1,1,1,1,1,1]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with one of its dimensions equals one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 2 2 2 2]);
+%! exp  = '[[[[[1,1],[1,1]],[[1,1],[1,1]]],[[[1,1],[1,1]],[[1,1],[1,1]]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with some of its dimensions equal one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 2 2 1 2]);
+%! exp  = '[[[[[1,1]],[[1,1]]],[[[1,1]],[[1,1]]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with some of its dimensions equal one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 2 1 2 1 2]);
+%! exp  = '[[[[[[1,1]],[[1,1]]]],[[[[1,1]],[[1,1]]]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with some of its dimensions equal one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 1 2 1 2 1 2]);
+%! exp  = '[[[[[[[1,1]],[[1,1]]]],[[[[1,1]],[[1,1]]]]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with some of its dimensions equal one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 2 2 1 1 2]);
+%! exp  = '[[[[[[1,1]]],[[[1,1]]]],[[[[1,1]]],[[[1,1]]]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with some of its dimensions equal one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 2 1 3 1 1 1 2]);
+%! exp  = ['[[[[[[[[1,1]]]],[[[[1,1]]]],[[[[1,1]]]]]],[[[[[[1,1]]]],', ...
+%!         '[[[[1,1]]]],[[[[1,1]]]]]]]]'];
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with some of its dimensions equal one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 1 1 1 2 1 1 1 2]);
+%! exp  = '[[[[[[[[[1,1]]]],[[[[1,1]]]]]]]]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with some of its dimensions equal one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 3 2 1 1 2 1 2 2]);
+%! exp  = ['[[[[[[[[[1,1],[1,1]]],[[[1,1],[1,1]]]]]],[[[[[[1,1],', ...
+%!         '[1,1]]],[[[1,1],[1,1]]]]]]],[[[[[[[1,1],[1,1]]],[[[1,', ...
+%!         '1],[1,1]]]]]],[[[[[[1,1],[1,1]]],[[[1,1],[1,1]]]]]]],', ...
+%!         '[[[[[[[1,1],[1,1]]],[[[1,1],[1,1]]]]]],[[[[[[1,1],[1,', ...
+%!         '1]]],[[[1,1],[1,1]]]]]]]]]'];
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array with some of its dimensions equal one
+%!testif HAVE_RAPIDJSON
+%! data = ones ([1 1 1 1 1 1 2 1 1 2 2 3 1 1 1 1 1 1 1 2]);
+%! exp  =  ['[[[[[[[[[[[[[[[[[[[[1,1]]]]]]]],[[[[[[[[1,1]]]]]]]],', ...
+%!          '[[[[[[[[1,1]]]]]]]]],[[[[[[[[[1,1]]]]]]]],[[[[[[[[1,1]]]]]', ...
+%!          ']]],[[[[[[[[1,1]]]]]]]]]],[[[[[[[[[[1,1]]]]]]]],[[[[[[[[1,1]', ...
+%!          ']]]]]]],[[[[[[[[1,1]]]]]]]]],[[[[[[[[[1,1]]]]]]]],[[[[[[', ...
+%!          '[[1,1]]]]]]]],[[[[[[[[1,1]]]]]]]]]]]]],[[[[[[[[[[[[[1,1]', ...
+%!          ']]]]]]],[[[[[[[[1,1]]]]]]]],[[[[[[[[1,1]]]]]]]]],[[[[[[[[', ...
+%!          '[1,1]]]]]]]],[[[[[[[[1,1]]]]]]]],[[[[[[[[1,1]]]]]]]]]],[[[', ...
+%!          '[[[[[[[1,1]]]]]]]],[[[[[[[[1,1]]]]]]]],[[[[[[[[1,1]]]]]]]]],', ...
+%!          '[[[[[[[[[1,1]]]]]]]],[[[[[[[[1,1]]]]]]]],[[[[[[[[1,1]]', ...
+%!          ']]]]]]]]]]]]]]]]]]'];
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% try higher dimensions for the array
+%!testif HAVE_RAPIDJSON
+%! tmp1 = cat (3, [1, 3; 5, 7; 9, 11; 13, 15], [2, 4; 6, 8; 10, 12; 14, 16]);
+%! tmp2 = cat (3, [-1, -3; -5, -7; -9, -11; -13, -15], ...
+%!             [-2, -4; -6, -8; -10, -12; -14, -16]);
+%! data = cat (4, tmp1, tmp2);
+%! exp  = ['[[[[1,-1],[2,-2]],[[3,-3],[4,-4]]],[[[5,-5],[6,-6]],[[7,-7],', ...
+%!         '[8,-8]]],[[[9,-9],[10,-10]],[[11,-11],[12,-12]]],', ...
+%!         '[[[13,-13],[14,-14]],[[15,-15],[16,-16]]]]'];
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! data = [true false; true false; true false];
+%! exp  = '[[true,false],[true,false],[true,false]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+%% Test 4: encode containers.Map
+
+% KeyType must be char to encode objects of containers.Map
+%!testif HAVE_RAPIDJSON
+%! assert (isequal (jsonencode (containers.Map('1', [1, 2, 3])), '{"1":[1,2,3]}'));
+
+%!testif HAVE_RAPIDJSON
+%! data = containers.Map({'foo'; 'bar'; 'baz'}, [1, 2, 3]);
+%! exp  = '{"bar":2,"baz":3,"foo":1}';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! data = containers.Map({'foo'; 'bar'; 'baz'}, {{1, 'hello', NaN}, true, [2, 3, 4]});
+%! exp  = '{"bar":true,"baz":[2,3,4],"foo":[1,"hello",NaN]}';
+%! act  = jsonencode (data, 'ConvertInfAndNaN', false);
+%! assert (isequal (exp, act));
+
+%% Test 5: encode scalar structs
+% check the encoding of Boolean, Number and String values inside a struct
+%!testif HAVE_RAPIDJSON
+%! data = struct ('number', 3.14, 'string', 'string', 'boolean', false);
+%! exp  = '{"number":3.14,"string":"string","boolean":false}';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% check the decoding of null, Inf and -Inf values inside a struct
+%!testif HAVE_RAPIDJSON
+%! data = struct ('numericArray', [7, NaN, Inf, -Inf]);
+%! exp  = '{"numericArray":[7,null,null,null]}';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% Customized encoding of Nan, Inf, -Inf
+%!testif HAVE_RAPIDJSON
+%! data = struct ('numericArray', [7, NaN, Inf, -Inf]);
+%! exp  = '{"numericArray":[7,NaN,Infinity,-Infinity]}';
+%! act  = jsonencode (data, 'ConvertInfAndNaN', false);
+%! assert (isequal (exp, act));
+
+% check the encoding of structs inside a struct
+%!testif HAVE_RAPIDJSON
+%! data = struct ('object', struct ('field1', 1, 'field2', 2, 'field3', 3));
+%! exp  = '{"object":{"field1":1,"field2":2,"field3":3}}';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% check the encoding of empty structs, empty arrays and Inf inside a struct
+%!testif HAVE_RAPIDJSON
+%! data = struct ('a', Inf, 'b', [], 'c', struct ());
+%! exp  = '{"a":null,"b":[],"c":{}}';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% a big test
+%!testif HAVE_RAPIDJSON
+%! tmp1 = struct ('para', ['A meta-markup language, used to create markup languages', ...
+%!                ' such as DocBook.'],'GlossSeeAlso', {{'GML'; 'XML'}});
+%! tmp2 = struct ('ID', 'SGML', 'SortAs', 'SGML', 'GlossTerm', ...
+%!                'Standard Generalized Markup Language', 'Acronym', 'SGML', ...
+%!                'Abbrev', 'ISO 8879:1986', 'GlossDef', tmp1, 'GlossSee', 'markup');
+%! data = struct ('glossary', struct ('title', 'example glossary', 'GlossDiv', ...
+%!                 struct ('title', 'S', 'GlossList', struct ('GlossEntry', tmp2))));
+%! exp = ['{' , ...
+%!     '"glossary":{', ...
+%!         '"title":"example glossary",', ...
+%! 		'"GlossDiv":{', ...
+%!             '"title":"S",', ...
+%! 			'"GlossList":{', ...
+%!                 '"GlossEntry":{', ...
+%!                     '"ID":"SGML",', ...
+%! 					'"SortAs":"SGML",', ...
+%! 					'"GlossTerm":"Standard Generalized Markup Language",', ...
+%! 					'"Acronym":"SGML",', ...
+%! 					'"Abbrev":"ISO 8879:1986",', ...
+%! 					'"GlossDef":{', ...
+%!                         '"para":"A meta-markup language, ', ...
+%!                         'used to create markup languages such as DocBook.",', ...
+%! 						'"GlossSeeAlso":["GML","XML"]', ...
+%!                     '},', ...
+%! 					'"GlossSee":"markup"', ...
+%!                 '}', ...
+%!             '}', ...
+%!         '}', ...
+%!     '}', ...
+%! '}'];
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+%% Test 6: encode struct arrays
+%!testif HAVE_RAPIDJSON
+%! data = struct ('structarray', struct ('a', {1; 3}, 'b', {2; 4}));
+%! exp  = '{"structarray":[{"a":1,"b":2},{"a":3,"b":4}]}';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% a big Test
+%!testif HAVE_RAPIDJSON
+%! tmp1 = struct ('id', {0; 1; 2}, 'name', {'Collins'; 'Hays'; 'Griffin'});
+%! tmp2 = struct ('id', {0; 1; 2}, 'name', {'Osborn'; 'Mcdowell'; 'Jewel'});
+%! tmp3 = struct ('id', {0; 1; 2}, 'name', {'Socorro'; 'Darla'; 'Leanne'});
+%! data = struct ('x_id', {'5ee28980fc9ab3'; '5ee28980dd7250'; '5ee289802422ac'}, ...
+%!                'index', {0; 1; 2}, 'guid', {'b229d1de-f94a'; '39cee338-01fb'; '3db8d55a-663e'}, ...
+%!                'latitude', {-17.124067; 13.205994; -35.453456}, 'longitude', ...
+%!                {-61.161831; -37.276231; 14.080287}, 'friends', {tmp1; tmp2; tmp3});
+%! exp  = ['[', ...
+%!   '{', ...
+%!     '"x_id":"5ee28980fc9ab3",', ...
+%!     '"index":0,', ...
+%!     '"guid":"b229d1de-f94a",', ...
+%!     '"latitude":-17.124067,', ...
+%!     '"longitude":-61.161831,', ...
+%!     '"friends":[', ...
+%!       '{', ...
+%!         '"id":0,', ...
+%!         '"name":"Collins"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":1,', ...
+%!         '"name":"Hays"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":2,', ...
+%!         '"name":"Griffin"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '},', ...
+%!   '{', ...
+%!     '"x_id":"5ee28980dd7250",', ...
+%!     '"index":1,', ...
+%!     '"guid":"39cee338-01fb",', ...
+%!     '"latitude":13.205994,', ...
+%!     '"longitude":-37.276231,', ...
+%!     '"friends":[', ...
+%!       '{', ...
+%!         '"id":0,', ...
+%!         '"name":"Osborn"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":1,', ...
+%!         '"name":"Mcdowell"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":2,', ...
+%!         '"name":"Jewel"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '},', ...
+%!   '{', ...
+%!     '"x_id":"5ee289802422ac",', ...
+%!     '"index":2,', ...
+%!     '"guid":"3db8d55a-663e",', ...
+%!     '"latitude":-35.453456,', ...
+%!     '"longitude":14.080287,', ...
+%!     '"friends":[', ...
+%!       '{', ...
+%!         '"id":0,', ...
+%!         '"name":"Socorro"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":1,', ...
+%!         '"name":"Darla"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":2,', ...
+%!         '"name":"Leanne"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '}', ...
+%! ']'];
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+%% Test 7: encode cell arrays
+%!testif HAVE_RAPIDJSON
+%! assert (isequal ('[]', jsonencode ({})));
+%! assert (isequal ('[5]', jsonencode ({5})));
+%! assert (isequal ('["hello there"]', jsonencode ({'hello there'})));
+
+%!testif HAVE_RAPIDJSON
+%! data = {'true', 'true'; 'false', 'true'};
+%! exp  = '["true","false","true","true"]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+%!testif HAVE_RAPIDJSON
+%! data = {'foo'; 'bar'; {'foo'; 'bar'}};
+%! exp  = '["foo","bar",["foo","bar"]]';
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% cell array of structs & a big test
+%!testif HAVE_RAPIDJSON
+%! tmp1 = struct ('x_id', '5ee28980fc9ab3', 'index', 0, 'guid', 'b229d1de-f94a', ...
+%!                 'latitude', -17.124067, 'longitude', -61.161831, 'friends', ...
+%!                struct ('id', {0; 1; 2}, 'name', {'Collins'; 'Hays'; 'Griffin'}));
+%! tmp2 = struct ('numericArray',{{'str'; 5; []}}, 'nonnumericArray', {[1; 2; NaN]});
+%! tmp3 = struct ('firstName', 'John','lastName', 'Smith', 'age', 25, 'address', ...
+%!                struct('streetAddress', '21 2nd Street', 'city', 'New York', 'state', 'NY'), ...
+%!                'phoneNumber', struct ('type', 'home', 'number', '212 555-1234'));
+%! data = {tmp1; tmp2; tmp3};
+%! exp  = ['[', ...
+%!   '{', ...
+%!     '"x_id":"5ee28980fc9ab3",', ...
+%!     '"index":0,', ...
+%!     '"guid":"b229d1de-f94a",', ...
+%!     '"latitude":-17.124067,', ...
+%!     '"longitude":-61.161831,', ...
+%!     '"friends":[', ...
+%!       '{', ...
+%!         '"id":0,', ...
+%!         '"name":"Collins"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":1,', ...
+%!         '"name":"Hays"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":2,', ...
+%!         '"name":"Griffin"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '},', ...
+%!   '{"numericArray":["str",5,[]],"nonnumericArray":[1,2,null]},', ...
+%!   '{', ...
+%!      '"firstName":"John",', ...
+%!      '"lastName":"Smith",', ...
+%!      '"age":25,', ...
+%!      '"address":', ...
+%!      '{', ...
+%!          '"streetAddress":"21 2nd Street",', ...
+%!          '"city":"New York",', ...
+%!          '"state":"NY"', ...
+%!      '},', ...
+%!      '"phoneNumber":', ...
+%!          '{', ...
+%!            '"type":"home",', ...
+%!            '"number":"212 555-1234"', ...
+%!          '}', ...
+%!  '}]'];
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
+
+% cell array of diferrent types & Customized encoding of Nan, Inf, -Inf
+%!testif HAVE_RAPIDJSON
+%! tmp =  struct ('x_id', '5ee28980dd7250', 'index', 1, 'guid', '39cee338-01fb', ...
+%!                 'latitude', 13.205994, 'longitude', -37.276231, 'friends', ...
+%!                 struct ('id', {0; 1; 2}, 'name', {'Osborn'; 'Mcdowell'; 'Jewel'}));
+%! data = {NaN; true; Inf; 2531.023; 'hello there'; tmp};
+%! exp  = ['[NaN,true,Infinity,2531.023,"hello there",', ...
+%!   '{', ...
+%!     '"x_id":"5ee28980dd7250",', ...
+%!     '"index":1,', ...
+%!     '"guid":"39cee338-01fb",', ...
+%!     '"latitude":13.205994,', ...
+%!     '"longitude":-37.276231,', ...
+%!     '"friends":[', ...
+%!       '{', ...
+%!         '"id":0,', ...
+%!         '"name":"Osborn"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":1,', ...
+%!         '"name":"Mcdowell"', ...
+%!       '},', ...
+%!       '{', ...
+%!         '"id":2,', ...
+%!         '"name":"Jewel"', ...
+%!       '}', ...
+%!     ']', ...
+%!   '}]'];
+%! act  = jsonencode (data, 'ConvertInfAndNaN', false);
+%! assert (isequal (exp, act));
+
+% a big example
+%!testif HAVE_RAPIDJSON
+%! tmp1 = struct ('x_id', '5ee28980fc9ab3', 'index', 0, 'guid', 'b229d1de-f94a', ...
+%!                'latitude', -17.124067, 'longitude', -61.161831, 'friends', ...
+%!                struct ('id', {0; 1; 2}, 'name', {'Collins'; 'Hays'; 'Griffin'}));
+%! tmp2 = struct ('numericArray',{{'str'; 5; -Inf}}, 'nonnumericArray', {[1; 2; NaN]});
+%! tmp3 = struct ('firstName', 'John','lastName', 'Smith', 'age', 25, 'address', ...
+%!                   struct('streetAddress', '21 2nd Street', 'city', 'New York', 'state', 'NY'), ...
+%!                   'phoneNumber', struct ('type', 'home', 'number', '212 555-1234'));
+%! data = {{'str'; Inf; {}}; [1; 2; NaN]; {'foo'; 'bar'; {'foo'; 'bar'}};
+%!        cat(3, [1, 3; 5, 7], [2, 4; 6, 8]); {tmp1; tmp2 ;tmp3}};
+%! exp  = ['[["str",null,[]],[1,2,null],["foo","bar",["foo","bar"]],', ...
+%!   '[[[1,2],[3,4]],[[5,6],[7,8]]],' , ...
+%!   '[', ...
+%!     '{', ...
+%!       '"x_id":"5ee28980fc9ab3",', ...
+%!       '"index":0,', ...
+%!       '"guid":"b229d1de-f94a",', ...
+%!       '"latitude":-17.124067,', ...
+%!       '"longitude":-61.161831,', ...
+%!       '"friends":[', ...
+%!         '{', ...
+%!           '"id":0,', ...
+%!           '"name":"Collins"', ...
+%!         '},', ...
+%!         '{', ...
+%!           '"id":1,', ...
+%!           '"name":"Hays"', ...
+%!         '},', ...
+%!         '{', ...
+%!           '"id":2,', ...
+%!           '"name":"Griffin"', ...
+%!         '}', ...
+%!       ']', ...
+%!     '},', ...
+%!     '{"numericArray":["str",5,null],"nonnumericArray":[1,2,null]},', ...
+%!     '{', ...
+%!        '"firstName":"John",', ...
+%!        '"lastName":"Smith",', ...
+%!        '"age":25,', ...
+%!        '"address":', ...
+%!        '{', ...
+%!            '"streetAddress":"21 2nd Street",', ...
+%!            '"city":"New York",', ...
+%!            '"state":"NY"', ...
+%!        '},', ...
+%!        '"phoneNumber":', ...
+%!            '{', ...
+%!              '"type":"home",', ...
+%!              '"number":"212 555-1234"', ...
+%!            '}', ...
+%!    '}]]'];
+%! act  = jsonencode (data);
+%! assert (isequal (exp, act));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/json/module.mk	Thu Aug 13 23:57:07 2020 +0900
@@ -0,0 +1,5 @@
+json_TEST_FILES = \
+  %reldir%/jsondecodetest.tst \
+  %reldir%/jsonencodetest.tst
+
+TEST_FILES += $(json_TEST_FILES)
--- a/test/module.mk	Mon Aug 03 11:13:26 2020 -0400
+++ b/test/module.mk	Thu Aug 13 23:57:07 2020 +0900
@@ -90,6 +90,7 @@
 include %reldir%/colon-op/module.mk
 include %reldir%/ctor-vs-method/module.mk
 include %reldir%/fcn-handle/module.mk
+include %reldir%/json/module.mk
 include %reldir%/local-functions/module.mk
 include %reldir%/mex/module.mk
 include %reldir%/nest/module.mk