Jump to content

Recommended Posts

I have some code that I inherited from someone else which is used to convert a dataset to JSON.

 

Mostly it works OK, but rather than a £ being converted to \u00A3 it gets converted to \\u00A3 i.e the slash gets escaped again.

 

So from

"Additional Fuel Used 170 Litres @ £2.00 per Litre"

rather than 
 "Additional Fuel Used 170 Litres @ \u00A32.00 per Litre"

I get 

"Additional Fuel Used 170 Litres @ \\u00A32.00 per Litre"

 

I've found very similar code in StackOverflow, but that has the same issue. This is the SO code as it's potentially easier to read. 

Can anyone point out where I/they are going wrong please ?

As far as I can tell the EscapeValue code is correctly converting the £ to \u00A3, but then the TJSONPair.create seems to be then re-escaping the one '\' in to '\\'

 

image.png.52b58df173083a62b9c5e022f379e798.png

 

unit Unit71;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, dbxjson, json;

type
  TForm71 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  type
  TSvJsonString = class(TJSONString)
  private
    function EscapeValue(const AValue: string): string;
  public
    constructor Create(const AValue: string); overload;
  end;

var
  Form71: TForm71;

implementation

{$R *.dfm}

constructor TSvJsonString.Create(const AValue: string);
begin
  inherited Create(EscapeValue(AValue));
end;

function TSvJsonString.EscapeValue(const AValue: string): string;

  procedure AddChars(const AChars: string; var Dest: string; var AIndex: Integer); inline;
  begin
    System.Insert(AChars, Dest, AIndex);
    System.Delete(Dest, AIndex + 2, 1);
    Inc(AIndex, 2);
  end;

  procedure AddUnicodeChars(const AChars: string; var Dest: string; var AIndex: Integer); inline;
  begin
    System.Insert(AChars, Dest, AIndex);
    System.Delete(Dest, AIndex + 6, 1);
    Inc(AIndex, 6);
  end;

var
  i, ix: Integer;
  AChar: Char;
begin
  Result := AValue;
  ix := 1;
  for i := 1 to System.Length(AValue) do
  begin
    AChar :=  AValue[i];
    case AChar of
      '/', '\', '"':
      begin
        System.Insert('\', Result, ix);
        Inc(ix, 2);
      end;
      #8:  //backspace \b
      begin
        AddChars('\b', Result, ix);
      end;
      #9:
      begin
        AddChars('\t', Result, ix);
      end;
      #10:
      begin
        AddChars('\n', Result, ix);
      end;
      #12:
      begin
        AddChars('\f', Result, ix);
      end;
      #13:
      begin
        AddChars('\r', Result, ix);
      end;
      #0 .. #7, #11, #14 .. #31:
      begin
        AddUnicodeChars('\u' + IntToHex(Word(AChar), 4), Result, ix);
      end
      else
      begin
        if Word(AChar) > 127 then
        begin
          AddUnicodeChars('\u' + IntToHex(Word(AChar), 4), Result, ix);
        end
        else
        begin
          Inc(ix);
        end;
      end;
    end;
  end;
end;


procedure TForm71.Button1Click(Sender: TObject);
var
  LText, LEscapedText: string;
  LJsonString: TSvJsonString;
  LJsonPair: TJsonPair;
  LJsonObject: TJsonObject;
begin
  LText := 'The price is £20.00';
  LJsonString := TSvJsonString.Create(LText);
  LJsonPair := TJsonPair.Create('MyString', LJsonString);
  LJsonObject := TJsonObject.Create(LJsonPair);
  try
    LEscapedText := LJsonObject.ToString;
    showmessage(LEscapedText);
    LEscapedText := LJsonObject.ToJSON;
    showmessage(LEscapedText);
  finally
    LJsonObject.Free;
  end;
end;

end.

 

Edited by david_navigator

Share this post


Link to post

None of this should be necessary.

Just using a regular TJsonString outputs 

{"MyString":"The price is £20.00"}
{"MyString":"The price is \u00A320.00"}

Check:

program Project1;

uses System.JSON;

var
	LText, LEscapedText: string;
	LJsonString: TJsonString;
	LJsonPair: TJsonPair;
	LJsonObject: TJsonObject;
begin
	LText := 'The price is £20.00';
	LJsonString := TJsonString.Create(LText); // Not your subclass TSvJsonString
	LJsonPair := TJsonPair.Create('MyString', LJsonString);
	LJsonObject := TJsonObject.Create(LJsonPair);
	try
		LEscapedText := LJsonObject.ToString;
		WriteLn(LEscapedText);
		LEscapedText := LJsonObject.ToJSON;
		WriteLn(LEscapedText);
	finally
		LJsonObject.Free;
	end;
	ReadLn;
end.

 

Share this post


Link to post

 

Quote

 


None of this should be necessary.

Just using a regular TJsonString outputs 

 

Many thanks.

Don't I feel stupid !!

 

Now to do some experimenting to see if all the characters in the above routine get escaped correctly automatically .

 


 

Share this post


Link to post
14 hours ago, david_navigator said:

do some experimenting to see

Don't experiment and then forget about it. Write some proper unit tests so that they will always be with you and your project. 🧐

Share this post


Link to post
 
 

A JSON string must be double-quoted, according to the specs, so you don't need to escape '.
If you have to use special character in your JSON string, you can escape it using \ character.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×