# จัดการ JSON ใน Dart ง่ายนิ้ดดดเดียว

#Dev#Dart#Flutter#Json#Library

เดี๋ยวนี้ไม่ว่าเราจะเรียก API หรือ Service ใดๆ ก็มักจะมีการส่งค่ากลับมาในรูปแบบของ JSON (Javascript Object Notation) กันหมดแล้ว ในบทความนี้เราจะมากล่าวถึงการจัดการกับ JSON พวกนี้กันครับ

Header

# พื้นฐาน

เราสามารถแปลงค่าไปมาระหว่าง Map ในภาษา Dart กับ JSON String ได้ด้วยคำสั่ง
String jsonEncode(object ) และ dynamic jsonDecode(String)

เช่น

import "dart:convert";

void main() {
  var userJson = '{"name":"Intception","page": "Intception Hideout"}';
  
  Map<String, dynamic> userMap = jsonDecode(userJson);
  print(userMap["name"]);
  print(userMap["page"]);
  
  var userJsonEncoded = jsonEncode(userJson);
  print(userJsonEncoded);
}

จะได้ผลลัพธ์

Intception
Intception Hideout
"{\"name\":\"Intception\",\"page\": \"Intception Hideout\"}"

แปลงค่า Map<String, dynamic>

ถ้าเราอยากได้ Map<String, String> เราสามารถใช้ Map<String, String>.from(userMap) ในการแปลงค่าได้

** โดย userMap มีชนิดเป็น Map<String, dynamic>

# แยกข้อมูลทำเป็น Model

คนที่มาทำงานต่อก็สามารถรู้ได้ถึงโครงสร้างข้อมูลได้ทันทีหลังจากดู Model

การที่เราทำงานกับ JSON นั้นมันจะสร้างความลำบากให้เราได้ถ้าเราเอาสิ่งที่ได้จาก jsonDecode มาใช้โดยตรง เพราะ ตัว IDE หรือ Editor ที่เราใช้ มันจะไม่บอกว่าตัวแปรของเรามีสมาชิกอะไรบ้างอย่างเช่นตัวอย่างด้านล่าง

IDE ของเราจะไม่รู้ว่า userMap มีสมาชิกอะไรบ้าง

import "dart:convert";

void main() {
  var userJson = '{"name":"Intception","page": "Intception Hideout"}';
  
  Map<String, dynamic> userMap = jsonDecode(userJson);
  print(userMap["name"]); // IDE จะไม่รู้ว่า userMap ของเรามีสมาชิกชื่ออะไรให้เรียกได้บ้าง
}

เราจึงจะทำการสร้าง class เพื่อที่จะมาจัดการกับข้อมูล แทนที่จะไปใช้ Map โดยตรง

import 'dart:convert';

class User {
  String name;
  String pageName;

  User(this.name, this.pageName);

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        pageName = json['page'];  // ผมสามารถเปลี่ยนชื่อไม่ต้องใช้ page ก็ได้
  Map<String, dynamic> toJson() => {'name': name, 'page': pageName};
}

void main() {
  var userJson = '{"name":"Intception","page": "Intception Hideout"}';

  var user = User.fromJson(jsonDecode(userJson));
  print(user.name);
  print(user.pageName);

  var userJsonEncoded = jsonEncode(user.toJson());
  print(userJsonEncoded);
}

เมื่อรันดูก็จะต้องได้ผลที่เหมือนเดิมครับ แต่สิ่งที่เราได้เพิ่มมาก็คือ Code Completion นั่นเอง

Title

# ใช้ตัวช่วยสร้าง Model ที่รองรับ JSON

ทีนี้ในโปรเจคที่ใหญ่มากขึ้น การมาสร้าง Model ให้รองรับ JSON ด้วยนั้น อาจจะมีความเยอะและเหนื่อยมากเกินไป

เราจึงจะมาใช้ Library ที่ช่วยในการ Generate Code ให้กับเรากันครับ โดย Library ที่จะใช้นั้นมีชื่อว่า build_runner และ json_serializable

สามารถเริ่มติดตั้งได้โดยการเพิ่ม dependencies ตามนี้ครับ

...
dependencies:
  json_annotation: ^3.0.0

dev_dependencies:
  build_runner: ^1.0.0
  json_serializable: ^3.2.0
...

สิ่งถัดไปที่เราจะทำก็คือแยก Model ของเราออกมาใส่ไฟล์แยกครับ ในที่นี้คือไฟล์ models/user.dart

 



 

 



 




 

 


import 'package:json_annotation/json_annotation.dart';

// File ที่จะถูก Generate นั้นจะอยู่ในไฟล์ user.g.dart ครับ
// ชื่อไฟล์/ชื่อ Class/ ชื่อ part ไฟล์ จะเป็นชื่อเดียวกัน *.g.dart
part 'user.g.dart';

()
class User {
  String name;

  (name: 'page')
  String pageName;

  User(this.name, this.pageName);

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  Map<String, dynamic> toJson() => _$UserToJson(this);
}

@JsonSerializable() - เป็นการบอกให้ dart รู้ว่าเราต้องการให้มันสร้าง fromJson กับ toJson ให้เราโดยอ้างอิง Class property และ Constructor
@JsonKey(name: '...') - ไว้กำหนดว่า Class Property นี้ต้องการจะรับค่าจาก Key ชื่ออะไรใน JSON

เมื่อเราสั่งคำสั่ง

flutter pub run build_runner build

หากขี้เกียจกด build บ่อยๆ สามารถใช้คำสั่งนี้แทนได้ครับ

flutter pub run build_runner watch

ก็จะเกิดไฟล์ชื่อ user.g.dart ขึ้นมาครับ

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'user.dart';

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

User _$UserFromJson(Map<String, dynamic> json) {
  return User(
    json['name'] as String,
    json['page'] as String,
  );
}

Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
      'name': instance.name,
      'page': instance.pageName,
    };

ซึ่งประหยัดเวลาในการเขียนส่วนของการแปลง JSON ได้อย่างมากเลยทีเดียว

ทีนี้ไฟล์ main.dart ของเราก็จะเหลือแค่

import 'dart:convert';

import 'models/user.dart';

void main() {
  var userJson =
      '{"name":"Intception","page": "Intception Hideout"}';

  var user = User.fromJson(jsonDecode(userJson));
  print(user.name);
  print(user.pageName);

  var userJsonEncoded = jsonEncode(user.toJson());
  print(userJsonEncoded);
}

เมื่อรันแล้วก็ยังได้ผลเหมือนเดิมอยู่

อัปเดตเมื่อ: 1 ปีที่แล้ว