viniciussanchez / dataset-serialize

JSON to DataSet and DataSet to JSON converter for Delphi and Lazarus (FPC)
MIT License
653 stars 161 forks source link

Numerical conversion issue #112

Closed Rollo62 closed 2 years ago

Rollo62 commented 3 years ago

Hi Vinicius,

I found a serious problem when converting the numerical numbers, e.g. from German locale. There is set "," comma as separator by default, while I assume JSON only allows "." dot. In US/ES this is maybe OK, but in many locales this will break into an exception.

Your Example Dataset.Serialize samples\delphi\basic\basic.dproj shows the issue when testing "Load empty dataset".

See following workaround below. Use the already existing FormatSetting, which is defined for datetime right now only.

`unit DataSet.Serialize.Import;

...

procedure TJSONSerialize.JSONObjectToDataSet(const AJSONObject: TJSONObject; const ADataSet: TDataSet; const ADetail: Boolean);

...

          TFieldType.ftFloat, TFieldType.ftFMTBcd, TFieldType.ftBCD{$IF NOT DEFINED(FPC)}, TFieldType.ftSingle{$ENDIF}:
          begin //S4: 22.09.21 Added Decimal enforcement
            LFormatSettings.DecimalSeparator := '.'; //S4: Added
            LField.AsFloat := StrToFloat(LJSONValue.Value, LFormatSettings ); // S4: Added LFormatSettings
          end;
`

moreover, to avoid setting the DecimalSeparator every time, I would propose an optimization to do this only once, at the head of the function.

Optimized, to set the FormatSettings only once, for Float and DateTime. As compromize only set DecimalSeparator once, as below.

`procedure TJSONSerialize.JSONObjectToDataSet(const AJSONObject: TJSONObject; const ADataSet: TDataSet; const ADetail: Boolean);

...

begin
  if (not Assigned(AJSONObject)) or (not Assigned(ADataSet)) or (AJSONObject.Count = 0) then
    Exit;

  //S4: Optimized
  LFormatSettings.DecimalSeparator := '.';

...

          TFieldType.ftFloat, TFieldType.ftFMTBcd, TFieldType.ftBCD{$IF NOT DEFINED(FPC)}, TFieldType.ftSingle{$ENDIF}:
          begin //S4: 22.09.21 Added Decimal enforcement
//S4: Optimized            LFormatSettings.DecimalSeparator := '.'; //S4: Added
            LField.AsFloat := StrToFloat(LJSONValue.Value, LFormatSettings ); // S4: Added LFormatSettings
          end;
          TFieldType.ftString, TFieldType.ftWideString, TFieldType.ftMemo, TFieldType.ftWideMemo, TFieldType.ftGuid, TFieldType.ftFixedChar, TFieldType.ftFixedWideChar:
            LField.AsString := LJSONValue.Value;
          TFieldType.ftDate:
             LField.AsDateTime := DateOf(ISO8601ToDate(LJSONValue.Value, TDataSetSerializeConfig.GetInstance.DateInputIsUTC));
          TFieldType.ftTimeStamp, TFieldType.ftDateTime:
             LField.AsDateTime := ISO8601ToDate(LJSONValue.Value, TDataSetSerializeConfig.GetInstance.DateInputIsUTC);
          TFieldType.ftTime:
          begin
             LFormatSettings.TimeSeparator := ':';
//S4: Optimized             LFormatSettings.DecimalSeparator := '.';
             LFormatSettings.ShortTimeFormat := 'hh:mm:ss.zzz';
             LField.AsDateTime := StrToTime(LJSONValue.Value, LFormatSettings);
          end;
`
carlospo commented 2 years ago

This solution solved 100% thanks

viniciussanchez commented 2 years ago

A configuration has been created. The default value is the point: TDataSetSerializeConfig.GetInstance.Import.DecimalSeparator