Jump to content
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr ×
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr
  • 델파이에서 JSON을 다룰 때 당신이 몰랐던 것들


    Kori

    << DelphiCon 2021 목록으로 이동

    원본 비디오(YouTube) 보기 (30 min)

    DelphiCon 의 2021 시리즈 중, Things That You Don’t Know About JSON in Delphi - Dion Carlos Mai & Gustavo Mena Barreto (25 min) 의 한글 요약본입니다.  

    • 델파이에서 JSON을 다루는 방법, JSON 파서를 가장 생산적으로 사용하는 방법, JSON 직렬화 작동을 커스터마이징하고 자동화하는 옵션을 설명합니다.
    • 발표자 Gustavo Mena BarretoAquasoft 선임 개발자이며, Dion Carlos Mai은 Aquasoft 개발팀장입니다.
    • 이 세션에서 사용된 전체 소스코드:  https://github.com/gustavomenabarreto/delphicon2021 

    목차


    델파이에서 JSON 다루기를 주제로 정한 이유

    요즘  개발자에게 JSON은 매우 중요한 자리를 차지하고 있다.

    • 요즘은 API 통합이 너무나 많다.
    • 심지어 NoSQL JSON 데이터베이스도 있다.

    JSON을 다루는 방법이 여러가지 라는 사실을 잘 모르는 개발자들이 있다.

    • 이 세션에서는 JSON을 다루는 여러 가지 방법을 설명한다.

     

    JSON을 다룰 때 델파이에서 선택할 수 있는 것들

    • 가장 간단한 방법
    • JsonToDelphi: JSON 오브젝트를  가지고 델파이 클래스를 자동 생성하는 웹사이트
    • Super Object (오픈소스): 델파이 6, 7 등 구버전에서 JSON을 다룰 수 있음 (최신 버전 사용자에게는 권장하지 않음)
    • RESTResponseDataSetAdapter 컴포넌트: JSON을 기반으로 데이터셋을 자동 생성
    • TJSONMarshal 클래스: JSON 직렬화 처리를 커스터마이징할 때 사용

    이 세션에서 사용할 JSON 샘플

    json_sample.png
    그림. 이 세션의 코드는 비트코인 블록체인에서 사용되는 매우 구조가 큰 JSON에서 극히 일부분만 뽑아 놓은 샘플 JSON을 사용한다.

    JSON을 사용하기 위해 필요한 델파이 유닛(들)

    uses
     System.JSON,
     REST.Json;

     

    1. 가장 간단한 방법

    TJSONObject와 TJSONArray를 사용하는 방법

    JSON 읽기와 쓰기 (아래 코드와 주석 참조)

    // [JSON 읽기]
    var
      objeto : TJSONObject;
      ObjetoArray: TJSONArray;
      I: Integer;
    begin
      // ParseJSONValue를 이용하면, 단 한줄로 JSONObject를 만들 수 있다.
      objeto := TJsonObject.ParseJSONValue(MemoJson.Lines.Text) as TJSONObject;
    
      // JSONObject의 Get과 GetValue 메소드를 이용하면, JSON 개체의 모든 데이터에 접근할 수 있다.
      ShowMessage(objeto.Get('block_hash').ToString);
      ShowMessage(objeto.GetValue('block_hash').Value);
    
      // 배열 형식의 JSON값은 TJSONArray로 받을 수 있다.
      ObjetoArray := objeto.Get('inputs').JsonValue as TJSONArray;
    
      // JSONArray 안에 있는 각 요소는 for 루프를 통해 접근할 수 있다. 
      for I := 0 to ObjetoArray.Size - 1 do
      begin
        MemoResultado.Lines.Add(
          ObjetoArray.Get(i).ToString + ': '
          + ObjetoArray.Get(i).Value);
      end;
    end;
    
    // [JSON 쓰기]
    var
      objeto : TJSONObject;
      ObjetoArray: TJSONArray;
      ObjetoInputs: TJSONObject;
    begin
      objeto := TJSONObject.Create;
      ObjetoArray := TJSONArray.Create;
    
      // JSONObject의 AddPair (필드 이름, 필드 값) 메소드를 사용하여 데이터 쓰기
      // 필드 값에 문자열 넣기
      objeto.AddPair('block_hash','123456789');
      // 필드 값에 JSONObject의 일종인 JSONArray 넣기
      Objeto.AddPair('Inputs',ObjetoArray);
    
      // 'Input' 필드 값인 ObjetoArray에 들어 갈 JSONObject 만들기
      ObjetoInputs := TJSONObject.Create;
      // 위와 동일하게 AddPair를 사용하여 JSONObject에 데이터 쓰기
      ObjetoInputs.AddPair('prev_hash','987654321');
      ObjetoInputs.AddPair('script','1');
    
      // JSON 배열인 ObjetoArray의 Add 메소드를 이용하여, 배열 안에 요소 넣기
      ObjetoArray.Add(ObjetoInputs);
      MemoResultado.Lines.Text := objeto.ToString;
    end;

     

    2. JsonToDelphi

    https://jsontodelphi.com는 JSON 샘플을 넣으면, 해당 JSON을 다룰 수 있는 델파이 클래스를 자동으로 받을 수 있는 웹사이트이다.

    • SOAP / XML 파서와 사용 방식이 상당히 유사하지만, XML이 아니라 JSON을 다룬다.
    • 우리가 직접 해당 클래스를 만들기 위해 수작업으로 코드를 작성할 수도 있지만,
      • 많은 API들이 방대한 JSON을 사용하고 있는데,
      • 그 JSON을 모두 클래스로 만들 기 위해 수작업으로 코드를 쓰려면,
      • 시간도 많이 걸리고 너무 수고스럽다.
    • 무료 서비스인 이 웹사이트를 이용하면
      • 그 모든 수고를 덜 수 있다.
      • 생성된 클래스의 전체 소스 코드를 다운로드 할 수 있으므로, 필요한 부분을 직접 변경할 수도 있다.
    // [JsonToDelphi에서 만들어 준 클래스 사용하여 읽기]
    // 제공한 JSON 샘플의 구조가 모두 반영된 클래스가 생성되므로, 그냥 사용하면 된다.
    var
      TesteJson: TRoot;
      Inputs: TInputs;
    begin
      // JsonToDelphi가 만들어준 클래스 안에 JSON을 읽어서 넣기 
      TesteJson := TJson.JsonToObject<TRoot>(MemoJson.Lines.Text);
    
      // 데이터 타입까지 이미 알맞게 지정된 해당 클래스의 프로퍼티를 통해 JSON 필드를 접근
      ShowMessage('BlockHash - '+TesteJson.BlockHash);
    
      for Inputs in TesteJson.Inputs do
      begin
        Showmessage('ScripType - '+Inputs.ScriptType);
      end;
    end;
    // [JsonToDelphi에서 만들어 준 클래스 사용하여 쓰기]
    var
      TesteJson: TRoot;
      Inputs: TInputs;
      I: Integer;
    begin
      // JsonToDelphi에서 만들어 준 클래스를 생성하고
      TesteJson := TRoot.Create;
      // 프로퍼티를 이용하여 값을 쓴다.
      TesteJson.BlockHash := 'www.aquasoft.com.br';
    
      // 마찬가지 방법으로 배열을 돌면서 값을 쓸 수도 있다.
      for I := 0 to 2 do
      begin
        Inputs := TInputs.Create;
        Inputs.Age := 20;
        Inputs.Script := 'teste script';
    
        TesteJson.Inputs.Add(Inputs);
      end;
    
      // 단 한줄로 JSON 데이터를 문자로 풀어낼 수 있다.
      MemoResultado.Lines.Text :=
        //이 때 joIgnoreEmptyStrings 옵션을 사용하면, 값이 빈 문자열인 데이터는, JSON 필드를 만들 지 않고 건너뛴다.
        // joIgnoreEmptyStrings는 델파이의 TJsonOption의 일종이다 (아래 본문의 설명 참조)
        TJson.ObjectToJsonString(TesteJson,[joIgnoreEmptyStrings]);
    end;

     

    JSON 직렬화 행위를 변경하는 옵션(들)

    델파이의 REST.Json.TJsonOption

    • https://docwiki.embarcadero.com/Libraries/Sydney/en/REST.Json.TJsonOption
    • JSON 생성 작동 방식 중 원하는 옵션을 선택할 수 있다.
      • 예: 유닉스 날짜 포맷 사용하기, UTC 날짜 사용, 들여쓰기, 카멜 표기, 등등 
    • 델파이 버전이 최신일수록 선택할 수 있는 직렬화 옵션이 더 많다. 
      • 이전 버전이라서 필요한 옵션이 없다면, 원하는 작동을 하는 코드를 수작업으로 써 넣는다.

    TJsonOption.png
    [그림. 델파이 11.0 알렉산드리아의 TJsonOption (참고: 이전 버전에는 옵션이 이것처럼 많지 않다)]

     

    3. Super Object (오픈소스)

    델파이 6, 7 등 구버전에서 JSON을 다룰 수 있음

    • 무료 오픈 소스: https://github.com/hgourvest/superobject
    • 델파이가 오래된 버전이지만, API 연동을 위해 JSON을 다루어야 한다면
      • 이런 역할을 하는 DLL 또는 패키지의 도움을 받을 수 있다.
      • 하지만, 직접 코드로 구현하려면 Super Object를 사용할 수 있다.
    • 주의! 앞에서 설명했듯이, 최신 버전에는 이미 이런 기능들이 들어있으므로, Super Object를 굳이 사용할 이유가 없음 

     

    // [SuperObject를 사용하여 읽기]
    var
      Objeto: ISuperObject;
      Inputs: ISuperObject;
    begin
      // SuperObject의 SO 메소드를 사용하여 오브젝트를 생성
      Objeto := SO(MemoJson.Lines.Text);
      // S 메소드는 원하는 필드와 값을 마치 배열처럼 접근  
      ShowMessage(Objeto.S['block_hash']);
      ShowMessage(Objeto['block_hash'].AsString);
      // for 루프 사용 가능 (for in 루프는 델파이 7 같은 구버전에는 없지만, for 문으로 유사하게 구현하면 된다.)  
      for Inputs in objeto['inputs'] do
      begin
        MemoResultado.Lines.Add(Inputs.AsString);
      end;
    end;
    
    // [델파이 구버전에서 SuperObject를 사용하여 쓰기]
    var
      Objeto: ISuperObject;
      Inputs: ISuperObject;
    begin
      // 오브젝트 생성
      Objeto := SO;
      // 데이터 쓰기 역시 마치 배열을 다루듯이 사용
      Objeto.S['block_hash'] := '123456789';
      Objeto.O['inputs'] := SA([]);
    
      Inputs := SO;
      Inputs.S['script'] := 'Teste script 1';
      Inputs.I['age'] := 30;
      // JSON 배열을 쓸 수도 있다.
      Objeto.A['inputs'].O[0] := Inputs;
    
      Inputs := SO;
      Inputs.S['script'] := 'Teste script 2';
      Inputs.I['age'] := 60;
    
      Objeto.A['inputs'].O[1] := Inputs;
    
      // AsString 메소드를 사용하여 JSON으로 쓸 수도 있다.
      MemoResultado.Lines.Text := Objeto.AsString;
    end;

     

    4. RESTResponseDataSetAdapter 컴포넌트

    RESTResponse에 받은 JSON 데이터를 FDMemTable 등의 데이터셋에 넣어주는 컴포넌트 (델파이 최근 버전에서 추가됨)

    REST Debugger.png
    [그림. REST 디버거를 사용하면 더 쉽게 만들 수 있다]

    REST API를 통해서 JSON 데이터를 받아서 델파이 폼에 표현하기 

    • REST 디버거 (REST API의 읽기와 쓰기를 테스트하는 무료 도구)를 실행한다.
    • 테스트가 성공했다면, [Copy Components] 버튼을 클릭한다.
      • 그러면, 해당 REST API를 다루도록 설정이 완료된 델파이 컴포넌트가 클립보드에 복사된다.
        • RESTClient: REST 서비스 제공자에게 요청을하고 응답을 받는  사용자 오브젝트
        • RESTRequest: RESTClient가 REST 서비스 제공자에게 전달하는 요청
        • RESTResponse: RESTClient가 REST 서비스 제공자로부터 전달받은 응답
        • RESTResponseDataSetAdapter: 전달받은 응답을 FDMemTable 등의 데이터셋에 넣어주는 매개체
        • FDMemTable: 데이터셋
    • 델파이 폼에서 붙여넣기를 하면, 복사된 컴포넌트가 모두 추가되고, 되델파이 프로젝트에서 바로 사용할 수 있다.
    • 그 후에는, 일반 데이터와 마찬가지로 FDMemTable을 사용하면 된다.
      • 함께 복사된 REST 관련 컴포넌트들이 자동으로 이 데이터셋에 JSON 데이터를 담는다.
      • FDMemTable 이 가장  좋고 편한 데이터셋이기 때문에 사용한다 (원한다면 다른 데이터셋을 사용해도 된다)
    • 예를 들어, 데이터가 표현될 그리드의 데이터소스 즉 DataSource 컴포넌트의 DataSet 프로퍼티에 FDMemTable를 지정하면 된다.
    • 직접 작성해야할 코드는 2줄 뿐이다 (아래 코드 참조)
    procedure TForm1.Button5Click(Sender: TObject);
    begin
      // REST 요청을 한다
      RESTRequest1.Execute;
      // RESTResponse에 받은 JSON 데이터를 FDMemTable 등의 데이터셋에 넣기
      RESTResponseDataSetAdapter1.Active := True;
    end;

     

    5. TJSONMarshal 클래스

    JSON 데이터 직렬화 알고리즘을 커스터마이징 할 수 있다.

     

    // [TJSONMarshal을 사용하여 맞춤 JSON 구현하기]
    var
      lMarshal : TJSONMarshal;
      TesteJson: TRoot; //JsonToDelphi에서 제공하는 SuperObject 타입을 사용
      Inputs: TInputs;
      I: Integer;
    begin
      TesteJson := TRoot.Create; // 오브젝트 생성 (SuperObject)
      TesteJson.BlockHash := 'www.aquasoft.com.br';
    
      for I := 0 to 2 do
      begin
        Inputs := TInputs.Create;
        Inputs.Age := 20;
        Inputs.Script := 'teste script';
    
        TesteJson.Inputs.Add(Inputs);
      end;
    
      // JSONMarshal을 생성할 때 컨버터를 지정 (커스터마이징을 위해 컨버터를 이용하는 것은 뒤에서 설명)
      lMarshal := TJSONMarshal.Create (TJSONConverter.Create);
      // Marshal 메소드를 사용하면 컨버터에 지정된 대로 컨버전 된다.
      MemoResultado.Lines.Text := lMarshal.Marshal(TesteJson).ToString();
    
      lMarshal.Free;
    end;

     

    JSON 커스터마이징 방식 1JSONMarshalled 어트리뷰트 사용

    [JSONMarshalled(False)] // 이 어트리뷰트에서 False로 지정된 것, 즉 FScript는 생성될 JSON의 필드에 들어가지 않는다.
    FScript: string;

    JSON 커스터마이징 방식 2RegisterConverter 프로시저를 오버로딩하기

    • 프로시저 오버로딩을 통해 파서인 TJSONMarshal 클래스의 기본 전환 방식을 덮어쓰는 방법
    • JSON의 특정 필드의 데이터 타입 바꾸기 등 얼마든지 처리 로직을 바꿀 수 있다. (굳이 RTTI를 건드리지 않아도 된다)
    • 좀 복잡하긴 하지만, 필요하다면 이처럼 TJSONMarshal 클래스를 생성할 때 컨버터를 파라미터로 넣는 방식을 쓸 수 있다.
    •  

    << DelphiCon 2021 목록으로 이동

     


    User Feedback

    Recommended Comments

    There are no comments to display.


×
×
  • Create New...

중요한 정보

이용약관 개인정보보호정책 이용규칙 이 사이트가 더 잘 작동하기 위해 방문자의 컴퓨터에 쿠키가 배치됩니다. 쿠키 설정 변경에서 원하는 설정을 할 수 있습니다. 변경하지 않으면 쿠키를 허용하는 것으로 이해합니다.