2019-09-25 14:54:02 +02:00
|
|
|
{******************************************************************************}
|
|
|
|
{ }
|
|
|
|
{ Delphi SwagDoc Library }
|
|
|
|
{ Copyright (c) 2018 Marcelo Jaloto }
|
|
|
|
{ https://github.com/marcelojaloto/SwagDoc }
|
|
|
|
{ }
|
|
|
|
{******************************************************************************}
|
|
|
|
{ }
|
|
|
|
{ Licensed under the Apache License, Version 2.0 (the "License"); }
|
|
|
|
{ you may not use this file except in compliance with the License. }
|
|
|
|
{ You may obtain a copy of the License at }
|
|
|
|
{ }
|
|
|
|
{ http://www.apache.org/licenses/LICENSE-2.0 }
|
|
|
|
{ }
|
|
|
|
{ Unless required by applicable law or agreed to in writing, software }
|
|
|
|
{ distributed under the License is distributed on an "AS IS" BASIS, }
|
|
|
|
{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
|
|
|
|
{ See the License for the specific language governing permissions and }
|
|
|
|
{ limitations under the License. }
|
|
|
|
{ }
|
|
|
|
{******************************************************************************}
|
|
|
|
|
|
|
|
unit Json.Schema;
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
System.SysUtils,
|
|
|
|
System.Json,
|
|
|
|
Json.Schema.Common.Types,
|
|
|
|
Json.Schema.Field,
|
|
|
|
Json.Schema.Field.Objects;
|
|
|
|
|
|
|
|
type
|
|
|
|
ETypeNotSupportedByAField = class(Exception);
|
|
|
|
|
|
|
|
TJsonSchema = class(TObject)
|
|
|
|
strict private
|
|
|
|
fRoot: TJsonFieldObject;
|
|
|
|
fRef : string;
|
|
|
|
const c_ErrorTypeNotSupportedByAField = 'Type not supported by a field.';
|
|
|
|
|
|
|
|
function GetJsonFieldClass<T>: TJsonFieldClass;
|
|
|
|
function GetSchemaKind<T>: TSchemaKind;
|
|
|
|
private
|
|
|
|
procedure SetRef(const Value: string);
|
|
|
|
function GetRef: string;
|
|
|
|
public
|
|
|
|
constructor Create; reintroduce;
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
|
|
function ToJson: TJsonObject;
|
|
|
|
function Clone: TJsonSchema; overload;
|
|
|
|
function Clone(pSourceField: TJsonSchema): TJsonSchema; overload;
|
|
|
|
function AddField<T>(const pName: string; const pDescription: string = ''): TJsonField; overload;
|
|
|
|
function AddField(pSchemaObject: TJsonSchema): TJsonField; overload;
|
2020-05-03 01:00:17 +02:00
|
|
|
function AddFieldAsType<T>(const pName: string; const pDescription: string = ''): T;
|
2019-09-25 14:54:02 +02:00
|
|
|
|
|
|
|
property Root: TJsonFieldObject read fRoot;
|
|
|
|
property Ref: string read GetRef write SetRef;
|
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
|
|
|
System.Classes,
|
|
|
|
System.TypInfo,
|
|
|
|
System.Rtti,
|
|
|
|
Json.Schema.Field.Strings,
|
|
|
|
Json.Schema.Field.Enums,
|
|
|
|
Json.Schema.Field.Numbers,
|
|
|
|
Json.Schema.Field.Arrays,
|
|
|
|
Json.Schema.Field.DateTimes,
|
|
|
|
Json.Schema.Field.Booleans;
|
|
|
|
|
|
|
|
{ TJsonSchema }
|
|
|
|
|
|
|
|
constructor TJsonSchema.Create;
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
fRoot := TJsonFieldObject.Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TJsonSchema.Destroy;
|
|
|
|
begin
|
|
|
|
FreeAndNil(fRoot);
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJsonSchema.GetJsonFieldClass<T>: TJsonFieldClass;
|
|
|
|
var
|
|
|
|
vSchemaKind: TSchemaKind;
|
|
|
|
vClass: TPersistentClass;
|
|
|
|
begin
|
|
|
|
Result := nil;
|
|
|
|
vSchemaKind := GetSchemaKind<T>;
|
|
|
|
if (vSchemaKind = skUnknown) then
|
|
|
|
raise ETypeNotSupportedByAField.Create(c_ErrorTypeNotSupportedByAField);
|
|
|
|
|
|
|
|
case vSchemaKind of
|
|
|
|
skObject:
|
|
|
|
begin
|
2021-07-22 19:13:58 +02:00
|
|
|
vClass := FindClass(string(PTypeInfo(System.TypeInfo(T))^.Name));
|
2019-09-25 14:54:02 +02:00
|
|
|
if not vClass.InheritsFrom(TJsonField) then
|
|
|
|
raise ETypeNotSupportedByAField.Create(c_ErrorTypeNotSupportedByAField);
|
|
|
|
Result := TJsonFieldClass(vClass);
|
|
|
|
end;
|
|
|
|
skInteger: Result := TJsonFieldInteger;
|
|
|
|
skInt64: Result := TJsonFieldInt64;
|
|
|
|
skNumber: Result := TJsonFieldNumber;
|
|
|
|
skDateTime: Result := TJsonFieldDateTime;
|
|
|
|
skDate: Result := TJsonFieldDate;
|
|
|
|
skTime: Result := TJsonFieldTime;
|
|
|
|
skEnumeration: Result := TJsonFieldEnum;
|
|
|
|
skBoolean: Result := TJsonFieldBoolean;
|
|
|
|
skArray: Result := TJsonFieldArray;
|
|
|
|
skString, skChar: Result := TJsonFieldString;
|
|
|
|
skGuid: Result := TJsonFieldGuid;
|
|
|
|
else
|
|
|
|
raise ETypeNotSupportedByAField.Create(c_ErrorTypeNotSupportedByAField);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJsonSchema.SetRef(const Value: string);
|
|
|
|
begin
|
|
|
|
fRoot.Ref := Value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJsonSchema.GetSchemaKind<T>: TSchemaKind;
|
|
|
|
var
|
|
|
|
vTypeInfo: PTypeInfo;
|
|
|
|
vClass: TPersistentClass;
|
|
|
|
begin
|
|
|
|
Result := skUnknown;
|
|
|
|
vTypeInfo := System.TypeInfo(T);
|
|
|
|
if not Assigned(vTypeInfo) then
|
|
|
|
Exit;
|
|
|
|
|
|
|
|
case vTypeInfo^.Kind of
|
|
|
|
tkClass: Result := skObject;
|
|
|
|
tkArray: Result := skArray;
|
|
|
|
tkString, tkUString, tkChar: Result := skString;
|
|
|
|
tkRecord:
|
|
|
|
begin
|
2021-07-22 19:13:58 +02:00
|
|
|
if (LowerCase(string(vTypeInfo^.Name)) = 'tguid') then
|
2019-09-25 14:54:02 +02:00
|
|
|
Result := skGuid
|
|
|
|
end;
|
|
|
|
tkInteger: Result := skInteger;
|
|
|
|
tkInt64: Result := skInt64;
|
|
|
|
tkEnumeration:
|
|
|
|
begin
|
2021-07-22 19:13:58 +02:00
|
|
|
if (LowerCase(string(vTypeInfo^.Name)) = 'boolean') then
|
2019-09-25 14:54:02 +02:00
|
|
|
Result := skBoolean
|
|
|
|
else
|
|
|
|
Result := skEnumeration;
|
|
|
|
end;
|
|
|
|
tkFloat:
|
|
|
|
begin
|
2021-07-22 19:13:58 +02:00
|
|
|
if (LowerCase(string(vTypeInfo^.Name)) = 'tdatetime') then
|
2019-09-25 14:54:02 +02:00
|
|
|
Result := skDateTime
|
2021-07-22 19:13:58 +02:00
|
|
|
else if (LowerCase(string(vTypeInfo^.Name)) = 'tdate') then
|
2019-09-25 14:54:02 +02:00
|
|
|
Result := skDate
|
2021-07-22 19:13:58 +02:00
|
|
|
else if (LowerCase(string(vTypeInfo^.Name)) = 'ttime') then
|
2019-09-25 14:54:02 +02:00
|
|
|
Result := skTime
|
|
|
|
else
|
|
|
|
Result := skNumber;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJsonSchema.GetRef: string;
|
|
|
|
begin
|
|
|
|
Result := FRef;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJsonSchema.Clone: TJsonSchema;
|
|
|
|
begin
|
|
|
|
Result := Self.Clone(Self);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJsonSchema.AddField(pSchemaObject: TJsonSchema): TJsonField;
|
|
|
|
begin
|
|
|
|
Result := pSchemaObject.Root.Clone;
|
|
|
|
Self.Root.AddField(Result);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJsonSchema.AddField<T>(const pName: string; const pDescription: string = ''): TJsonField;
|
|
|
|
begin
|
|
|
|
Result := GetJsonFieldClass<T>.Create;
|
|
|
|
Result.Name := pName;
|
|
|
|
Result.Description := pDescription;
|
|
|
|
fRoot.AddField(Result);
|
|
|
|
end;
|
|
|
|
|
2020-05-03 01:00:17 +02:00
|
|
|
function TJsonSchema.AddFieldAsType<T>(const pName, pDescription: string): T;
|
|
|
|
var
|
|
|
|
vValue: TValue;
|
|
|
|
begin
|
|
|
|
vValue := AddField<T>(pName, pDescription);
|
|
|
|
Result := vValue.AsType<T>;
|
|
|
|
end;
|
|
|
|
|
2019-09-25 14:54:02 +02:00
|
|
|
function TJsonSchema.Clone(pSourceField: TJsonSchema): TJsonSchema;
|
|
|
|
begin
|
|
|
|
Result := TJsonSchema.Create;
|
|
|
|
Result.Root.CopyFields(pSourceField.Root);
|
|
|
|
Result.Root.Ref := pSourceField.Root.Ref;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJsonSchema.ToJson: TJsonObject;
|
|
|
|
begin
|
|
|
|
if fRef.Length = 0 then
|
|
|
|
Result := fRoot.ToJsonSchema
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
Result := TJSONObject.Create;
|
|
|
|
Result.AddPair('$ref', fRef);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|