RTC Forums
April 25, 2024, 04:51:57 AM *
Welcome, Guest. Please login or register.

Login with username, password and session length
 
   Home   Help Login Register  
Pages: [1]
  Print  
Author Topic: JSON decode?  (Read 4285 times)
sanche
RTC Expired
*
Posts: 6


« on: April 09, 2018, 09:02:33 AM »

Hi!

Code:
var s: String;
    rec: TRtcRecord;
    FS: TFileStream;
begin
//Create JSON text
rec:=TRtcRecord.Create;
FS:=TFileStream.Create('d:\1.jpg', fmOpenRead);
FS.Position:=0;
rec.NewByteStream('stream').CopyFrom(FS, FS.Size);
FreeAndNil(fs);
s:=rec.toJSON;
FreeAndNil(rec);

//Read JSON Text
rec:=TRtcRecord.FromJSON(s);
ShowMessage(Inttostr(rec.asByteStream['stream'].Size));
FreeAndNil(rec);
end;
Result: Error - Can not convert TRtcTextValue to TStream.

Why error?  Sad

PS: ver. 9.24
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #1 on: April 09, 2018, 10:48:19 AM »

Since JSON does NOT have a native "ByteStream" data type, when you convert a "TRtcRecord" containing a "ByteStream" into JSON, your data previously stored in a "ByteStream" will be converted to a Base64-encoded "String" in JSON, but the original type information will be lost in the process, unless ... you've changed the "RTC_JSON_GenTypedByteStream" global variable from the "rtcInfo.pas" unit to TRUE, in which case a '\/base64\/' prefix would be added to the output "String" to include the original type information in the JSON output.

Without that additional type information (or if the JSON decoder does NOT check for the existence of that additional information), when decoding JSON back into a "TRtcRecord", you get a "String" instead of a "ByteStream". And since there is no automatic conversion from a "String" to a "ByteStream", you get an exception if you try to access that "String" (stored as a TRtcTextValue) into a "ByteStream" (TRtcByteStream, a TStream descendant).

If you do set "RTC_JSON_GenTypedByteStream" to TRUE (in which case the JSON "String" would start with '\/base64\/' if it was encoded from a "ByteStream"), you should also change the "RTC_JSON_ParseTypedByteStream" global variable to TRUE, so the JSON parser will check for that '\/base64\/' prefix and create a "TRtcByteStream" instead of a "TRtcTextValue" when that prefix is found.

In other words, for your code sample (above) to work without raising an exception, the following two lines of code should be executed before running your code, but please keep in mind that these are global variables (so they will affect the entire application/process) and if you are sending that JSON to any 3rd-party, they need to know how to decode that JSON, because this is NOT part of a JSON standard ...

RTC_JSON_GenTypedByteStream:=TRUE;
RTC_JSON_ParseTypedByteStream:=TRUE;

There are quite a few global parameters in the "rtcInfo.pas" unit (all starting with "RTC_JSON_"), which define how the JSON parser and generator included in the RTC SDK should behave, so make sure to check them all if you are using JSON ...

  { JSON Generator:
    Should we escape the "/" character in JSON, converting it into "\/"? (default=TRUE) @html(<br><br>)

    If False, "/" will NOT be changed into "\/" when generating a JSON String.
    This would make the output String shorter, but it will also make it
    *impossible* for plain Strings to have a "\/" anywhere, because
    "/" will remain "/", but "\/" will be converted to "\\/".  @html(<br><br>)

    If True, all "/" characters in a string or name will be sent as "\/". }
  RTC_JSON_GenEscapeSlash:boolean=True;

  { JSON Generator:
    Encode DateTime type as "\/Date(<milliseconds-since-1970-1-1>)\/" in JSON? (default=FALSE) @html(<br><br>)
    When False, asDateTime values will be stored as Strings in JSON, using the ISO DateTime format. }
  RTC_JSON_GenTypedDateTime:boolean=False;

  { JSON Generator:
    Encode Exception type with a "\/error\/" prefix in front of the message string? (default=FALSE) @html(<br><br>)
    When False, asException values will be stored using the JSON-RPC format for Errors '{"error":{"code":...'. }
  RTC_JSON_GenTypedException:boolean=False;

  { JSON Generator:
    Encode ByteStram data with a "\/base64\/" prefix in front of the Mime_Encoded stream? (default=FALSE) @html(<br><br>)
    When False, there will be no prefix, but asByteStream will still be stored as a String using Base64 encoding. }
  RTC_JSON_GenTypedByteStream:boolean=False;

  { JSON Generator:
    Include FunctionName as "\/method" parameter when encoding a Function object to JSON? (default=FALSE) @html(<br><br>)
    When False, asFunction objects will be serialized into JSON based on the "reqVer" parameter. @html(<br>)
    reqVer=0 -> using REST format, FunctionName is NOT serialized (it should be in the URL). @html(<br>)
    reqVer=1 -> serializes Function objects using the JSON-RPC 1.0 format. @html(<br>)
    reqVer=2 -> serializes Function objects using the JSON-RPC 2.0 format. @html(<br>)
    reqVer=3 -> serializes Function objects using a custom compact version of JSON-RPC (minimal overhead). }
  RTC_JSON_GenTypedFunctions:boolean=False;

  {JSON Parser:
    Check for typed DateTime values in "\/date\/" and "\/Date(..)\/" and store them as DateTime fields? (default=FALSE) }
  RTC_JSON_ParseTypedDateTime:boolean=False;

  {JSON Parser:
    Check for typed ByteStream (starts with "\/base64\/") and store them as ByteStream objects? (default=FALSE) }
  RTC_JSON_ParseTypedByteStream:boolean=False;

  {JSON Parser:
    Check for typed Functions (FunctionName in "\/method") and store them as Function objects? (default=FALSE) }
  RTC_JSON_ParseTypedFunctions:boolean=False;

  {JSON Parser:
    Set this to TRUE if you want the "FromJSON" method and "asJSON" properties to
    check if JSON contains '{"method":"any-string" and store it as a RTC Function object. (default=FALSE) }
  RTC_JSON_ParseMethodFunctions:boolean=False;

  {JSON Parser:
    Set this to TRUE if you want to enable support for automatic recognition
    of "JSON-RPC 1.0" objects (functions, errors and results) when using the
    "FromJSON" method or "asJSON" properties with "TRtcValue" objects (default=FALSE). @html(<br><br>)

    NOTE: Also check "RTC_JSON_ParseRPC1Functions", "RTC_JSON_ParseRPC1Errors" and
    "RTC_JSON_ParseRPC1Results" variables if you want to ENABLE "JSON-RPC 1.0" support
    when using "FromJSON" and/or "asJSON" directly with TRtcValue objects (default=FALSE). }
  RTC_JSON_ParseRPC1:boolean=False;

  {JSON Parser:
    Set this to TRUE if you want to enable support for automatic recognition
    of "JSON-RPC 2.0" objects (functions, errors and results) when using the
    "FromJSON" method or "asJSON" properties with "TRtcValue" objects (default=FALSE).

    NOTE: Also check "RTC_JSON_ParseRPC2Functions", "RTC_JSON_ParseRPC2Errors" and
    "RTC_JSON_ParseRPC2Results" variables if you want to ENABLE "JSON-RPC 2.0" support
    when using "FromJSON" and/or "asJSON" directly with TRtcValue objects (default=FALSE). }
  RTC_JSON_ParseRPC2:boolean=False;

  {JSON Parser:
    Set this to TRUE if you want the "FromJSON" method and "asJSON" properties to
    check if JSON contains '{"method":"any-string","params":' followed either by
    '[', '{' or 'null' and store it as a RTC Function object. (default=FALSE) @html(<br><br>)

    NOTE: In addition to this global variable, "RTC_JSON_ParseRPC1" should also be TRUE,
    or nested remote function calls received in "JSON-RPC 1.0" format will NOT be recognized
    by the "TRtcServerModule" and will be handled as plain records (passed as parameters). }
  RTC_JSON_ParseRPC1Functions:boolean=False;

  {JSON Parser:
    Set this to TRUE if you want the "FromJSON" method and "asJSON" properties to
    check if JSON contains '{"error":{' or '{"result":null,"error":{' followed either
    by '"code":', '"message":', '"data":' or '"errors":' and store it as a RTC Exception object. (default=FALSE)  @html(<br><br>)

    NOTE: In addition to this global variable, "RTC_JSON_ParseRPC1" should also be TRUE,
    or nested "error" objects received in "JSON-RPC 1.0" format will be stored as plain records. }
  RTC_JSON_ParseRPC1Errors:boolean=False;

  {JSON Parser:
    Set this to TRUE if you want the "FromJSON" method and "asJSON" properties to
    check if JSON contains '{"result":' - but NOT '{"result":null,"error":{' followed
    by '"code"', '"message"', '"data"' or '"errors"' (which would make it an Exception object)
    and store the contents behind "result" directly as a RTC Value object, instead of storing
    a RTC Record with a sub-record called "result". (default=FALSE) @html(<br><br>)

    NOTE: In addition to this global variable, "RTC_JSON_ParseRPC1" should also be TRUE,
    or nested "JSON-RPC 1.0" results received from the Server will be made accessible as
    plain records containing a "result" element (result data), "error" element
    (in case of an error on the Server) and an "id" element (containing the ID sent). }
  RTC_JSON_ParseRPC1Results:boolean=False;

  {JSON Parser:
    Set this to TRUE if you want the "FromJSON" method and "asJSON" properties to check
    if JSON contains '{"jsonrpc":"2.0","method":"any-string","params":' followed either
    by '[', '{' or 'null' and store it as a RTC Function object. (default=FALSE) @html(<br><br>)

    NOTE: In addition to this global variable, "RTC_JSON_ParseRPC2" should also be TRUE,
    or nested remote function calls received in "JSON-RPC 2.0" format will NOT be recognized
    by the "TRtcServerModule" component and will be handled as records (passed as parameters). }
  RTC_JSON_ParseRPC2Functions:boolean=False;

  {JSON Parser:
    Set this to TRUE if you want the "FromJSON" method and "asJSON" properties to check if
    JSON contains '{"jsonrpc":"2.0","error":{' or '{"jsonrpc":"2.0","result":null,"error":{'
    followed either by '"code":', '"message":', '"data":' or '"errors"' and
    store it as a RTC Exception object. (default=FALSE)  @html(<br><br>)

    NOTE: In addition to this global variable, "RTC_JSON_ParseRPC2" should also be TRUE,
    or nested "error" objects received in "JSON-RPC 2.0" format will be stored as records
    with elements named "jsonrpc", "error" and "id" instead of RTC Exception objects. }
  RTC_JSON_ParseRPC2Errors:boolean=False;

  {JSON Parser:
    Set this to TRUE if you want the "FromJSON" method and "asJSON" properties
    to check if JSON contains '{"jsonrpc":"2.0","result":' - but NOT
    '{"jsonrpc":"2.0","result":null,"error":{' followed by '"code"', '"message"', '"data"' or '"errors"'
    (which would make it an Exception object) and store the contents behind "result"
    as a RTC Value object instead of a RTC Record with a sub-record called "result". (default=FALSE)  @html(<br><br>)

    NOTE: In addition to this global variable, "RTC_JSON_ParseRPC2" should also be TRUE,
    or nested "JSON-RPC 2.0" results received from the Server will be made accessible
    as records containing a "jsonrpc" element ("2.0"), "result" element (result data),
    "error" element (in case of an error) and an "id" element (containing the ID sent). }
  RTC_JSON_ParseRPC2Results:boolean=False;

  {JSON Parser:
    Check for typed DataSets ("\/dsfields" and "\/dsrows" in an record) and store them as DataSet objects? (default=FALSE) }
  RTC_JSON_ParseTypedDataSet:boolean=False;

  {JSON Parser:
    Check for typed Exceptions (starts with "\/error\/") and store them as Exception type? (default=FALSE) }
  RTC_JSON_ParseTypedException:boolean=False;

There are also a few global constants, which are used by the parser and generator. You can NOT change them, but it is good to know about them ...

  // JSON-RPC field name used when sending params to a function as an array rather than a record
  RTC_JSON_ParamsAsArrayName='params';

  // Reserved parameter name for keeping Field definitions when generating JSON for a DataSet object
  RTC_JSON_DataSetFieldsName = '"\/dsfields"';
  // Reserved parameter name for keeping Table rows when generating JSON for a DataSet object
  RTC_JSON_DataSetRowsName   = '"\/dsrows"';
  // Reserved parameter name for "FunctionName" when generating JSON for a FunctionInfo object
  RTC_JSON_FunctionName      = '"\/method"';

  // Opening String for DateTime values encoded as milliseconds since 1970-01-01
  RTC_JSON_DateTimeStr       = '"\/Date(';
  // Opening String for DateTime values encoded using the ISO format
  RTC_JSON_DateTimeISOStr    = '"\/date\/';
  // Opening String for ByteStream objects encoded as Base64
  RTC_JSON_ByteStreamStr     = '"\/base64\/';
  // Opening String for Exception values
  RTC_JSON_ExceptionStr      = '"\/error\/';
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #2 on: April 09, 2018, 11:53:10 AM »

As an alternative, in case you do NOT want to include the '\/base64\/' prefix when converting a "ByteStream" into JSON, you can use this function to write the contents of a Base64-encoded "String" into any "TStream" descendant ...

Code:
// uses rtcInfo, rtcSystem;
function Base64ToStream(const base64:String; stream:TStream):int64;
var
    ar:RtcByteArray;
begin
ar:=Mime_DecodeEx(RtcStringToBytes(base64));
Result:=length(ar);
stream.Write(ar[0],Result);
end;

IOW, without changing the values for RTC_JSON_GenByteStream and RTC_JSON_ParseByteStream variables in the "rtcInfo.pas" unit, you can use the "Base64ToStream" function (code above) to convert a "String" to a "ByteStream", like this ...

Code:
var s: String;
    rec: TRtcRecord;
    FS: TFileStream;
begin
//Create JSON text
rec:=TRtcRecord.Create;
FS:=TFileStream.Create('d:\1.jpg', fmOpenRead);
FS.Position:=0;
rec.NewByteStream('stream').CopyFrom(FS, FS.Size);
FreeAndNil(fs);
s:=rec.toJSON;
FreeAndNil(rec);

//Read JSON Text
rec:=TRtcRecord.FromJSON(s);
if rec.CheckType('stream',rtc_String) then
  Base64ToStream(rec.asString['stream'],rec.newByteStream('stream'));
ShowMessage(Inttostr(rec.asByteStream['stream'].Size));
FreeAndNil(rec);
end;

PS. I've included the type check, so the code above would also work if the JSON parser was modified to recognize the 'stream' field as a ByteStream, in which case there would be no need for a manual type conversion.
Logged
sanche
RTC Expired
*
Posts: 6


« Reply #3 on: April 09, 2018, 01:16:08 PM »

Thanks a lot for help!
Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.029 seconds with 16 queries.