Flutter Json (Json_serializable)
Updated:
Json
- 모든 형태의 웹 / 모바일 App 은 API 와 통신하기 위해 Json 을 필수 적으로 사용되게 됩니다. 그러나, Json 의 데이터의 양이 많아 질 수록 구조가 점점 복잡하게 되어져서 사용하기 불편할때가 많습니다. (nesting 형태로 key : value 에서 value 가 다른 object 를 가져서 하위로 계속 key: value 형태로 가지치기가 될때 데이터의 양이 많아질 수 록 관리하기기 복잡하게 됩니다)
Json 에 대해 간단 정리
-
Most widely used dat format for data interchange on the web interface
-
Primary data structure of Json
-
A collection of name / value pairs
-
An ordered list of values
-
-
Json seperator / token
- ”:”(between name and value), “,”(between name / value pairs), “{“,”}” (for object), “[”,”]”(for arrays)
-
Json values
- String, numbers, objects, arrays, booleans, null(or empty)
-
특히, flutter 에서 Json 을 사용할때 map 을 사용해서 string type 으로 사용하는데 Json 의 value 의 type 이 dynamic 이 되기 때문에 나중에 type checking 할 때, 에러가 많이 발생 됩니다. 그래서 그것을 방지하고자 주로, class 를 만들어서 converting 하는데 두가지 function 을 만드는데,
fromJson()
,toJson()
이라는 method 를 만듭니다. 근데 그 만드는 과정이 데이터가 많을 경우 코드의 양이 상당히 많아지고 시간도 많이 소요 됩니다 -
이러한 과정을 자동화 할 수 있고 error 를 미리 방지하기 위해서 flutter 에서는 주로 Json_serializable package 를 사용합니다
serialization, deserialization ?
-
Encoding == serialization == data structure 를 string 타입으로 변경
-
Decoding == deserialization == string 타입을 data structure 로 변경
Json 을 serialization 하는 2가지 방법
-
Manual Serialization (수동 직렬화) -
dart:convert
사용해서 직접 코드를 작성해서 직렬화 하기 -
Automated serialization using code generation (코드 생성을 통한 자동 직렬화) - json_serializable, built_value 등 라이브러리를 사용해서 Json 직렬화 하기
Manual Serialization
- 데이터 양이 얼마 없을때 주로 사용합니다
예시
var jsonString = '{
"name" : "Jacob ko",
"email" : "jacobko@info.com",
"age" : 30
}'
위의 Json 형식의 데이터를 serialization 하기위해서 다음과 같이 진행 합니다.
-
User 클래스 생성
-
User.formJson() 생성자를 만들어서 map 으로 부터 User 를 생성합니다
-
User.toJson() 반대로 User 를 map 형태로 변환 시킵니다
// Manual Serialization 예시
// user.dart
class User {
final String name;
final String email;
final int age;
User({required this.name, required this.email, required this.age});
// fromJson
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email'],
age = json['age'];
// toJson
Map<String, dynamic> toJson() => {'name': name, 'email': email, 'age': age};
}
Map userMap = jsonDecode(jsonString);
var user = User.fromJson(userMap);
print('How are you ${user.name}');
print('We sent the verification link to ${user.email}.');
String json = jsonEncode(user);
json_serializable package 설치
dependencies:
# 실제 deploy 할때 json 형태 사용되는 package
json_annotation:
dev_dependencies:
# build_runner: json_serializable 의 spec 에 맞게 data 구조를 만들고, 변환 시킬때 사용되는 package
build_runner:
json_serializable:
json_serializable 을 사용해 json 데이터를 처리하기
import 'package:json_annotation/json_annotation.dart';
// This allows the `User` class to access private members in
// the generated file. The value for this is (파일명:*).g.dart, where
// the star denotes the source file name.
// 자동화 생성 되는 파일명 제시 (대부분 이러한 패턴으로 생성됨)
part 'user.g.dart'; // 이부분에서 에러표시가 발생함.
// JSON 직렬화 로직이 만들어져야 한다고 알림 어노테이션
@JsonSerializable()
class User {
User(this.name, this.email. this.registDateMill);
String name;
String email;
// @JsonKey를 사용하여 네이밍전략을 알려줄수 있다.
// name: 네이밍 전략, required(t/f): 무조건 포함해야 하는 키 인지 여부,
//defaultValue(t/f): Json에 해당 키가 없어도 되거나 값이 'null'인 경우.
@JsonKey(name: 'registration_date_millis')
final int registrationDateMillis;
/// map에서 User를 생성하기 위한 팩토리 생성자.
/// Pass the map to the generated `_$UserFromJson()` constructor.
/// 생성자의 이름은 클래스 명을 따른다. ( _$+클래스명+FromJson )
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
/// `toJson` JSON 인코딩을 지원을 선언하는 규칙.
/// The implementation simply calls the private, generated
/// helper method `_$UserToJson`. ( _$+클래스명+ToJson)
Map<String, dynamic> toJson() => _$UserToJson(this);
}
-
위의 코드를 작성하고 터미널에서
flutter pub run build_runner build
실행하면 동일 경로에user.g.dart
에 code generation 이 생성된다. 일회성이 아닌 지속적인 변경 감지를 위해watch
모드를 실행하면 된다flutter pub run build_runner watch
실행 -
사용법은 manual 부분과 마찬가지로 실행하면 됩니다
Map userMap = jsonDecode(jsonString);
var user = User.fromJson(userMap);
String json = jsonEncode(user);
json_serializable package 실전 사용 예시
Json 형태의 OpenWeather API 를 예시로 fetch data 를 해서 json_serializable 패키지를 통해 flutter 에서 Json 사용하는 예시 코드 입니다.
// https://api.openweathermap.org/data/2.5/weather?q=seoul&appid=$KEY
{
"coord": {
"lon": 126.9778,
"lat": 37.5683
},
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01n"
}
],
"base": "stations",
"main": {
"temp": 285.09,
"feels_like": 284.06,
"temp_min": 281.38,
"temp_max": 286.88,
"pressure": 1024,
"humidity": 66
},
"visibility": 10000,
"wind": {
"speed": 0.51,
"deg": 150
},
"clouds": {
"all": 0
},
"dt": 1635508436,
"sys": {
"type": 1,
"id": 8105,
"country": "KR",
"sunrise": 1635458015,
"sunset": 1635496672
},
"timezone": 32400,
"id": 1835848,
"name": "Seoul",
"cod": 200
}
// open_weather_screen.dart (UI widget)
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
// json model
import 'package:json/models/open_weather/open_weather.dart';
class OpenWeatherScreen extends StatelessWidget {
// Future type getWeather 함수
Future<OpenWeather> getWeather() async {
try {
const String url =
'http://api.openweathermap.org/data/2.5/weather?q=seoul&appid=$KEY';
final http.Response response = await http.get(url);
final responseData = json.decode(response.body);
final OpenWeather ow = OpenWeather.fromJson(responseData);
// Json 형태로 확인
print(ow.toJson());
return ow;
} catch (err) {
print(err);
throw err;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flat Weather'),
),
// FutureBuilder 생성
body: FutureBuilder(
future: getWeather(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final ow = snapshot.data;
return ......
} else if (snapshot.hasError) {
return Center(
child: Text(
'Error: ${snapshot.error}',
style: TextStyle(
fontSize: 24,
),
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}
// in coord.dart
*/ nesting 부분된 일부분 처리를 위한 model class 생성
"coord": {
"lon": 126.9778,
"lat": 37.5683
},
*/
import 'package:json_annotation/json_annotation.dart';
part 'coord.g.dart';
@JsonSerializable()
class Coord {
final double lon;
final double lat;
Coord({required this.lon, required this.lat});
factory Coord.fromJson(Map<String, dynamic> json) => _$CoordFromJson(json);
Map<String, dynamic> toJson() => _$CoordToJson(this);
}
import 'package:json_annotation/json_annotation.dart';
// 아래의 자동화된 code source 를 coord.g.dart 파일에 넣으라는 것
part 'main.g.dart';
@JsonSerializable()
class Main {
final double temp;
// 실제로 API 에서 불러올때 의 원래의 값을 @JsonKey 로 지정해줘서 camelCase 로 변경해서 사용할 수 있도록 key 값을 매칭 해주는 것임
@JsonKey(name: 'feels_like')
final double feelsLike;
@JsonKey(name: 'temp_min')
final double tempMin;
@JsonKey(name: 'temp_max')
final double tempMax;
final int pressure;
final int humidity;
Main(
{required this.temp,
required this.feelsLike,
required this.tempMin,
required this.tempMax,
required this.pressure,
required this.humidity});
factory Main.fromJson(Map<String, dynamic> json) => _$MainFromJson(json);
Map<String, dynamic> toJson() => _$MainToJson(this);
}
// in weather.dart
import 'package:json_annotation/json_annotation.dart';
// 아래의 자동화된 code source 를 coord.g.dart 파일에 넣으라는 것
part 'weather.g.dart';
@JsonSerializable()
class Weather {
final int id;
final String main;
final String description;
final String icon;
Weather({
required this.id,
required this.main,
required this.description,
required this.icon,
});
factory Weather.fromJson(Map<String, dynamic> json) =>
_$WeatherFromJson(json);
Map<String, dynamic> toJson() => _$WeatherToJson(this);
}
// coord.dart , main.dart, weather.dart 에서 생성된것을 종합하기
import 'package:json_annotation/json_annotation.dart';
import 'package:json_serializable_practice/models/open_weather/coord.dart';
import 'package:json_serializable_practice/models/open_weather/main.dart';
import 'package:json_serializable_practice/models/open_weather/weather.dart';
// 아래의 자동화된 code source 를 coord.g.dart 파일에 넣으라는 것
part 'open_weather.g.dart';
@JsonSerializable(explicitToJson: true)
class OpenWeather {
final Coord coord;
final List<Weather> weather;
final Main main;
final int visibility;
OpenWeather(
{required this.coord,
required this.weather,
required this.main,
required this.visibility});
factory OpenWeather.fromJson(Map<String, dynamic> json) =>
_$OpenWeatherFromJson(json);
Map<String, dynamic> toJson() => _$OpenWeatherToJson(this);
}
🔶 🔷 📌 🔑
Reference
json_serializable pub.dev - https://pub.dev/packages/json_serializable
Heavy Fran - https://youtu.be/DgLLC4hiUgE
자몽이랑꼬부기 For Programming Code!! - https://hellowk1.blogspot.com/2020/10/flutterjson-and-serialization-1.html?view=sidebar
Leave a comment