unit Myc.Data.Scalar.JSON; interface uses System.SysUtils, System.Rtti, System.TypInfo, Myc.Data.Scalar; type TRttiAstHelper = class private // Maps a Delphi RTTI type to its TScalarKind. class function TypeToScalarKind(AType: TRttiType): TScalarKind; static; public // Creates a JSON definition string from a record type info. class function RecordDefinitionToJson(ATypeInfo: PTypeInfo): string; static; // Creates a record definition from a JSON string. class function JsonToRecordDefinition(const AJson: string): TScalarRecordDefinition; static; end; implementation uses Myc.Data.Decimal, System.Generics.Collections, System.JSON; { TRttiAstHelper } class function TRttiAstHelper.JsonToRecordDefinition(const AJson: string): TScalarRecordDefinition; var jsonValue: TJSONValue; rootObj: TJSONObject; fieldsArray: TJSONArray; i: Integer; fieldObj: TJSONObject; kindStr: string; fields: TArray; begin jsonValue := TJSONObject.ParseJSONValue(AJson); if not Assigned(jsonValue) then exit; try if not (jsonValue is TJSONObject) then exit; rootObj := jsonValue as TJSONObject; if not rootObj.TryGetValue('fields', fieldsArray) then exit; SetLength(fields, fieldsArray.Count); for i := 0 to fieldsArray.Count - 1 do begin fieldObj := fieldsArray.Items[i] as TJSONObject; if not Assigned(fieldObj) then continue; // or raise error fields[i].Name := fieldObj.GetValue('name'); kindStr := fieldObj.GetValue('kind'); fields[i].Kind := TScalarKind(GetEnumValue(TypeInfo(TScalarKind), kindStr)); end; Result := TScalarRecordDefinition.Create(fields); finally jsonValue.Free; end; end; class function TRttiAstHelper.RecordDefinitionToJson(ATypeInfo: PTypeInfo): string; var ctx: TRttiContext; rttiType: TRttiType; rttiRecordType: TRttiRecordType; field: TRttiField; rootObj: TJSONObject; fieldsArray: TJSONArray; fieldObj: TJSONObject; begin // 1. Create TRttiContext and get TRttiRecordType. ctx := TRttiContext.Create; rttiType := ctx.GetType(ATypeInfo); if not (rttiType is TRttiRecordType) then raise EArgumentException.Create('PTypeInfo provided is not a record type.'); rttiRecordType := rttiType as TRttiRecordType; // 2. Create root TJSONObject and a TJSONArray for 'fields'. rootObj := TJSONObject.Create; try fieldsArray := TJSONArray.Create; rootObj.AddPair('fields', fieldsArray); // 3. Loop through all fields of the record type. for field in rttiRecordType.GetFields do begin // 4a. Create a TJSONObject for the field definition. fieldObj := TJSONObject.Create; // 4b. Add field name. fieldObj.AddPair('name', TJSONString.Create(field.Name)); // 4c. Call TypeToScalarKind to get the type and add it. fieldObj.AddPair('kind', TJSONString.Create(GetEnumName(TypeInfo(TScalarKind), Ord(TypeToScalarKind(field.FieldType))))); // 4d. Add the field object to the 'fields' array. fieldsArray.Add(fieldObj); end; // 5. Convert the root JSON object to a string and set it as Result. Result := rootObj.ToJSON; finally rootObj.Free; end; end; class function TRttiAstHelper.TypeToScalarKind(AType: TRttiType): TScalarKind; var typeHandle: PTypeInfo; begin // 1. Use a case statement or if-checks on AType.Handle. typeHandle := AType.Handle; // 2. Compare with TypeInfo(Integer), TypeInfo(Double), TypeInfo(TDecimal) etc. if typeHandle = TypeInfo(Integer) then Result := skInteger else if typeHandle = TypeInfo(Int64) then Result := skInt64 else if typeHandle = TypeInfo(UInt64) then Result := skUInt64 else if typeHandle = TypeInfo(Single) then Result := skSingle else if typeHandle = TypeInfo(Double) then Result := skDouble else if typeHandle = TypeInfo(TDateTime) then Result := skDateTime else if typeHandle = TypeInfo(Boolean) then Result := skBoolean else if typeHandle = TypeInfo(Char) then Result := skChar else if typeHandle = TypeInfo(TDecimal) then Result := skDecimal else if typeHandle = TypeInfo(TTimestamp) then Result := skTimestamp else // 4. Raise an exception for unsupported types. raise EArgumentException.CreateFmt('Unsupported record field type: %s', [AType.Name]); end; end.