Kori 11월 17일, 2021에 포스트됨 공유하기 11월 17일, 2021에 포스트됨 << 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 Barreto는 Aquasoft 선임 개발자이며, Dion Carlos Mai은 Aquasoft 개발팀장입니다. 이 세션에서 사용된 전체 소스코드: https://github.com/gustavomenabarreto/delphicon2021 목차 델파이에서 JSON 다루기를 주제로 정한 이유 JSON을 다룰 때 델파이에서 선택할 수 있는 것들 이 세션에서 사용할 JSON 샘플 JSON을 사용하기 위해 필요한 델파이 유닛(들) 1. 가장 간단한 방법 JSON 읽기 (아래 코드와 주석 참조) JSON 쓰기 (아래 코드와 주석 참조) 2. JsonToDelphi JSON 직렬화 행위를 변경하는 옵션(들) 3. Super Object (오픈소스) 4. RESTResponseDataSetAdapter 컴포넌트 REST API를 통해서 JSON 데이터를 받아서 델파이 폼에 표현하기 5. TJSONMarshal 클래스 델파이에서 JSON 다루기를 주제로 정한 이유 요즘 개발자에게 JSON은 매우 중요한 자리를 차지하고 있다. 요즘은 API 통합이 너무나 많다. 심지어 NoSQL JSON 데이터베이스도 있다. JSON을 다루는 방법이 여러가지 라는 사실을 잘 모르는 개발자들이 있다. 이 세션에서는 JSON을 다루는 여러 가지 방법을 설명한다. JSON을 다룰 때 델파이에서 선택할 수 있는 것들 가장 간단한 방법 JsonToDelphi: JSON 오브젝트를 가지고 델파이 클래스를 자동 생성하는 웹사이트 Super Object (오픈소스): 델파이 6, 7 등 구버전에서 JSON을 다룰 수 있음 (최신 버전 사용자에게는 권장하지 않음) RESTResponseDataSetAdapter 컴포넌트: JSON을 기반으로 데이터셋을 자동 생성 TJSONMarshal 클래스: JSON 직렬화 처리를 커스터마이징할 때 사용 이 세션에서 사용할 JSON 샘플 그림. 이 세션의 코드는 비트코인 블록체인에서 사용되는 매우 구조가 큰 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 날짜 사용, 들여쓰기, 카멜 표기, 등등 델파이 버전이 최신일수록 선택할 수 있는 직렬화 옵션이 더 많다. 이전 버전이라서 필요한 옵션이 없다면, 원하는 작동을 하는 코드를 수작업으로 써 넣는다. [그림. 델파이 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 디버거를 사용하면 더 쉽게 만들 수 있다] 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 데이터 직렬화 알고리즘을 커스터마이징 할 수 있다. RTTI를 직접 건드릴 필요가 없기 때문에 보다 안전하고 편하다. RTTI를 잘못 다루면 쓰레드 문제가 발생할 수 있다. JSON을 다룰 때는 쓰레드를 사용하고 JSON 구조를 알맞게 맞추어 쓰는 것이 좋다. 보다 자세한 내용은 사용자 오브젝트 직렬화에 대한 상세한 Docwiki 도움말을 참고하기 바란다. // [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 커스터마이징 방식 1: JSONMarshalled 어트리뷰트 사용 [JSONMarshalled(False)] // 이 어트리뷰트에서 False로 지정된 것, 즉 FScript는 생성될 JSON의 필드에 들어가지 않는다. FScript: string; JSON 커스터마이징 방식 2: RegisterConverter 프로시저를 오버로딩하기 프로시저 오버로딩을 통해 파서인 TJSONMarshal 클래스의 기본 전환 방식을 덮어쓰는 방법 JSON의 특정 필드의 데이터 타입 바꾸기 등 얼마든지 처리 로직을 바꿀 수 있다. (굳이 RTTI를 건드리지 않아도 된다) 좀 복잡하긴 하지만, 필요하다면 이처럼 TJSONMarshal 클래스를 생성할 때 컨버터를 파라미터로 넣는 방식을 쓸 수 있다. << DelphiCon 2021 목록으로 이동 View full 엠바카데로 기술자료 인용하기 이 댓글 링크 다른 사이트에 공유하기 더 많은 공유 선택 사항
Recommended Posts
이 토의에 참여하세요
지금 바로 의견을 남길 수 있습니다. 그리고 나서 가입해도 됩니다. 이미 회원이라면, 지금 로그인하고 본인 계정으로 의견을 남기세요.