[Flutter] json_serializable 사용법

홍석·2023년 9월 4일

json_serializable를 사용하는 이유

Flutter에서 서버와 json을 통해 통신을 할때 주로 class.fromJson, class.toJson의 방식으로 변환하여 주고받게 된다.
이를 클래스에서 직접 메소드로 만들어 줘야하는데

이는 클래스의 속성이 조금만 더 커지더라도 써야할 코드가 곱절로 늘어나게 되고, 사람의 실수가 발생하기 쉽고, 모델 클래스의 가독성이 떨어질 수 있다.

 

이를 해당 패키지를 사용함으로써 코드가 간결해지고, 다른 사람이 이를 보았을때도 한눈에 코드의 구조를 파악할 수 있다.

json_serializable은 freezed에서도 사용되는데, freezed는 상속이 아닌 합성을 하는 방식을 택하므로 상속 등 다양한 커스텀이 필요한 상황에서는 json_serializable이 더 나은 선택이 될 수 있다.


사용전

class Person {
  
  final String firstName;
  final String lastName;
  final DateTime? dateOfBirth;

  Person({required this.firstName, required this.lastName, this.dateOfBirth});

  factory Person.fromJson(Map<String, dynamic> json) => Person(
  	firstName: json['firstName'] as String,
      lastName: json['lastName'] as String,
      dateOfBirth: json['dateOfBirth'] == null
          ? null
          : DateTime.parse(json['dateOfBirth'] as String),
  );

  Map<String, dynamic> toJson() => {
      'firstName': instance.firstName,
      'lastName': instance.lastName,
      'dateOfBirth': instance.dateOfBirth?.toIso8601String(),
    };
}

사용후

import 'package:json_annotation/json_annotation.dart';

part 'example.g.dart';

()
class Person {
  final String firstName;
  final String lastName;
  final DateTime? dateOfBirth;

  Person({required this.firstName, required this.lastName, this.dateOfBirth});

  factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
  Map<String, dynamic> toJson() => _$PersonToJson(this);
}

설치

json_annotaion과 json_serializable, build_runner을 설치한다.

dart pub add json_annotation
dart pub add --dev build_runner json_serializable

사용방법

Code Generate을 실행하려면 다음 명령 중 하나를 수행한다.

dart run build_runner build

dart run build_runner watch

대부분의 코드 생성기와 마찬가지로 json_serializable는 어노테이션을 import한다.
그리고 파일 상단에 part 키워드를 적어야한다.(파일명과 동일)

import 'package:json_annotation/json_annotation.dart';

part 'my_file.g.dart';

어노테이션 종류

@JsonSerialiable

explicitToJson

  • 클래스 안에 클래스가 있는경우, toJson에서 이를 알 수 있게 해준다.

genericArgumentFactories

  • 제네릭이 사용되었을시 사용한다.

@JsonKey

name

  • json으로 변환시 key(name)의 이름을 바꿔준다.

fromJson, toJson

  • 값을 변형해서 넘겨줘야 할때, 함수를 통해 바꿔준다.

defaultValue

  • json에서 값이 null일때, 기본값으로 넣어준다.

unknownEnumValue

  • fromJson에서 정해진 enum값이 들어오지 않을시, 기본값으로 넣어준다.

includeFromJson

  • fromJson에서 받아 올지 말지를 결정한다.

includeToJson

  • toJson에서 포함 할지 말지를 결정한다.

includeIfNull

  • null인 변수를 toJson으로 보낼때 true이면 'a':null이라고 보내고, false이면 추가하지 않는다.

disallowNullValue

  • fromJson에서, key가 존재하는데 value가 null일때 에러를 발생시킨다.

@JsonValue

enum 사용시 맵핑될 값을 정해준다.

enum PersonType{
  ('Student')
  student,
  ('Teacher')
  teacher,
}

@JsonEnum

enhance enum을 사용시 JsonValue대신 맵핑을 해주는 용도이다.
즉, 각각의 enum마다 @JsonValue 다는것이 아닌 enum의 속성(field)를 사용하겠다는 의미.

(valueField: 'code') //final int "code"와 같게 써주면 된다.
enum PersonCode{
  man(10),
  women(20);

  const PersonCode(this.code);
  final int code;
}

사용예시1

(genericArgumentFactories: true)
class CursorPagination<T> extends CursorPaginationBase {
  bool hasNext;
  List<T> content;

  CursorPagination({
    required this.hasNext,
    required this.content,
  });

  factory CursorPagination.fromJson(
          Map<String, dynamic> json, T Function(Object? json) fromJsonT) =>
      _$CursorPaginationFromJson(json, fromJsonT);

  CursorPagination<T> copyWith({
    bool? hasNext,
    List<T>? content,
  }) {
    return CursorPagination<T>(
      hasNext: hasNext ?? this.hasNext,
      content: content ?? this.content,
    );
  }
}

사용예시2

import 'package:json_annotation/json_annotation.dart';

part 'person.g.dart';

(explicitToJson: true)
class Person {
  final String name;
  final int age;
  (fromJson: DataUtils.imgUrlToString)
  final String imUrl;
  (name: 'home_phone')
  final String homePhone;
  final PersonType type;
  final School school;

  Person({
    required this.name,
    required this.age,
    required this.imUrl,
    required this.homePhone,
    required this.type,
    required this.school,
  });

  factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
  Map<String, dynamic> toJson() => _$PersonToJson(this);
}

()
class School{
  final String name;
  final String address;
  School(this.name,this.address);
  factory School.fromJson(Map<String, dynamic> json) => _$SchoolFromJson(json);
  Map<String, dynamic> toJson() => _$SchoolToJson(this);
}



(valueField: 'english')
enum PersonType{
  student('Student','학생'),
  teacher('Teacher','선생님');
  const PersonType(this.english,this.korean);
  final String english;
  final String korean;
}

class DataUtils{
  static String imgUrlToString(String url){
    return 'http://$ip$url';
  }
}

const ip = "127.0.0.1:8080";

Reference

https://pub.dev/packages/json_serializable

profile
bayy1216.tistory.com <- 블로그 이전했습니다 🥹

0개의 댓글