Types-JSON
This commit is contained in:
+313
-207
@@ -10,6 +10,31 @@ uses
|
|||||||
type
|
type
|
||||||
// Provides methods for serializing and deserializing data values to/from JSON using the TDataType helpers.
|
// Provides methods for serializing and deserializing data values to/from JSON using the TDataType helpers.
|
||||||
TDataJsonConverter = class
|
TDataJsonConverter = class
|
||||||
|
private
|
||||||
|
// Serialization helpers for specific data types
|
||||||
|
class function OrdinalToJson(const Value: TDataType.TOrdinal.TValue): TJSONValue; static;
|
||||||
|
class function FloatToJson(const Value: TDataType.TFloat.TValue): TJSONValue; static;
|
||||||
|
class function TextToJson(const Value: TDataType.TText.TValue): TJSONValue; static;
|
||||||
|
class function TimestampToJson(const Value: TDataType.TTimestamp.TValue): TJSONValue; static;
|
||||||
|
class function DecimalToJson(const Value: TDataType.TDecimal.TValue): TJSONValue; static;
|
||||||
|
class function EnumToJson(const Value: TDataType.TEnum.TValue): TJSONValue; static;
|
||||||
|
class function RecordToJson(const Value: TDataType.TRecord.TValue): TJSONObject; static;
|
||||||
|
class function TupleToJson(const Value: TDataType.TTuple.TValue): TJSONArray; static;
|
||||||
|
class function ArrayToJson(const Value: TDataType.TArray.TValue): TJSONArray; static;
|
||||||
|
class function VectorToJson(const Value: TDataType.TVector.TValue): TJSONArray; static;
|
||||||
|
|
||||||
|
// Deserialization helpers for specific data types
|
||||||
|
class function JsonToOrdinal(const Json: TJSONValue; const DataType: TDataType): TDataType.TOrdinal.TValue; static;
|
||||||
|
class function JsonToFloat(const Json: TJSONValue; const DataType: TDataType): TDataType.TFloat.TValue; static;
|
||||||
|
class function JsonToText(const Json: TJSONValue; const DataType: TDataType): TDataType.TText.TValue; static;
|
||||||
|
class function JsonToTimestamp(const Json: TJSONValue; const DataType: TDataType): TDataType.TTimestamp.TValue; static;
|
||||||
|
class function JsonToDecimal(const Json: TJSONValue; const DataType: TDataType): TDataType.TDecimal.TValue; static;
|
||||||
|
class function JsonToEnum(const Json: TJSONValue; const DataType: TDataType): TDataType.TEnum.TValue; static;
|
||||||
|
class function JsonToRecord(const Json: TJSONObject; const DataType: TDataType): TDataType.TRecord.TValue; static;
|
||||||
|
class function JsonToTuple(const Json: TJSONArray; const DataType: TDataType): TDataType.TTuple.TValue; static;
|
||||||
|
class function JsonToArray(const Json: TJSONArray; const DataType: TDataType): TDataType.TArray.TValue; static;
|
||||||
|
class function JsonToVector(const Json: TJSONArray; const DataType: TDataType): TDataType.TVector.TValue; static;
|
||||||
|
|
||||||
public
|
public
|
||||||
// Serializes a data value to a TJSONValue.
|
// Serializes a data value to a TJSONValue.
|
||||||
class function ValueToJson(const Value: TDataType.TValue): TJSONValue;
|
class function ValueToJson(const Value: TDataType.TValue): TJSONValue;
|
||||||
@@ -26,92 +51,290 @@ uses
|
|||||||
System.DateUtils,
|
System.DateUtils,
|
||||||
System.Math;
|
System.Math;
|
||||||
|
|
||||||
{ TDataJsonConverter }
|
class function TDataJsonConverter.OrdinalToJson(const Value: TDataType.TOrdinal.TValue): TJSONValue;
|
||||||
|
begin
|
||||||
|
Result := TJSONNumber.Create(Value.Value);
|
||||||
|
end;
|
||||||
|
|
||||||
class function TDataJsonConverter.ValueToJson(const Value: TDataType.TValue): TJSONValue;
|
class function TDataJsonConverter.FloatToJson(const Value: TDataType.TFloat.TValue): TJSONValue;
|
||||||
|
begin
|
||||||
|
Result := TJSONNumber.Create(Value.Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.TextToJson(const Value: TDataType.TText.TValue): TJSONValue;
|
||||||
|
begin
|
||||||
|
Result := TJSONString.Create(Value.Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.TimestampToJson(const Value: TDataType.TTimestamp.TValue): TJSONValue;
|
||||||
|
begin
|
||||||
|
Result := TJSONString.Create(DateToISO8601(Value.Value, true));
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.DecimalToJson(const Value: TDataType.TDecimal.TValue): TJSONValue;
|
||||||
|
var
|
||||||
|
val: Int64;
|
||||||
|
scale: Integer;
|
||||||
|
s: string;
|
||||||
|
begin
|
||||||
|
// Serialize as string to preserve precision.
|
||||||
|
val := Value.Value;
|
||||||
|
scale := Value.DataType.Scale;
|
||||||
|
|
||||||
|
if (scale = 0) then
|
||||||
|
s := IntToStr(val)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
s := IntToStr(Abs(val));
|
||||||
|
if (Length(s) <= scale) then
|
||||||
|
s := StringOfChar('0', scale - Length(s) + 1) + s;
|
||||||
|
|
||||||
|
s := s.Insert(Length(s) - scale, '.');
|
||||||
|
|
||||||
|
if (val < 0) then
|
||||||
|
s := '-' + s;
|
||||||
|
end;
|
||||||
|
Result := TJSONString.Create(s);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.EnumToJson(const Value: TDataType.TEnum.TValue): TJSONValue;
|
||||||
|
begin
|
||||||
|
Result := TJSONString.Create(Value.DataType.Identifiers[Value.Value]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.RecordToJson(const Value: TDataType.TRecord.TValue): TJSONObject;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
recordType: TDataType.TRecord;
|
||||||
|
begin
|
||||||
|
recordType := Value.DataType;
|
||||||
|
Result := TJSONObject.Create;
|
||||||
|
for i := 0 to recordType.FieldCount - 1 do
|
||||||
|
Result.AddPair(recordType.Fields[i].Name, ValueToJson(Value.Items[i]));
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.TupleToJson(const Value: TDataType.TTuple.TValue): TJSONArray;
|
||||||
var
|
var
|
||||||
jsonObject: TJSONObject;
|
|
||||||
jsonArray: TJSONArray;
|
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
if not Assigned(Value.DataValue) then
|
Result := TJSONArray.Create;
|
||||||
begin
|
for i := 0 to Value.ItemCount - 1 do
|
||||||
Result := TJSONNull.Create;
|
Result.AddElement(ValueToJson(Value.Items[i]));
|
||||||
Exit;
|
end;
|
||||||
end;
|
|
||||||
|
|
||||||
|
class function TDataJsonConverter.ArrayToJson(const Value: TDataType.TArray.TValue): TJSONArray;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
Result := TJSONArray.Create;
|
||||||
|
for i := 0 to Value.ElementCount - 1 do
|
||||||
|
Result.AddElement(ValueToJson(Value.Items[i]));
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.VectorToJson(const Value: TDataType.TVector.TValue): TJSONArray;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
Result := TJSONArray.Create;
|
||||||
|
for i := 0 to Value.ElementCount - 1 do
|
||||||
|
Result.AddElement(ValueToJson(Value.Items[i]));
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDataJsonConverter - Deserialization Helpers }
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToOrdinal(const Json: TJSONValue; const DataType: TDataType): TDataType.TOrdinal.TValue;
|
||||||
|
var
|
||||||
|
ordinalType: TDataType.TOrdinal;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkOrdinal);
|
||||||
|
ordinalType := DataType.AsOrdinal;
|
||||||
|
Result := ordinalType.CreateValue((Json as TJSONNumber).AsInt64);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToFloat(const Json: TJSONValue; const DataType: TDataType): TDataType.TFloat.TValue;
|
||||||
|
var
|
||||||
|
floatType: TDataType.TFloat;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkFloat);
|
||||||
|
floatType := DataType.AsFloat;
|
||||||
|
Result := floatType.CreateValue((Json as TJSONNumber).AsDouble);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToText(const Json: TJSONValue; const DataType: TDataType): TDataType.TText.TValue;
|
||||||
|
var
|
||||||
|
textType: TDataType.TText;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkText);
|
||||||
|
textType := DataType.AsText;
|
||||||
|
Result := textType.CreateValue((Json as TJSONString).Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToTimestamp(const Json: TJSONValue; const DataType: TDataType): TDataType.TTimestamp.TValue;
|
||||||
|
var
|
||||||
|
timestampType: TDataType.TTimestamp;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkTimestamp);
|
||||||
|
timestampType := DataType.AsTimestamp;
|
||||||
|
Result := timestampType.CreateValue(ISO8601ToDate((Json as TJSONString).Value, True));
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToDecimal(const Json: TJSONValue; const DataType: TDataType): TDataType.TDecimal.TValue;
|
||||||
|
var
|
||||||
|
val: Int64;
|
||||||
|
i: Integer;
|
||||||
|
decimalType: TDataType.TDecimal;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkDecimal);
|
||||||
|
decimalType := DataType.AsDecimal;
|
||||||
|
|
||||||
|
// Handle string representation for precision, with fallback to number for compatibility.
|
||||||
|
if (Json is TJSONString) then
|
||||||
|
begin
|
||||||
|
var s := (Json as TJSONString).Value.Trim;
|
||||||
|
var isNegative := s.StartsWith('-');
|
||||||
|
if isNegative then
|
||||||
|
s := s.Remove(0, 1); // remove sign
|
||||||
|
|
||||||
|
var dotPos := Pos('.', s);
|
||||||
|
var numFracDigits: Integer;
|
||||||
|
if (dotPos = 0) then
|
||||||
|
numFracDigits := 0
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
numFracDigits := Length(s) - dotPos;
|
||||||
|
s := s.Remove(dotPos - 1, 1); // remove dot
|
||||||
|
end;
|
||||||
|
|
||||||
|
val := StrToInt64(s);
|
||||||
|
|
||||||
|
var scaleDiff := decimalType.Scale - numFracDigits;
|
||||||
|
if (scaleDiff > 0) then // Not enough fractional digits
|
||||||
|
begin
|
||||||
|
for i := 1 to scaleDiff do
|
||||||
|
val := val * 10;
|
||||||
|
end
|
||||||
|
else if (scaleDiff < 0) then // Too many fractional digits
|
||||||
|
begin
|
||||||
|
for i := 1 to -scaleDiff do
|
||||||
|
val := val div 10;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if isNegative then
|
||||||
|
val := -val;
|
||||||
|
|
||||||
|
Result := decimalType.CreateValue(val);
|
||||||
|
end
|
||||||
|
else if (Json is TJSONNumber) then
|
||||||
|
begin
|
||||||
|
// Support numbers for compatibility, but this can lead to precision loss.
|
||||||
|
val := Round((Json as TJSONNumber).AsDouble * Power(10, decimalType.Scale));
|
||||||
|
Result := decimalType.CreateValue(val);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise EConvertError.Create('JSON String or Number expected for Decimal type');
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToEnum(const Json: TJSONValue; const DataType: TDataType): TDataType.TEnum.TValue;
|
||||||
|
var
|
||||||
|
idx: Integer;
|
||||||
|
enumType: TDataType.TEnum;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkEnum);
|
||||||
|
enumType := DataType.AsEnum;
|
||||||
|
|
||||||
|
idx := enumType.IndexOf((Json as TJSONString).Value);
|
||||||
|
if (idx < 0) then
|
||||||
|
raise EConvertError.CreateFmt('Invalid enum identifier "%s" for type %s', [Json.Value, DataType.Name]);
|
||||||
|
Result := enumType.CreateValue(idx);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToRecord(const Json: TJSONObject; const DataType: TDataType): TDataType.TRecord.TValue;
|
||||||
|
var
|
||||||
|
items: TArray<IDataValue>;
|
||||||
|
i: Integer;
|
||||||
|
recordType: TDataType.TRecord;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkRecord);
|
||||||
|
recordType := DataType.AsRecord;
|
||||||
|
|
||||||
|
SetLength(items, recordType.FieldCount);
|
||||||
|
for i := 0 to recordType.FieldCount - 1 do
|
||||||
|
begin
|
||||||
|
var field := recordType.Fields[i];
|
||||||
|
var jsonPair := Json.Get(field.Name);
|
||||||
|
if Assigned(jsonPair) then
|
||||||
|
items[i] := JsonToValue(jsonPair.JsonValue, field.DataType);
|
||||||
|
end;
|
||||||
|
Result := recordType.CreateValue(items);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToTuple(const Json: TJSONArray; const DataType: TDataType): TDataType.TTuple.TValue;
|
||||||
|
var
|
||||||
|
items: TArray<IDataValue>;
|
||||||
|
i: Integer;
|
||||||
|
tupleType: TDataType.TTuple;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkTuple);
|
||||||
|
tupleType := DataType.AsTuple;
|
||||||
|
|
||||||
|
if (Json.Count <> tupleType.ItemCount) then
|
||||||
|
raise EConvertError.CreateFmt('JSON array size (%d) does not match tuple size (%d)', [Json.Count, tupleType.ItemCount]);
|
||||||
|
|
||||||
|
SetLength(items, tupleType.ItemCount);
|
||||||
|
for i := 0 to tupleType.ItemCount - 1 do
|
||||||
|
items[i] := JsonToValue(Json.Items[i], tupleType.ItemType[i]);
|
||||||
|
Result := tupleType.CreateValue(items);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToArray(const Json: TJSONArray; const DataType: TDataType): TDataType.TArray.TValue;
|
||||||
|
var
|
||||||
|
items: TArray<IDataValue>;
|
||||||
|
i: Integer;
|
||||||
|
arrayType: TDataType.TArray;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkArray);
|
||||||
|
arrayType := DataType.AsArray;
|
||||||
|
|
||||||
|
SetLength(items, Json.Count);
|
||||||
|
for i := 0 to Json.Count - 1 do
|
||||||
|
items[i] := JsonToValue(Json.Items[i], arrayType.ElementType);
|
||||||
|
Result := arrayType.CreateValue(items);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TDataJsonConverter.JsonToVector(const Json: TJSONArray; const DataType: TDataType): TDataType.TVector.TValue;
|
||||||
|
var
|
||||||
|
items: TArray<IDataValue>;
|
||||||
|
i: Integer;
|
||||||
|
vectorType: TDataType.TVector;
|
||||||
|
begin
|
||||||
|
Assert(DataType.Kind = dkVector);
|
||||||
|
vectorType := DataType.AsVector;
|
||||||
|
|
||||||
|
if (Json.Count <> vectorType.ElementCount) then
|
||||||
|
raise EConvertError.CreateFmt('JSON array size (%d) does not match vector size (%d)', [Json.Count, vectorType.ElementCount]);
|
||||||
|
|
||||||
|
SetLength(items, Json.Count);
|
||||||
|
for i := 0 to Json.Count - 1 do
|
||||||
|
items[i] := JsonToValue(Json.Items[i], vectorType.ElementType);
|
||||||
|
Result := vectorType.CreateValue(items);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDataJsonConverter - Public Methods }
|
||||||
|
|
||||||
|
class function TDataJsonConverter.ValueToJson(const Value: TDataType.TValue): TJSONValue;
|
||||||
|
begin
|
||||||
case Value.DataType.Kind of
|
case Value.DataType.Kind of
|
||||||
dkVoid: Result := TJSONNull.Create;
|
dkVoid: Result := TJSONNull.Create;
|
||||||
dkOrdinal: Result := TJSONNumber.Create(Value.AsOrdinal.Value);
|
dkOrdinal: Result := OrdinalToJson(Value.AsOrdinal);
|
||||||
dkFloat: Result := TJSONNumber.Create(Value.AsFloat.Value);
|
dkFloat: Result := FloatToJson(Value.AsFloat);
|
||||||
dkText: Result := TJSONString.Create(Value.AsText.Value);
|
dkText: Result := TextToJson(Value.AsText);
|
||||||
dkTimestamp: Result := TJSONString.Create(DateToISO8601(Value.AsTimestamp.Value, true));
|
dkTimestamp: Result := TimestampToJson(Value.AsTimestamp);
|
||||||
dkDecimal:
|
dkDecimal: Result := DecimalToJson(Value.AsDecimal);
|
||||||
begin
|
dkEnum: Result := EnumToJson(Value.AsEnum);
|
||||||
// Serialize as string to preserve precision.
|
dkRecord: Result := RecordToJson(Value.AsRecord);
|
||||||
var decValue := Value.AsDecimal;
|
dkTuple: Result := TupleToJson(Value.AsTuple);
|
||||||
var val := decValue.Value;
|
dkArray: Result := ArrayToJson(Value.AsArray);
|
||||||
var scale := decValue.DataType.Scale;
|
dkVector: Result := VectorToJson(Value.AsVector);
|
||||||
var s: string;
|
|
||||||
|
|
||||||
if (scale = 0) then
|
|
||||||
s := IntToStr(val)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
s := IntToStr(Abs(val));
|
|
||||||
if (Length(s) <= scale) then
|
|
||||||
s := StringOfChar('0', scale - Length(s) + 1) + s;
|
|
||||||
|
|
||||||
s := s.Insert(Length(s) - scale, '.');
|
|
||||||
|
|
||||||
if (val < 0) then
|
|
||||||
s := '-' + s;
|
|
||||||
end;
|
|
||||||
Result := TJSONString.Create(s);
|
|
||||||
end;
|
|
||||||
dkEnum:
|
|
||||||
begin
|
|
||||||
var enumValue := Value.AsEnum;
|
|
||||||
Result := TJSONString.Create(enumValue.DataType.Identifiers[enumValue.Value]);
|
|
||||||
end;
|
|
||||||
dkRecord:
|
|
||||||
begin
|
|
||||||
var recordValue := Value.AsRecord;
|
|
||||||
var recordType := recordValue.DataType;
|
|
||||||
jsonObject := TJSONObject.Create;
|
|
||||||
Result := jsonObject;
|
|
||||||
for i := 0 to recordType.FieldCount - 1 do
|
|
||||||
begin
|
|
||||||
jsonObject.AddPair(
|
|
||||||
recordType.Fields[i].Name,
|
|
||||||
ValueToJson(recordValue.Items[i]) // Items property returns TDataType.TValue
|
|
||||||
);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
dkTuple:
|
|
||||||
begin
|
|
||||||
var tupleValue := Value.AsTuple;
|
|
||||||
jsonArray := TJSONArray.Create;
|
|
||||||
Result := jsonArray;
|
|
||||||
for i := 0 to tupleValue.ItemCount - 1 do
|
|
||||||
jsonArray.AddElement(ValueToJson(tupleValue.Items[i]));
|
|
||||||
end;
|
|
||||||
dkArray:
|
|
||||||
begin
|
|
||||||
var arrayValue := Value.AsArray;
|
|
||||||
jsonArray := TJSONArray.Create;
|
|
||||||
Result := jsonArray;
|
|
||||||
for i := 0 to arrayValue.ElementCount - 1 do
|
|
||||||
jsonArray.AddElement(ValueToJson(arrayValue.Items[i]));
|
|
||||||
end;
|
|
||||||
dkVector:
|
|
||||||
begin
|
|
||||||
var vectorValue := Value.AsVector;
|
|
||||||
jsonArray := TJSONArray.Create;
|
|
||||||
Result := jsonArray;
|
|
||||||
for i := 0 to vectorValue.ElementCount - 1 do
|
|
||||||
jsonArray.AddElement(ValueToJson(vectorValue.Items[i]));
|
|
||||||
end;
|
|
||||||
dkMethod: raise ENotSupportedException.Create('Cannot serialize a method type to JSON.');
|
dkMethod: raise ENotSupportedException.Create('Cannot serialize a method type to JSON.');
|
||||||
else
|
else
|
||||||
raise ENotSupportedException.CreateFmt('Unsupported data kind: %s', [GetEnumName(TypeInfo(TDataKind), Ord(Value.DataType.Kind))]);
|
raise ENotSupportedException.CreateFmt('Unsupported data kind: %s', [GetEnumName(TypeInfo(TDataKind), Ord(Value.DataType.Kind))]);
|
||||||
@@ -119,136 +342,19 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
class function TDataJsonConverter.JsonToValue(const Json: TJSONValue; const DataType: TDataType): TDataType.TValue;
|
class function TDataJsonConverter.JsonToValue(const Json: TJSONValue; const DataType: TDataType): TDataType.TValue;
|
||||||
var
|
|
||||||
jsonObject: TJSONObject;
|
|
||||||
jsonArray: TJSONArray;
|
|
||||||
items: TArray<IDataValue>;
|
|
||||||
i, idx: Integer;
|
|
||||||
val: Int64;
|
|
||||||
begin
|
begin
|
||||||
Assert(Assigned(DataType.DataType), 'DataType cannot be nil for deserialization');
|
|
||||||
|
|
||||||
case DataType.Kind of
|
case DataType.Kind of
|
||||||
dkVoid:
|
dkVoid: Result := TDataType.Void.Value;
|
||||||
if (Json is TJSONNull) then
|
dkOrdinal: Result := JsonToOrdinal(Json, DataType);
|
||||||
Result := TDataType.Void.Value
|
dkFloat: Result := JsonToFloat(Json, DataType);
|
||||||
else
|
dkText: Result := JsonToText(Json, DataType);
|
||||||
raise EConvertError.Create('JSON Null expected for Void type');
|
dkTimestamp: Result := JsonToTimestamp(Json, DataType);
|
||||||
dkOrdinal: Result := DataType.AsOrdinal.CreateValue((Json as TJSONNumber).AsInt64);
|
dkDecimal: Result := JsonToDecimal(Json, DataType);
|
||||||
dkFloat: Result := DataType.AsFloat.CreateValue((Json as TJSONNumber).AsDouble);
|
dkEnum: Result := JsonToEnum(Json, DataType);
|
||||||
dkText: Result := DataType.AsText.CreateValue((Json as TJSONString).Value);
|
dkRecord: Result := JsonToRecord(Json as TJSONObject, DataType);
|
||||||
dkTimestamp: Result := DataType.AsTimestamp.CreateValue(ISO8601ToDate((Json as TJSONString).Value, True));
|
dkTuple: Result := JsonToTuple(Json as TJSONArray, DataType);
|
||||||
dkDecimal:
|
dkArray: Result := JsonToArray(Json as TJSONArray, DataType);
|
||||||
begin
|
dkVector: Result := JsonToVector(Json as TJSONArray, DataType);
|
||||||
// Handle string representation for precision, with fallback to number for compatibility.
|
|
||||||
var decimalType := DataType.AsDecimal;
|
|
||||||
if (Json is TJSONString) then
|
|
||||||
begin
|
|
||||||
var s := (Json as TJSONString).Value.Trim;
|
|
||||||
var isNegative := s.StartsWith('-');
|
|
||||||
if isNegative then
|
|
||||||
s := s.Remove(0, 1); // remove sign
|
|
||||||
|
|
||||||
var dotPos := Pos('.', s);
|
|
||||||
var numFracDigits: Integer;
|
|
||||||
if (dotPos = 0) then
|
|
||||||
numFracDigits := 0
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
numFracDigits := Length(s) - dotPos;
|
|
||||||
s := s.Remove(dotPos - 1, 1); // remove dot
|
|
||||||
end;
|
|
||||||
|
|
||||||
val := StrToInt64(s);
|
|
||||||
|
|
||||||
var scaleDiff := decimalType.Scale - numFracDigits;
|
|
||||||
if (scaleDiff > 0) then // Not enough fractional digits
|
|
||||||
begin
|
|
||||||
for i := 1 to scaleDiff do
|
|
||||||
val := val * 10;
|
|
||||||
end
|
|
||||||
else if (scaleDiff < 0) then // Too many fractional digits
|
|
||||||
begin
|
|
||||||
for i := 1 to -scaleDiff do
|
|
||||||
val := val div 10;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if isNegative then
|
|
||||||
val := -val;
|
|
||||||
|
|
||||||
Result := decimalType.CreateValue(val);
|
|
||||||
end
|
|
||||||
else if (Json is TJSONNumber) then
|
|
||||||
begin
|
|
||||||
// Support numbers for compatibility, but this can lead to precision loss.
|
|
||||||
val := Round((Json as TJSONNumber).AsDouble * Power(10, decimalType.Scale));
|
|
||||||
Result := decimalType.CreateValue(val);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise EConvertError.Create('JSON String or Number expected for Decimal type');
|
|
||||||
end;
|
|
||||||
dkEnum:
|
|
||||||
begin
|
|
||||||
var enumType := DataType.AsEnum;
|
|
||||||
idx := enumType.IndexOf((Json as TJSONString).Value);
|
|
||||||
if (idx < 0) then
|
|
||||||
raise EConvertError.CreateFmt('Invalid enum identifier "%s" for type %s', [Json.Value, DataType.Name]);
|
|
||||||
Result := enumType.CreateValue(idx);
|
|
||||||
end;
|
|
||||||
dkRecord:
|
|
||||||
begin
|
|
||||||
var recordType := DataType.AsRecord;
|
|
||||||
jsonObject := Json as TJSONObject;
|
|
||||||
SetLength(items, recordType.FieldCount);
|
|
||||||
for i := 0 to recordType.FieldCount - 1 do
|
|
||||||
begin
|
|
||||||
var field := recordType.Fields[i];
|
|
||||||
var jsonPair := jsonObject.Get(field.Name);
|
|
||||||
if not Assigned(jsonPair) then
|
|
||||||
raise EConvertError.CreateFmt('Missing field "%s" for record type %s', [field.Name, DataType.Name]);
|
|
||||||
|
|
||||||
// Recursive call returns TDataType.TValue, which implicitly converts to IDataValue for the array
|
|
||||||
items[i] := JsonToValue(jsonPair.JsonValue, field.DataType);
|
|
||||||
end;
|
|
||||||
Result := recordType.CreateValue(items);
|
|
||||||
end;
|
|
||||||
dkTuple:
|
|
||||||
begin
|
|
||||||
var tupleType := DataType.AsTuple;
|
|
||||||
jsonArray := Json as TJSONArray;
|
|
||||||
if (jsonArray.Count <> IDataTupleType(tupleType).ItemCount) then
|
|
||||||
raise EConvertError
|
|
||||||
.CreateFmt('JSON array size (%d) does not match tuple size (%d)', [jsonArray.Count, tupleType.ItemCount]);
|
|
||||||
|
|
||||||
SetLength(items, IDataTupleType(tupleType).ItemCount);
|
|
||||||
for i := 0 to IDataTupleType(tupleType).ItemCount - 1 do
|
|
||||||
begin
|
|
||||||
var itemType := IDataTupleType(tupleType).ItemType[i];
|
|
||||||
items[i] := JsonToValue(jsonArray.Items[i], itemType);
|
|
||||||
end;
|
|
||||||
Result := tupleType.CreateValue(items);
|
|
||||||
end;
|
|
||||||
dkArray:
|
|
||||||
begin
|
|
||||||
var arrayType := DataType.AsArray;
|
|
||||||
jsonArray := Json as TJSONArray;
|
|
||||||
SetLength(items, jsonArray.Count);
|
|
||||||
for i := 0 to jsonArray.Count - 1 do
|
|
||||||
items[i] := JsonToValue(jsonArray.Items[i], arrayType.ElementType);
|
|
||||||
Result := arrayType.CreateValue(items);
|
|
||||||
end;
|
|
||||||
dkVector:
|
|
||||||
begin
|
|
||||||
var vectorType := DataType.AsVector;
|
|
||||||
jsonArray := Json as TJSONArray;
|
|
||||||
if (jsonArray.Count <> vectorType.ElementCount) then
|
|
||||||
raise EConvertError
|
|
||||||
.CreateFmt('JSON array size (%d) does not match vector size (%d)', [jsonArray.Count, vectorType.ElementCount]);
|
|
||||||
SetLength(items, jsonArray.Count);
|
|
||||||
for i := 0 to jsonArray.Count - 1 do
|
|
||||||
items[i] := JsonToValue(jsonArray.Items[i], vectorType.ElementType);
|
|
||||||
Result := vectorType.CreateValue(items);
|
|
||||||
end;
|
|
||||||
dkMethod: raise ENotSupportedException.Create('Cannot deserialize a method type from JSON.');
|
dkMethod: raise ENotSupportedException.Create('Cannot deserialize a method type from JSON.');
|
||||||
else
|
else
|
||||||
raise ENotSupportedException.CreateFmt('Unsupported data kind: %s', [GetEnumName(TypeInfo(TDataKind), Ord(DataType.Kind))]);
|
raise ENotSupportedException.CreateFmt('Unsupported data kind: %s', [GetEnumName(TypeInfo(TDataKind), Ord(DataType.Kind))]);
|
||||||
|
|||||||
@@ -381,7 +381,6 @@ begin
|
|||||||
|
|
||||||
// Test Deserialization
|
// Test Deserialization
|
||||||
FJson := TJSONNull.Create;
|
FJson := TJSONNull.Create;
|
||||||
//TODO Fails with "Method did not throw any exceptions. Deserializing a method should raise an exception"
|
|
||||||
Assert.WillRaise(
|
Assert.WillRaise(
|
||||||
procedure begin TDataJsonConverter.JsonToValue(FJson, methodType); end,
|
procedure begin TDataJsonConverter.JsonToValue(FJson, methodType); end,
|
||||||
ENotSupportedException,
|
ENotSupportedException,
|
||||||
|
|||||||
Reference in New Issue
Block a user