Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/RTL/Delphi/Other RTL    [ Add a report in this area ]  
Report #:  119779   Status: Closed
TJSONString is not JSON compliant. All TJSONObject produce not parsable JSON
Project:  Delphi Build #:  19.0.13476.4176
Version:    19.0 Submitted By:   Daniele Teti
Report Type:  Crash / Data loss / Total failure Date Reported:  10/15/2013 9:09:20 AM
Severity:    Critical / Show Stopper Last Updated: 9/3/2014 5:26:41 AM
Platform:    All platforms Internal Tracking #:   44288
Resolution: Fixed (Resolution Comments) Resolved in Build: : XE7
Duplicate of:  None
Voting and Rating
Overall Rating: (1 Total Rating)
5.00 out of 5
Total Votes: 30
Description
TJSONString is not JSON compliant.

Do not escape correctly the character in ToString method.

Call the attached procedure to see the bug.

I've a path for the problem, but it is not efficient as EMBT coud do.

This bug is reported in http://qc.embarcadero.com/wc/qcmain.aspx?d=103674 but IT IS NOT FIXED

This is my patch (tested in production environment) for TJSONString.ToString method

// FIX DANIELE TETI - 30/08/2013
function TJSONString.ToString: string;
var
  v: string;
begin
  if FStrBuffer <> nil then
  begin
    v := FStrBuffer.ToString;
    v := AnsiReplaceStr(v, '\', '\\'); // MUST BE THE FIRST SUBSTITUTION
    v := AnsiReplaceStr(v, '"', '\"');
    v := AnsiReplaceStr(v, '/', '\/');
    v := AnsiReplaceStr(v, #$8, '\b');
    v := AnsiReplaceStr(v, #$c, '\f');
    v := AnsiReplaceStr(v, #$a, '\n');
    v := AnsiReplaceStr(v, #$d, '\r');
    v := AnsiReplaceStr(v, #$9, '\t');
    Exit('"' + v + '"');
  end;
  Result := NullString;
end;

Steps to Reproduce:
Add this unit to a console project and call procedure Main.

----------------
unit MainU;

interface

uses
  Data.DBXJSON;

procedure Main;

implementation

procedure Main;
var
  OriginalString, OutputString: string;
  jobj: TJSONObject;
begin
  OriginalString := '{"prop":"First row\nSecond row"}';
  jobj := TJSONObject.ParseJSONValue(OriginalString) as TJSONObject;
  OutputString := jobj.ToString;
  jobj.Free;
  writeln(OriginalString);
  writeln(OutputString); // do not escape the \n Other parser cannot parse this JSON
end;

end.
----------------
Workarounds
None
Attachment
None
Comments

Marc Durdin at 10/31/2013 5:12:45 PM -
The workaround given is not valid for Unicode values >= 0x80, or for other control characters < 0x20.

Marc Durdin at 10/31/2013 6:12:16 PM -
A better workaround is to use TJSONBlah.ToBytes, because this appears to be correct even in XE2:

function JSONToString(obj: TJSONAncestor): string;
var
  bytes: TBytes;
  len: Integer;
begin
  SetLength(bytes, obj.EstimatedByteSize);
  len := obj.ToBytes(bytes, 0);
  Result := TEncoding.ANSI.GetString(bytes, 0, len);
end;

http://marc.durdin.net/2013/11/delphis-tjsonstring-tostring-is-broken/

Daniele Teti at 4/8/2014 4:09:11 AM -
Good solution, I'll put it in the TJSONString.ToString

Daniele Teti at 4/8/2014 4:08:38 AM -
This is the solution for this bug...
Open Data.DBXJSON, locate TJSONString.ToString the replace with the following (solution based also on others comments on this report)

function TJSONString.ToString: string;
var
  bytes: TBytes;
  len: Integer;
begin
  if FStrBuffer <> nil then
  begin
    SetLength(bytes, EstimatedByteSize);
    len := ToBytes(bytes, 0);
    Exit(TEncoding.ANSI.GetString(bytes, 0, len));
  // THIS IS THE ORIGINAL VERSION
  //Exit('"' + AnsiReplaceStr(FStrBuffer.ToString, '"', '\"') + '"');
  end;
  Result := NullString;
end;

Daniele Teti at 4/27/2014 6:21:27 AM -
Still open in XE6

Tomohiro Takahashi at 11/19/2014 5:49:42 PM -
This is a comment from internal tracking system.
<<<<<<<<
In XE7, the ToString method is for debug purposes, so a new method is added in base class (TJSONAncestor) to generate a JSON compliant string.
So the user now should use ToJSON instead of ToString.
>>>>>>>>

Server Response from: ETNACODE01