JSON이 직렬화/역 직렬화 (대부분의 경우) 전용 인 경우 JSON은 응용 프로그램의 경계에서만 처리해야합니다.
계약
외부에 대한 계약 (들)을 정의하고 외부 그 반대로 내부에서 데이터를 전송하는 데 사용할. 편리한 탈을 위해 설계 계약 단위/
unit whatever.ApiJson.v1;
// this is the contract definition for version 1
interface
uses
System.SysUtils,
REST.Json.Types,
Commons.JsonContract;
type
TApprenantJSON = class;
TContratJSON = class;
TContratJSON = class(TJsonContractBase)
private
[ JSONName('date_deb') ]
FDateDeb: TDateTime;
public
property DateDeb: TDateTime read FDateDeb write FDateDeb;
public
class function FromJson(const aJsonStr: string): TContratJSON;
end;
TApprenantJSON = class(TJsonContractBase)
private
[ JSONName('nom') ]
FNom: string;
[ JSONName('contrats') ]
FContrats: TArray<TContratJSON>;
public
property Nom : string read FNom write FNom;
property Contrats: TArray<TContratJSON> read FContrats write FContrats;
public
destructor Destroy; override;
public
class function FromJson(const aJsonStr: string): TApprenantJSON;
end;
implementation
{ TApprenantJSON }
destructor TApprenantJSON.Destroy;
begin
DisposeObjectArray<TContratJSON>(FContrats);
inherited;
end;
class function TApprenantJSON.FromJson(const aJsonStr: string): TApprenantJSON;
begin
Result := _FromJson(aJsonStr) as TApprenantJSON;
end;
{ TContratJSON }
class function TContratJSON.FromJson(const aJsonStr: string): TContratJSON;
begin
Result := _FromJson(aJsonStr) as TContratJSON;
end;
end.
당신은 내가 배열과 클래스를 사용하여 볼 수 있듯이 직렬화
첫째. 클래스 이러한 배열을 관리하기 위해 우리가 탈/직렬화이 클래스 만를 사용하는 기본 클래스는 응용 프로그램 자체를 들어
을 그
unit Commons.JsonContract;
interface
type
TJsonContractBase = class abstract
protected
procedure DisposeObjectArray<T: class>(var arr: TArray<T>);
class function _FromJson(const aJsonStr: string): TObject; overload;
class procedure _FromJson(aResult: TObject; const aJsonStr: string); overload;
public
function ToJson(): string; virtual;
end;
implementation
uses
System.Sysutils,
System.JSON,
REST.JSON;
{ TJsonContractBase }
procedure TJsonContractBase.DisposeObjectArray<T>(var arr: TArray<T>);
var
I: Integer;
begin
for I := low(arr) to high(arr) do
FreeAndNil(arr[ I ]);
SetLength(arr, 0);
end;
class function TJsonContractBase._FromJson(const aJsonStr: string): TObject;
begin
Result := Self.Create;
try
_FromJson(Result, aJsonStr);
except
Result.Free;
raise;
end;
end;
class procedure TJsonContractBase._FromJson(aResult: TObject; const aJsonStr: string);
var
lJson: TJsonObject;
begin
lJson := TJsonObject.ParseJSONValue(aJsonStr) as TJsonObject;
try
TJson.JsonToObject(aResult, lJson);
finally
lJson.Free;
end;
end;
function TJsonContractBase.ToJson: string;
begin
Result := TJson.ObjectToJsonString(Self);
end;
end.
비즈니스를 다루는 개체있다. 내부 비즈니스 오브젝트/엔티티는 이들로부터 분리됩니다.
unit whatever.DataObjects;
interface
uses
System.Generics.Collections;
type
TApprenant = class;
TContrat = class;
TApprenant = class
private
FNom : string;
FContrats: TObjectList<TContrat>;
public
property Nom : string read FNom write FNom;
property Contrats: TObjectList<TContrat> read FContrats;
public
constructor Create;
destructor Destroy; override;
end;
TContrat = class
private
FDateDeb: TDateTime;
public
property DateDeb: TDateTime read FDateDeb write FDateDeb;
end;
implementation
{ TApprenant }
constructor TApprenant.Create;
begin
inherited;
FContrats := TObjectList<TContrat>.Create(true);
end;
destructor TApprenant.Destroy;
begin
FContrats.Free;
inherited;
end;
end.
모든 것을 두 번 선언하면 어떤 이점이 있습니까?
이제는 서로 감염시키지 않고 비즈니스 개체 또는 계약을 변경할 수 있습니다. 당신은 둘 다 다른 유형과 이름을 가질 수 있으며, 내부 수업은 외부 계약에 긴밀히 묶이지 않습니다.
참조 : 비즈니스 오브젝트와 계약 사이에 쉽게 맵핑의 Single Responsibility Principle
매핑
모두 함께 퍼팅 매퍼
unit Commons.Mappings;
interface
uses
System.Generics.Collections,
System.Rtti,
System.SysUtils,
System.TypInfo;
type
TMapKey = record
Source: PTypeInfo;
Target: PTypeInfo;
class function Create<TSource, TTarget>(): TMapKey; static;
end;
TMapper = class
private
FMappings: TDictionary<TMapKey, TFunc<TValue, TValue>>;
public
procedure Add<TSource, TTarget>(aConverter: TFunc<TSource, TTarget>); overload;
procedure Add<TSource, TTarget>(aConverter: TFunc<TSource, TTarget>; aReverter: TFunc<TTarget, TSource>); overload;
public
constructor Create;
destructor Destroy; override;
function Map<TSource, TTarget>(const aSource: TSource): TTarget; overload;
procedure Map<TSource, TTarget>(const aSource: TSource; out aTarget: TTarget); overload;
function MapCollection<TSource, TTarget>(const aCollection: TEnumerable<TSource>): TArray<TTarget>; overload;
function MapCollection<TSource, TTarget>(const aCollection: array of TSource): TArray<TTarget>; overload;
end;
implementation
{ TMapper }
procedure TMapper.Add<TSource, TTarget>(aConverter: TFunc<TSource, TTarget>);
var
lKey: TMapKey;
begin
lKey := TMapKey.Create<TSource, TTarget>();
FMappings.Add(lKey,
function(Source: TValue): TValue
begin
Result := TValue.From<TTarget>(aConverter(Source.AsType<TSource>()));
end);
end;
procedure TMapper.Add<TSource, TTarget>(
aConverter: TFunc<TSource, TTarget>;
aReverter : TFunc<TTarget, TSource>);
begin
Add<TSource, TTarget>(aConverter);
Add<TTarget, TSource>(aReverter);
end;
constructor TMapper.Create;
begin
inherited;
FMappings := TDictionary < TMapKey, TFunc < TValue, TValue >>.Create;
end;
destructor TMapper.Destroy;
begin
FMappings.Free;
inherited;
end;
function TMapper.Map<TSource, TTarget>(const aSource: TSource): TTarget;
var
lKey: TMapKey;
begin
lKey := TMapKey.Create<TSource, TTarget>();
Result := FMappings[ lKey ](TValue.From<TSource>(aSource)).AsType<TTarget>();
end;
procedure TMapper.Map<TSource, TTarget>(
const aSource: TSource;
out aTarget : TTarget);
begin
aTarget := Map<TSource, TTarget>(aSource);
end;
function TMapper.MapCollection<TSource, TTarget>(const aCollection: array of TSource): TArray<TTarget>;
var
lCollection: TList<TSource>;
begin
lCollection := TList<TSource>.Create();
try
lCollection.AddRange(aCollection);
Result := MapCollection<TSource, TTarget>(lCollection);
finally
lCollection.Free;
end;
end;
function TMapper.MapCollection<TSource, TTarget>(const aCollection: TEnumerable<TSource>): TArray<TTarget>;
var
lKey : TMapKey;
lMapHandler: TFunc<TValue, TValue>;
lResult : TList<TTarget>;
lSourceItem: TSource;
begin
lKey := TMapKey.Create<TSource, TTarget>();
lMapHandler := FMappings[ lKey ];
lResult := TList<TTarget>.Create;
try
for lSourceItem in aCollection do
begin
lResult.Add(lMapHandler(TValue.From<TSource>(lSourceItem)).AsType<TTarget>());
end;
Result := lResult.ToArray();
finally
lResult.Free;
end;
end;
{ TMapKey }
class function TMapKey.Create<TSource, TTarget>: TMapKey;
begin
Result.Source := TypeInfo(TSource);
Result.Target := TypeInfo(TTarget);
end;
end.
를 사용
program so_37659536;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Commons.Mappings in 'Commons.Mappings.pas',
Commons.JsonContract in 'Commons.JsonContract.pas',
whatever.DataObjects in 'whatever.DataObjects.pas',
whatever.ApiJson.v1 in 'whatever.ApiJson.v1.pas',
whatever.ApiJson.v2 in 'whatever.ApiJson.v2.pas';
procedure DemoMapV1(aMapper: TMapper);
var
lApprenant: TApprenant;
lContrat : TContrat;
lApprenantJSON: whatever.ApiJson.v1.TApprenantJSON;
lApprenantJSONStr: string;
begin
WriteLn;
WriteLn('V1');
WriteLn;
{$REGION 'Serialize'}
lApprenantJSON := nil;
try
lApprenant := TApprenant.Create;
try
lApprenant.Nom := 'JEAN';
lContrat := TContrat.Create;
lContrat.DateDeb := EncodeDate(2015, 1, 1);
lApprenant.Contrats.Add(lContrat);
aMapper.Map(lApprenant, lApprenantJSON);
finally
lApprenant.Free;
end;
lApprenantJSONStr := lApprenantJSON.ToJson();
finally
lApprenantJSON.Free;
end;
{$ENDREGION 'Serialize'}
WriteLn(lApprenantJSONStr);
{$REGION 'Deserialize'}
lApprenant := nil;
lApprenantJSON := whatever.ApiJson.v1.TApprenantJSON.FromJson(lApprenantJSONStr);
try
aMapper.Map(lApprenantJSON, lApprenant);
try
WriteLn('Nom: ', lApprenant.Nom);
WriteLn('Contrats:');
for lContrat in lApprenant.Contrats do
begin
WriteLn('- ', DateToStr(lContrat.DateDeb));
end;
finally
lApprenant.Free;
end;
finally
lApprenantJSON.Free;
end;
{$ENDREGION 'Deserialize'}
end;
var
Mapper: TMapper;
begin
try
Mapper := TMapper.Create;
try
{$REGION 'Define Mapping'}
{$REGION 'v1'}
Mapper.Add<TApprenant, whatever.ApiJson.v1.TApprenantJSON>(
function(s: TApprenant): whatever.ApiJson.v1.TApprenantJSON
begin
Result := whatever.ApiJson.v1.TApprenantJSON.Create;
Result.Nom := s.Nom;
Result.Contrats := Mapper.MapCollection<TContrat, whatever.ApiJson.v1.TContratJSON>(s.Contrats);
end,
function(s: whatever.ApiJson.v1.TApprenantJSON): TApprenant
begin
Result := TApprenant.Create;
Result.Nom := s.Nom;
Result.Contrats.AddRange(Mapper.MapCollection<whatever.ApiJson.v1.TContratJSON, TContrat>(s.Contrats));
end);
Mapper.Add<TContrat, whatever.ApiJson.v1.TContratJSON>(
function(s: TContrat): whatever.ApiJson.v1.TContratJSON
begin
Result := whatever.ApiJson.v1.TContratJSON.Create;
Result.DateDeb := s.DateDeb;
end,
function(s: whatever.ApiJson.v1.TContratJSON): TContrat
begin
Result := TContrat.Create;
Result.DateDeb := s.DateDeb;
end);
{$ENDREGION 'v1'}
{$REGION 'v2'}
// mapping for v2
{$ENDREGION 'v2'}
{$ENDREGION 'Define Mapping'}
DemoMapV1(Mapper);
finally
Mapper.Free;
end;
except
on E: Exception do
WriteLn(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
주이것은 델파이 시애틀에서 테스트되었습니다 - XE6에서이 기능을 사용하려면 일부 유닛을 변경해야 할 수도 있습니다.
죄송합니다. 이러한 질문은 논쟁의 여지가있는 답변을 얻으려는 주제와 관련이 없습니다. –
자, 그럼 TJson.ObjectToJsonString을 사용하여 JSON에서 "ownsObjects"속성을 삭제할 수 있습니까? – Joc02
다른 개체를 포함하는 개체를 직렬화하려고 시도하는 것은 좋지 않습니다. 반면에'TContrat'가 왜 클래스인지 이해할 수 없습니다. 그것은 기록이어야합니다. 개체 목록을 배열로 바꾸고 클래스를 레코드로 바꾸면 더 좋은 위치에 있어야합니다. –