2016-06-06 7 views
1

저는 Delphi XE6을 사용하고 있습니다. JSON을 만들고 JSON을 파싱하는 가장 좋은 방법을 찾고 있습니다.Delphi가 JSON을 만듭니다

나는 REST.Json 장치 및이 방법으로 작업하려고 :

TContrat = class 
private 
    FdDateDeb: TDate; 
public 
    property dDateDeb: TDate read FdDateDeb write FdDateDeb; 
end; 

TApprenant = class 
private 
    FsNom : string; 
    [JSONName('ListContrat')] 
    FListContrat: TObjectList<TContrat>; 
public 
    property sNom : string read FsNom write FsNom; 
    property ListContrat: TObjectList<TContrat> read FListContrat write FListContrat; 
end; 

... 
procedure TForm3.Button2Click(Sender: TObject); 
var 
    apprenant : TApprenant; 
    contrat : TContrat; 
begin 
    Memo1.Clear; 

    apprenant := TApprenant.Create; 
    apprenant.sNom := 'JEAN'; 

    contrat := TContrat.Create; 
    contrat.dDateDeb := StrToDate('01/01/2015'); 
    apprenant.ListContrat.Add(contrat); 

    contrat := TContrat.Create; 
    contrat.dDateDeb := StrToDate('01/01/2016'); 
    apprenant.ListContrat.Add(contrat); 

    Memo1.Lines.Add(TJson.ObjectToJsonString(apprenant)); 
end; 

결과는 내가 TObjectList와 <> (예 : 일부 속성이 결과에서

{ 
    "sNom": "JEAN", 
    "ListContrat": { 
     "ownsObjects": true, 
     "items": [{ 
      "dDateDeb": 42005, 
     }, { 
      "dDateDeb": 42370, 
     }], 
     "count": 2, 
     "arrayManager": {} 
    } 
} 

입니다 TJson.ObjectToJsonString "ownsObjects").

JSON을 만드는 가장 좋은 방법이 있습니까? 프레임 워크를 사용해야합니까? 튜토리얼을 잘 읽었습니까?

죄송합니다, 포럼에서 검색했지만 좋은 방법은 없습니다.

+0

죄송합니다. 이러한 질문은 논쟁의 여지가있는 답변을 얻으려는 주제와 관련이 없습니다. –

+0

자, 그럼 TJson.ObjectToJsonString을 사용하여 JSON에서 "ownsObjects"속성을 삭제할 수 있습니까? – Joc02

+0

다른 개체를 포함하는 개체를 직렬화하려고 시도하는 것은 좋지 않습니다. 반면에'TContrat'가 왜 클래스인지 이해할 수 없습니다. 그것은 기록이어야합니다. 개체 목록을 배열로 바꾸고 클래스를 레코드로 바꾸면 더 좋은 위치에 있어야합니다. –

답변

6

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에서이 기능을 사용하려면 일부 유닛을 변경해야 할 수도 있습니다.

+0

"map"호출을 제외하고 XE6에서 코드 작업이 정상입니다. 나는 유형을 쓸지도 모른다 Mapper.Map (Apprenant, ApprenantJSON); – Joc02

+0

네, 최신 버전의 또 다른 기능입니다 o) –