← Back to team overview

zorba-coders team mailing list archive

[Merge] lp:~zorba-coders/zorba/bug-1047547 into lp:zorba

 

Matthias Brantner has proposed merging lp:~zorba-coders/zorba/bug-1047547 into lp:zorba.

Requested reviews:
  Ghislain Fourny (gislenius)
Related bugs:
  Bug #1047547 in Zorba: "jn:parse-json needs options and be able to handle sequences"
  https://bugs.launchpad.net/zorba/+bug/1047547

For more details, see:
https://code.launchpad.net/~zorba-coders/zorba/bug-1047547/+merge/123396

- jn:parse-json to allow multiple top-level items
- added jn:parse-json#2 taking an options parameter
-- 
https://code.launchpad.net/~zorba-coders/zorba/bug-1047547/+merge/123396
Your team Zorba Coders is subscribed to branch lp:zorba.
=== modified file 'include/zorba/pregenerated/diagnostic_list.h'
--- include/zorba/pregenerated/diagnostic_list.h	2012-08-30 13:45:43 +0000
+++ include/zorba/pregenerated/diagnostic_list.h	2012-09-08 01:52:17 +0000
@@ -848,6 +848,8 @@
 extern ZORBA_DLL_PUBLIC JSONiqErrorCode JNTY0003;
 
 extern ZORBA_DLL_PUBLIC JSONiqErrorCode JSDY0040;
+
+extern ZORBA_DLL_PUBLIC JSONiqErrorCode JSDY0041;
 #endif
 
 } // namespace jerr

=== modified file 'modules/org/jsoniq/www/functions.xq'
--- modules/org/jsoniq/www/functions.xq	2012-09-05 12:22:23 +0000
+++ modules/org/jsoniq/www/functions.xq	2012-09-08 01:52:17 +0000
@@ -39,19 +39,49 @@
 
 declare namespace err = "http://www.w3.org/2005/xqt-errors";;
 
+declare namespace jerr = "http://www.jsoniq.org/errors";;
+
 declare namespace ver = "http://www.zorba-xquery.com/options/versioning";;
 declare option ver:module-version "1.0";
 
 
 (:~
- : This function has the same semantics as fn:parse-xml(), except that
- : it parses the string as JSON (not XML), and returns an Object or
- : Array rather than an XML document.
- :
- : @param $j A string containing a valid JSON text.
- : @return a JSON Object or Array item.
- :)
-declare function jn:parse-json($j as xs:string) as json-item()? external;
+ : This function parses a given string as JSON and returns a sequence
+ : of Objects or Arrays.
+ :
+ : Please note that this function allows to parse sequences of whitespace
+ : separated objects and arrays.
+ :
+ : @param $j A string containing a valid JSON text.
+ :
+ : @return A sequence of JSON Object or Array item.
+ :
+ : @error jerr:JSDY0040 if the given string is not valid JSON.
+ :)
+declare function jn:parse-json($j as xs:string) as json-item()* external;
+
+(:~
+ : This function parses a given string as JSON and returns a sequence
+ : of Objects or Arrays.
+ :
+ : @param $j A string containing a valid JSON text.
+ : @param $o A JSON object defining options to configure the parser.
+ : Allowed options are
+ : <ul>
+ :   <li>jsoniq-multiple-top-level-items: allow parsing of sequences of JSON Objects and Arrays (boolean; default: true)</li>
+ : </ul>
+ :
+ : @error jerr:JSDY0040 if the given string is not valid JSON or 
+ :   if jsoniq-multiple-top-level-items is false and there is additionalx
+ :   content after the first JSON Object or Array.
+ : @error jerr:JSDY0041 if the value for the option
+ :   jsoniq-multiple-top-level-items is not of type xs:boolean.
+ :
+ : @return a sequence of JSON Object or Array item.
+ :)
+declare function jn:parse-json(
+  $j as xs:string,
+  $o as object()) as json-item()* external;
 
 
 (:~

=== modified file 'modules/org/jsoniq/www/pregenerated/errors.xq'
--- modules/org/jsoniq/www/pregenerated/errors.xq	2012-08-30 13:45:43 +0000
+++ modules/org/jsoniq/www/pregenerated/errors.xq	2012-09-08 01:52:17 +0000
@@ -141,4 +141,9 @@
 (:~
  :parser errors raised by the JSONIQLoader
 :)
-declare variable $jerr:JSDY0040 as xs:QName := fn:QName($jerr:NS, "jerr:JSDY0040");
\ No newline at end of file
+declare variable $jerr:JSDY0040 as xs:QName := fn:QName($jerr:NS, "jerr:JSDY0040");
+
+(:~
+ :parser error for invalid option type
+:)
+declare variable $jerr:JSDY0041 as xs:QName := fn:QName($jerr:NS, "jerr:JSDY0041");
\ No newline at end of file

=== modified file 'src/diagnostics/diagnostic_en.xml'
--- src/diagnostics/diagnostic_en.xml	2012-08-31 19:53:50 +0000
+++ src/diagnostics/diagnostic_en.xml	2012-09-08 01:52:17 +0000
@@ -2700,6 +2700,11 @@
       <value>$1</value>
     </diagnostic>
 
+    <diagnostic code="JSDY0041" if="defined(ZORBA_WITH_JSON)">
+      <comment>parser error for invalid option type</comment>
+      <value>$1: invalid option type for option $2 (expected $3)</value>
+    </diagnostic>
+
   </namespace>
 
   <!--////////// Zorba Warnings ////////////////////////////////////////////-->
@@ -3947,6 +3952,10 @@
       <value>unterminated JSON string${ at 2}</value>
     </entry>
 
+    <entry key="JSON_UNEXPECTED_EXTRA_CONTENT">
+      <value>unexpected extra content at the end of the document (consider using the jsoniq-multiple-top-level-items option)</value>
+    </entry>
+
   </subvalues>
 
 </diagnostic-list>

=== modified file 'src/diagnostics/pregenerated/diagnostic_list.cpp'
--- src/diagnostics/pregenerated/diagnostic_list.cpp	2012-08-30 13:45:43 +0000
+++ src/diagnostics/pregenerated/diagnostic_list.cpp	2012-09-08 01:52:17 +0000
@@ -1247,6 +1247,9 @@
 
 
 JSONiqErrorCode JSDY0040( "JSDY0040" );
+
+
+JSONiqErrorCode JSDY0041( "JSDY0041" );
 #endif
 
 

=== modified file 'src/diagnostics/pregenerated/dict_en.cpp'
--- src/diagnostics/pregenerated/dict_en.cpp	2012-08-31 19:53:50 +0000
+++ src/diagnostics/pregenerated/dict_en.cpp	2012-09-08 01:52:17 +0000
@@ -154,6 +154,9 @@
 #if defined(ZORBA_WITH_JSON)
   { "JSDY0040", "$1" },
 #endif
+#if defined(ZORBA_WITH_JSON)
+  { "JSDY0041", "$1: invalid option type for option $2 (expected $3)" },
+#endif
   { "SENR0001", "\"$1\": can not serialize $2" },
   { "SEPM0004", "doctype-system parameter, or standalone parameter with a value other than \"omit\", specified" },
   { "SEPM0009", "omit-xml-declaration parameter is \"yes\" and $1" },
@@ -607,6 +610,7 @@
   { "~JSON_ILLEGAL_ESCAPE", "'\\$2': illegal JSON character escape${ at 3}" },
   { "~JSON_ILLEGAL_LITERAL", "illegal JSON literal${ at 2}" },
   { "~JSON_ILLEGAL_NUMBER", "illegal JSON number${ at 2}" },
+  { "~JSON_UNEXPECTED_EXTRA_CONTENT", "unexpected extra content at the end of the document (consider using the jsoniq-multiple-top-level-items option)" },
   { "~JSON_UNEXPECTED_TOKEN", "\"$2\": unexpected JSON token${ at 3}" },
   { "~JSON_UNTERMINATED_STRING", "unterminated JSON string${ at 2}" },
   { "~JSONiq dynamic error", "JSONIQ dynamic error" },

=== modified file 'src/functions/pregenerated/func_jsoniq_functions.cpp'
--- src/functions/pregenerated/func_jsoniq_functions.cpp	2012-08-30 13:45:43 +0000
+++ src/functions/pregenerated/func_jsoniq_functions.cpp	2012-09-08 01:52:17 +0000
@@ -202,8 +202,8 @@
       {
     DECL_WITH_KIND(sctx, fn_jsoniq_parse_json,
         (createQName("http://www.jsoniq.org/functions","","parse-json";), 
-        GENV_TYPESYSTEM.STRING_TYPE_ONE, 
-        GENV_TYPESYSTEM.JSON_ITEM_TYPE_QUESTION),
+        GENV_TYPESYSTEM.STRING_TYPE_QUESTION, 
+        GENV_TYPESYSTEM.JSON_ITEM_TYPE_STAR),
         FunctionConsts::FN_JSONIQ_PARSE_JSON_1);
 
   }
@@ -216,6 +216,23 @@
 
 
       {
+    DECL_WITH_KIND(sctx, fn_jsoniq_parse_json,
+        (createQName("http://www.jsoniq.org/functions","","parse-json";), 
+        GENV_TYPESYSTEM.STRING_TYPE_QUESTION, 
+        GENV_TYPESYSTEM.JSON_OBJECT_TYPE_ONE, 
+        GENV_TYPESYSTEM.JSON_ITEM_TYPE_STAR),
+        FunctionConsts::FN_JSONIQ_PARSE_JSON_2);
+
+  }
+
+
+#endif
+
+
+#ifdef ZORBA_WITH_JSON
+
+
+      {
     DECL_WITH_KIND(sctx, fn_jsoniq_keys,
         (createQName("http://www.jsoniq.org/functions","","keys";), 
         GENV_TYPESYSTEM.JSON_OBJECT_TYPE_ONE, 

=== modified file 'src/functions/pregenerated/function_enum.h'
--- src/functions/pregenerated/function_enum.h	2012-08-30 13:45:43 +0000
+++ src/functions/pregenerated/function_enum.h	2012-09-08 01:52:17 +0000
@@ -226,6 +226,7 @@
   FN_ZORBA_JSON_PARSE_INTERNAL_2,
   FN_ZORBA_JSON_SERIALIZE_INTERNAL_2,
   FN_JSONIQ_PARSE_JSON_1,
+  FN_JSONIQ_PARSE_JSON_2,
   FN_JSONIQ_KEYS_1,
   FN_JSONIQ_VALUE_2,
   FN_JSONIQ_PROJECT_2,

=== modified file 'src/runtime/json/jsoniq_functions_impl.cpp'
--- src/runtime/json/jsoniq_functions_impl.cpp	2012-08-30 13:45:43 +0000
+++ src/runtime/json/jsoniq_functions_impl.cpp	2012-09-08 01:52:17 +0000
@@ -53,52 +53,125 @@
 /*******************************************************************************
 
 ********************************************************************************/
+void
+JSONParseIteratorState::init(PlanState& aState)
+{
+  PlanIteratorState::init(aState);
+  theAllowMultiple = true; // default
+  theInputStream = 0;
+  theGotOne = false;
+}
+
+void
+JSONParseIteratorState::reset(PlanState& aState)
+{
+  PlanIteratorState::reset(aState);
+  if (theInput == NULL && theInputStream)
+  {
+    delete theInputStream;
+  }
+}
+
+JSONParseIteratorState::~JSONParseIteratorState()
+{
+  if (theInput == NULL && theInputStream)
+  {
+    delete theInputStream;
+  }
+}
+
 bool
 JSONParseIterator::nextImpl(
   store::Item_t& result,
   PlanState& planState) const
 {
   store::Item_t lInput;
+  store::Item_t lOptions;
 
-  PlanIteratorState* state;
-  DEFAULT_STACK_INIT(PlanIteratorState, state, planState);
+  JSONParseIteratorState* state;
+  DEFAULT_STACK_INIT(JSONParseIteratorState, state, planState);
 
   consumeNext(lInput, theChildren[0].getp(), planState);
+  if (theChildren.size() == 2)
+  {
+    consumeNext(lOptions, theChildren[1].getp(), planState);
+
+    store::Item_t lOptionName, lOptionValue;
+
+    zstring s("jsoniq-multiple-top-level-items");
+    GENV_ITEMFACTORY->createString(lOptionName, s);
+    lOptionValue = lOptions->getObjectValue(lOptionName);
+    if (lOptionValue != NULL)
+    {
+      store::SchemaTypeCode lType = lOptionValue->getTypeCode();
+      if (!TypeOps::is_subtype(lType, store::XS_BOOLEAN))
+      {
+        const TypeManager* tm = theSctx->get_typemanager();
+        xqtref_t lType = tm->create_value_type(lOptionValue, loc);
+        RAISE_ERROR(jerr::JSDY0041, loc, 
+        ERROR_PARAMS(lType->toSchemaString(), s, "xs:boolean"));
+      }
+      state->theAllowMultiple = lOptionValue->getBooleanValue();
+    }
+  }
 
   if (lInput->isStreamable())
   {
-    result = GENV_STORE.parseJSON(lInput->getStream(), 0);
+    state->theInput = lInput;
+    state->theInputStream = &lInput->getStream();
   }
   else
   {
-    std::stringstream lStr;
-    lStr << lInput->getStringValue();
-
-    if (theRelativeLocation == QueryLoc::null)
-    {
-      try
-      {
-        result = GENV_STORE.parseJSON(lStr, 0);
-      }
-      catch (zorba::ZorbaException& e)
-      {
-        set_source(e, theChildren[0]->getLocation());
-        throw;
-      }
-    }
-    else
-    {
-      // pass the query location of the StringLiteral to the JSON
-      // parser such that it can give better error locations.
-      // Also, parseJSON already raises an XQueryException with the
-      // location. Hence, no need to catch and rethrow the exception here
-      zorba::internal::diagnostic::location lLoc;
-      lLoc = ERROR_LOC(theRelativeLocation);
-      result = GENV_STORE.parseJSON(lStr, &lLoc);
-    }
-  }
-
-  STACK_PUSH(true, state);
+    state->theInputStream = new std::stringstream(
+        lInput->getStringValue().c_str());
+  }
+
+  while (true)
+  {
+    if (state->theInput != NULL)
+    {
+      result = GENV_STORE.parseJSON(*state->theInputStream, 0);
+    }
+    else
+    {
+      if (theRelativeLocation == QueryLoc::null)
+      {
+        try
+        {
+          result = GENV_STORE.parseJSON(*state->theInputStream, 0);
+        }
+        catch (zorba::ZorbaException& e)
+        {
+          set_source(e, theChildren[0]->getLocation());
+          throw;
+        }
+      }
+      else
+      {
+        // pass the query location of the StringLiteral to the JSON
+        // parser such that it can give better error locations.
+        // Also, parseJSON already raises an XQueryException with the
+        // location. Hence, no need to catch and rethrow the exception here
+        zorba::internal::diagnostic::location lLoc;
+        lLoc = ERROR_LOC(theRelativeLocation);
+        result = GENV_STORE.parseJSON(*state->theInputStream, &lLoc);
+      }
+    }
+    if (result != NULL)
+    {
+      if (!state->theAllowMultiple && state->theGotOne)
+      {
+        RAISE_ERROR(jerr::JSDY0040, loc, 
+        ERROR_PARAMS(ZED(JSON_UNEXPECTED_EXTRA_CONTENT)));
+      }
+      state->theGotOne = true;
+      STACK_PUSH(true, state);
+    }
+    else
+    {
+      break;
+    }
+  }
 
   STACK_END(state);
 }

=== modified file 'src/runtime/json/pregenerated/jsoniq_functions.cpp'
--- src/runtime/json/pregenerated/jsoniq_functions.cpp	2012-08-30 13:45:43 +0000
+++ src/runtime/json/pregenerated/jsoniq_functions.cpp	2012-09-08 01:52:17 +0000
@@ -39,7 +39,7 @@
 void JSONParseIterator::serialize(::zorba::serialization::Archiver& ar)
 {
   serialize_baseclass(ar,
-  (NaryBaseIterator<JSONParseIterator, PlanIteratorState>*)this);
+  (NaryBaseIterator<JSONParseIterator, JSONParseIteratorState>*)this);
 
     ar & theRelativeLocation;
 }
@@ -60,6 +60,8 @@
 
 JSONParseIterator::~JSONParseIterator() {}
 
+JSONParseIteratorState::JSONParseIteratorState() {}
+
 // </JSONParseIterator>
 
 #endif

=== modified file 'src/runtime/json/pregenerated/jsoniq_functions.h'
--- src/runtime/json/pregenerated/jsoniq_functions.h	2012-08-30 13:45:43 +0000
+++ src/runtime/json/pregenerated/jsoniq_functions.h	2012-09-08 01:52:17 +0000
@@ -38,7 +38,23 @@
  * 
  * Author: 
  */
-class JSONParseIterator : public NaryBaseIterator<JSONParseIterator, PlanIteratorState>
+class JSONParseIteratorState : public PlanIteratorState
+{
+public:
+  bool theAllowMultiple; //
+  store::Item_t theInput; //
+  std::istream* theInputStream; //
+  bool theGotOne; //
+
+  JSONParseIteratorState();
+
+  ~JSONParseIteratorState();
+
+  void init(PlanState&);
+  void reset(PlanState&);
+};
+
+class JSONParseIterator : public NaryBaseIterator<JSONParseIterator, JSONParseIteratorState>
 { 
 protected:
   QueryLoc theRelativeLocation; //
@@ -46,7 +62,7 @@
   SERIALIZABLE_CLASS(JSONParseIterator);
 
   SERIALIZABLE_CLASS_CONSTRUCTOR2T(JSONParseIterator,
-    NaryBaseIterator<JSONParseIterator, PlanIteratorState>);
+    NaryBaseIterator<JSONParseIterator, JSONParseIteratorState>);
 
   void serialize( ::zorba::serialization::Archiver& ar);
 
@@ -56,7 +72,7 @@
     std::vector<PlanIter_t>& children,
     QueryLoc aRelativeLocation)
     : 
-    NaryBaseIterator<JSONParseIterator, PlanIteratorState>(sctx, loc, children),
+    NaryBaseIterator<JSONParseIterator, JSONParseIteratorState>(sctx, loc, children),
     theRelativeLocation(aRelativeLocation)
   {}
 

=== modified file 'src/runtime/spec/json/jsoniq_functions.xml'
--- src/runtime/spec/json/jsoniq_functions.xml	2012-08-30 13:45:43 +0000
+++ src/runtime/spec/json/jsoniq_functions.xml	2012-09-08 01:52:17 +0000
@@ -18,8 +18,14 @@
 
   <zorba:function isDeterministic="true" generateCodegen="false">
     <zorba:signature localname="parse-json" prefix="fn-jsoniq">
-      <zorba:param>xs:string</zorba:param>
-      <zorba:output>json-item()?</zorba:output>
+      <zorba:param>xs:string?</zorba:param>
+      <zorba:output>json-item()*</zorba:output>
+    </zorba:signature>
+
+    <zorba:signature localname="parse-json" prefix="fn-jsoniq">
+      <zorba:param>xs:string?</zorba:param>
+      <zorba:param>object()</zorba:param>
+      <zorba:output>json-item()*</zorba:output>
     </zorba:signature>
 
     <zorba:methods>
@@ -29,6 +35,13 @@
 
   </zorba:function>
 
+  <zorba:state generateInit="false" generateReset="false" generateDestructor="false">
+    <zorba:member type="bool" name="theAllowMultiple" brief=""/>
+    <zorba:member type="store::Item_t" name="theInput" brief=""/>
+    <zorba:member type="std::istream*" name="theInputStream" brief=""/>
+    <zorba:member type="bool" name="theGotOne"/>
+  </zorba:state>
+
   <zorba:constructor>
     <zorba:parameter type="QueryLoc" name="aRelativeLocation" />
   </zorba:constructor>


Follow ups