Jump to content
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr ×
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr

[아티클]델파이에서 JSON을 다룰 때 당신이 몰랐던 것들


Recommended Posts

<< 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 목록으로 이동

 


View full 엠바카데로 기술자료

이 댓글 링크
다른 사이트에 공유하기

이 토의에 참여하세요

지금 바로 의견을 남길 수 있습니다. 그리고 나서 가입해도 됩니다. 이미 회원이라면, 지금 로그인하고 본인 계정으로 의견을 남기세요.

Guest
이 토픽(기고/질문)에 답하기

×   서식있는 텍스트로 붙여넣기.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   이전에 작성한 콘텐츠가 복원되었습니다..   편집창 비우기

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

중요한 정보

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