changeset 28624:aae9d7f098bd

Support for RapidJSON 1.1.0 with jsonencode and jsondecode * configure.ac: Define HAVE_RAPIDJSON for RapidJSON 1.1.0 and HAVE_RAPIDJSON_DEV for the development or newer release in the future containing the `rapidjson/cursorstreamwrapper.h` header file. * libinterp/corefcn/jsondecode.cc: Improve docstrings and comments. * libinterp/corefcn/jsonencode.cc (Fjsonencode): Enable the 'PrettyWriter' option only if HAVE_RAPIDJSON_DEV is set to "1". Sort header includes. Improve docstrings and comments. This is the result of GSoC 2020 by Abdallah Elshamy and an addition to cset 5da49e37a6c9.
author Abdallah Elshamy <abdallah.k.elshamy@gmail.com>
date Tue, 18 Aug 2020 14:44:22 +0900
parents 7bdd127d77e5
children 173807014259
files configure.ac libinterp/corefcn/jsondecode.cc libinterp/corefcn/jsonencode.cc
diffstat 3 files changed, 76 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Sat Aug 15 23:41:26 2020 -0400
+++ b/configure.ac	Tue Aug 18 14:44:22 2020 +0900
@@ -1337,11 +1337,14 @@
 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."])
+  AC_CHECK_HEADER([rapidjson/cursorstreamwrapper.h], [have_rapidjson_dev=yes], [have_rapidjson_dev=no
+    rapid_json_warning="RapidJSON 1.1.0 or older found.  Octave will not support the 'PrettyWriter' option in 'jsonencode'."])
 fi
 if test "$have_rapidjson" = yes; then
   AC_DEFINE(HAVE_RAPIDJSON, 1, [Define to 1 if RapidJSON is available.])
+  if test "$have_rapidjson_dev" = yes; then
+    AC_DEFINE(HAVE_RAPIDJSON_DEV, 1, [Define to 1 if the development version of RapidJSON is available.])
+  fi
 else
   OCTAVE_CONFIGURE_WARNING([rapid_json_warning])
 fi
--- a/libinterp/corefcn/jsondecode.cc	Sat Aug 15 23:41:26 2020 -0400
+++ b/libinterp/corefcn/jsondecode.cc	Tue Aug 18 14:44:22 2020 +0900
@@ -499,36 +499,43 @@
 @example
 @group
 jsondecode ('[1, 2, null, 3]')
-    @result{} 1  2  NaN  3
+    @result{} ans =
+
+      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
-                  }
+       @{
+         [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')
+jsondecode ('@{"nu#m#ber": 7, "s#tr#ing": "hi"@}', 'ReplacementStyle', 'delete')
     @result{} scalar structure containing the fields:
-                number =  7
-                string = hi
+
+         number = 7
+         string = hi
 @end group
 
 @group
-jsondecode ('{"1": "one", "2": "two"}', 'Prefix', 'm_')
+jsondecode ('@{"1": "one", "2": "two"@}', 'Prefix', 'm_')
     @result{} scalar structure containing the fields:
-                m_1 = one
-                m_2 = two
+
+         m_1 = one
+         m_2 = two
 @end group
 @end example
 
@@ -538,8 +545,7 @@
 #if defined (HAVE_RAPIDJSON)
 
   int nargin = args.length ();
-  // makeValidName options must be in pairs
-  // The number of arguments must be odd
+  // makeValidName options are pairs, the number of arguments must be odd.
   if (! (nargin % 2))
     print_usage ();
 
@@ -549,24 +555,24 @@
   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
+  // 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 (),
+          static_cast<unsigned int> (d.GetErrorOffset ()),
           rapidjson::GetParseError_En (d.GetParseError ()));
-  return decode (d, args.slice (1, nargin-1));
+
+  return decode (d, args.slice (1, nargin - 1));
 
 #else
 
   octave_unused_parameter (args);
 
-  err_disabled_feature ("jsondecode",
-                        "RapidJSON is required for JSON encoding\\decoding");
+  err_disabled_feature ("jsondecode", "JSON decoding through RapidJSON");
 
 #endif
 }
--- a/libinterp/corefcn/jsonencode.cc	Sat Aug 15 23:41:26 2020 -0400
+++ b/libinterp/corefcn/jsonencode.cc	Tue Aug 18 14:44:22 2020 +0900
@@ -31,13 +31,13 @@
 #include "defun.h"
 #include "error.h"
 #include "errwarn.h"
+#include "oct-string.h"
 #include "ovl.h"
-#include "oct-string.h"
 
 #if defined (HAVE_RAPIDJSON)
-#  include <rapidjson/writer.h>
 #  include <rapidjson/prettywriter.h>
 #  include <rapidjson/stringbuffer.h>
+#  include <rapidjson/writer.h>
 #endif
 
 #if defined (HAVE_RAPIDJSON)
@@ -438,12 +438,12 @@
 
 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
+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.
+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
@@ -459,12 +459,12 @@
 
 @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,
+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
+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
 
@@ -529,20 +529,20 @@
 @example
 @group
 jsonencode ([1 NaN; 3 4])
-@result{}  [[1,null],[3,4]]
+@result{} [[1,null],[3,4]]
 @end group
 
 @group
 jsonencode ([1 NaN; 3 4], "ConvertInfAndNaN", false)
-@result{}  [[1,NaN],[3,4]]
+@result{} [[1,NaN],[3,4]]
 @end group
 
 @group
 jsonencode ([true; false], "ConvertInfAndNaN", false, "PrettyWriter", true)
 @result{} ans = [
-                    true,
-                    false
-                ]
+       true,
+       false
+   ]
 @end group
 
 @group
@@ -552,22 +552,22 @@
 
 @group
 jsonencode (struct ('a', Inf, 'b', [], 'c', struct ()))
-@result{} {"a":null,"b":[],"c":{}}
+@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}]}
+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'}})
+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}
+jsonencode (containers.Map(@{'foo'; 'bar'; 'baz'@}, [1, 2, 3]))
+@result{} @{"bar":2,"baz":3,"foo":1@}
 @end group
 @end example
 
@@ -601,15 +601,29 @@
         error ("jsonencode: Valid options are \'ConvertInfAndNaN\'"
                " and \'PrettyWriter\'");
     }
+
+  // FIXME: RapidJSON 1.1.0 (2016-08-25) is the latest release (2020-08-18)
+  //        and does not support the "PrettyWriter" option.  Once a newer
+  //        RapidJSON version is released and established with major
+  //        distributions, make that version a requirement.
+  #if ! defined (HAVE_RAPIDJSON_DEV)
+     if (PrettyWriter)
+       {
+         warn_disabled_feature ("jsonencode",
+                                "the \'PrettyWriter\' option of RapidJSON");
+         PrettyWriter = false;
+       }
+  #endif
+
   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);
+      #if defined (HAVE_RAPIDJSON_DEV)
+         rapidjson::PrettyWriter<rapidjson::StringBuffer, rapidjson::UTF8<>,
+                                 rapidjson::UTF8<>, rapidjson::CrtAllocator,
+                                 rapidjson::kWriteNanAndInfFlag> writer (json);
+         encode (writer, args(0), ConvertInfAndNaN);
+      #endif
     }
   else
     {
@@ -625,8 +639,7 @@
 
   octave_unused_parameter (args);
 
-  err_disabled_feature ("jsonencode",
-                        "RapidJSON is required for JSON encoding\\decoding");
+  err_disabled_feature ("jsonencode", "JSON encoding through RapidJSON");
 
 #endif
 }