본문 바로가기

Flutter

[Flutter]JsonSerializable(Json직렬화)

728x90

 

 

@JsonSerializable 어노테이션은 Json 데이터를 직렬화/역직렬화(인코딩/디코딩)를 쉽게 처리하기 위한 어노테이션이다.

 

@JsonSerializable 어노테이션을 사용하면 Json 직렬화 코드를 자동으로 생성한다.

코드의 간결성과 model 데이터가 변경되거나 추가될 때마다 수동으로 업데이트를 해줘야 하는 불편함을 줄어들고 유지보수가 향상된다.

 

 

1. json_sericalizable 추가

프로젝트 터미널에 아래 명령어를 입력한다.

 

flutter pub add json_annotation dev:build_runner dev:json_serializable

 

 

pubspec.yaml에 패키지가 추가된 것을 확인할 수 있다.

 

dependencies:
  flutter:
    sdk: flutter

  json_annotation: ^4.8.1

dev_dependencies:
  flutter_test:
    sdk: flutter

  build_runner: ^2.4.8
  json_serializable: ^6.7.1

 

 

2. Model 클래스 생성

 

*.g.dart 에서 * 에는 파일 이름을 적어준다.

 

*. g.dart 파일은 해당 파일에 필요한 직렬화 및 역직렬화 코드를 추가한다.

이 파일은 자동으로 생성되고 관리되기 때문에 내가 직접 수정하지 않아도 된다.

 

import 'package:json_annotation/json_annotation.dart';

// 파일 이름.g.dart
part 'color_model.g.dart';

@JsonSerializable()
class ColorModel {

  int id;
  String name;
  int year;
  String color;
  @JsonKey(name: 'pantone_value')
  String pantoneVale;

  ColorModel({
    required this.id,
    required this.name,
    required this.year,
    required this.color,
    required this.pantoneVale
  });


  // factory constructor : JSON 데이터를 기반으로 ColorModel 인스턴스를 생성하는데 필요하다.
  // _$ColorModelFromJson(json) : 주어진 JSON 맵을 이용하여 ColorModel의 객체를 자동으로 생성한다.
  factory ColorModel.fromJson(Map<String, dynamic> json) => _$ColorModelFromJson(json);


  // ColorModel 객체를 JSON 형태로 직렬화한다.
  // _$ColorModelToJson(this) : 해당 객체의 필드들이 JSON 키-값 쌍으로 매핑되어진 Map을 반환
  //{
  //  "name": "Red"
  //}
  Map<String, dynamic> toJson() => _$ColorModelToJson(this);
}

 

아직 코드 생성기를 실행하지 않아 에러가 발생한다.

 

3. 코드 생성기(Code Generator) 실행

코드 생성기(Code Generator)를 실행하는 방법은 두 가지가 있다.

 

3.1. One-time code generation

필요할 때마다 모델에 대한 JSON 직렬화 코드를 생성하는 방법이다.

모델 클래스를 변경할 때마다 아래 명령어를 통해 수동으로 빌드를 실행하면 된다.

 

dart run build_runner build --delete-conflicting-outputs

 

이 명령어는 build_runner 패키지를 사용하여 소스 코드를 생성하고 필요한 파일들을 빌드하고 충돌이 발생하는 파일들은 삭제해 준다.

 

3.2. Generating code continuously

프로젝트 파일의 변경 사항을 감시하고 필요할 때 자동으로 필요한 파일들을 빌드한다.

 

dart run build_runner watch --delete-conflicting-outputs

 

이 명령어는 한 번 실행한 다음 백그라운드에서 실행되도록 두는 것이 안전하다고 한다.

 


 

한 번만 빌드되는 명령어를 실행해 보았다.

 

dart run build_runner build --delete-conflicting-outputs

 

에러가 사라지고 color_model.g.dart 파일이 생성되었다.

 

 

 

아래는 생성된 ColorModel.g.dart 파일이다.

 

 

// 수정하지 말라고 적혀있다.
// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'color_model.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

ColorModel _$ColorModelFromJson(Map<String, dynamic> json) => ColorModel(
      id: json['id'] as int,
      name: json['name'] as String,
      year: json['year'] as int,
      color: json['color'] as String,
      
      // @JsonKey 어노테이션을 달았던 것도 잘 매핑되었다.
      pantoneVale: json['pantone_value'] as String,
    );

Map<String, dynamic> _$ColorModelToJson(ColorModel instance) =>
    <String, dynamic>{
      'id': instance.id,
      'name': instance.name,
      'year': instance.year,
      'color': instance.color,
      'pantone_value': instance.pantoneVale,
    };

 

 

만약, 중첩된 클래스가 있다면?

클래스 내에 또 다른 클래스가 있다면 잘못된 인자를 넘길 수도 있다고 한다.

 

다른 Model 클래스 생성해서 테스트를 해보았다.

위 과정과 똑같이 진행했다.

 

 

그럼 user_model.g.dart의 _$UserModelToJson()가 이렇게 만들어진다.

 

 

보기에는 문제가 없지만 출력해 보면 아래와 같다.

 

ColorModel colorModel = ColorModel(
      id: 2,
      name: 'fuchsia rose',
      year: 2001,
      color: '#C74375',
      pantoneVale: '17-2031'
  );

  UserModel userModel = UserModel('John', colorModel);

  print(userModel.toJson());

 

출력 결과 >

 

{name: John, colorModel: Instance of 'ColorModel'}

 

그런데 만약 아래와 같이 출력하고 싶다면,

 

{
  name: John, 
  colorModel: {
    id: 2, 
    name: fuchsia rose, 
    year: 2001, 
    color: #C74375, 
    pantone_value: 17-2031
  }
}

 

@JsonSerializable 어노테이션에 explicitToJson: true를 전달하면 된다.

 

@JsonSerializable(explicitToJson: true)
class UserModel {

  String name;
  ColorModel colorModel;

  UserModel(this.name, this.colorModel);


  factory UserModel.fromJson(Map<String, dynamic> json) => _$UserModelFromJson(json);
  Map<String, dynamic> toJson() => _$UserModelToJson(this);
}

 

 

다시 빌드하고 출력하면 ColorModel 클래스의 데이터를 출력할 수 있다.

 

 

dart run build_runner build --delete-conflicting-outputs

 

 

 

 

 

[참고]

https://docs.flutter.dev/data-and-backend/serialization/json#setting-up-json_serializable-in-a-project

 

JSON and serialization

How to use JSON with Flutter.

docs.flutter.dev

 

 

 

728x90