ในการแลกเปลี่ยนรับส่งข้อมูลระหว่าง Server ส่วนใหญ่ทั้งการใช้งาน
Mobile App หรือ Web App จะมีการใช้งานข้อมูลในรูปแบบ JSON ที่
เป็นข้อมูล Text ซึ่งสามารถแปลงไปเป็น Object เพื่อเรียกใช้งาน หรือใน Web
ก็จะเรียกว่า JavaScript Object
เนื้อหาในตอนต่อไปนี้ เราจะมาดูเกี่ยวกับการใช้งาน JSON ในภาษา Dart
ว่ามีวิธีการเรียกใช้งานอย่างไรบ้าง
ก่อนเข้าสู่เนื้อหา ขอเพิ่มเติมการใช้งาน Visual Studio Code ในการเขียนโปรแกรมภาษา Dart
ซึ่งจากเดิม เราจะเรียกใช้งานผ่าน Dartpad.dev https://dartpad.dev/ เพื่อทดสอบโค้ดต่างๆ และ
นั่นก็ถือว่าพอสมควรกับการใช้งาน และการแนะนำโค้ดเบื้องต้น ตามเนื้อหาที่ผ่านๆ มา แต่เนื่องจาก
เนื้อหาของเราเริ่มมีความเข้มข้นขึ้น และอาจจะต้องใช้งานบาง library หรือ package เพิ่มเข้ามา รวม
ถึงอาจจะมีการจัดการเกี่ยวกับไฟล์ ดังนั้น เราจะมาพัฒนาโปรแกรมภาษา Dart ต่อโดยใช้งาน VSCode
ให้ทำการติดตั้งทุกอย่างให้เรียบร้อย ตามเนื้อหาบทความด้านล่าง
เริ่มต้นใช้งาน Flutter พัฒนา Nativiely Compiled Apps เบื้องต้น http://niik.in/937
https://www.ninenik.com/content.php?arti_id=937 via @ninenik
ดูในส่วนของการใช้งาน VSCode โดยเฉพาะการติดตั้งที่เกี่ยวกับการใช้งาน Dart แต่ถ้าเป็นไปได้ ให้ทำตามทุกขั้นตอน
และติดตั้งส่วนอื่นๆ ให้ครบถ้วน เพราะ Flutter ก็เป็นส่วนหนึ่งของเนื้อหาการประยุกต์ใช้งาน ภาษา Dart
หากติดตั้งตามตัวอย่างเรียบร้อยแล้ว ให้เราเปิดโปรแกรม VSCode แล้ว ไปที่เมนู View > Command Pallete.. หรือกด
Ctrl + Shift + P แล้ว พิมพ์คว่า Dart แล้วเลือก Dart New Project...
จะขึ้นให้ติดตั้ง Extension เพิ่มเติม ตามรูป กรณียังไม่ได้ทำการติดตั้ง
กด Activate Stagehand รอสักครู่..... จนทำงานเสร็จ ให้เรา เลือกสร้าง Dart New project ใหม่อีกครั้ง จะมีรูปแบบ Template
สำหรับการพัฒนาด้วยภาษา Dart ในที่นี้เราจะใช้เป็น Console Application
ตั้งชื่อ ในที่นี้ใช้ชือว่า "myapp" กด Enter
เลือกโฟลเดอร์ที่ต้องการ ในที่นี้เก็บไว้ในโฟลเดอร์ dart ที่รวมโปรเจ็คเกี่ยวกับ Dart
รอให้โปรแกรมทำงานสักครู่ ตัวโปรแกรมจะทำการดาวน์โหลด และสร้าง Application โปรเจ็คมาให้ นั่นคือเราจะได้ชื่อโฟลเดอร์
"myapp" ที่เป็นโปรเจ็ค dart ของเรา และมีไฟล์ต่างๆ ด้านใน
เราจะสนใจเฉพาะไฟล์ main.dart ในโฟลเดอร์ bin ลบข้อมูลในไฟล์เดิม แล้วพิมพ์
void main(){ print('Hellow world'); }
จากนั้นกด F5 เพื่อรัน คำสั่ง Debug โค้ด ดูการทำงาน ผลลัพธ์จะแสดงที่ส่วนของ Debug Console
ตอนนี้เราพร้อมใช้งาน VSCode ศึกษาและพัฒนาการใช้งานภาษา Dart
การใช้งาน JSON String Data
การใช้งาน JSON String Data ในภาษา Dart สามารถใช้งาน Library และ Package ต่อไปนี้จัดการ ได้แก่
- ใช้งาน dart:convert
- ใช้งาน package:json_serializable
- ใช้งาน package:built_value
สำหรับ dart:convert Library จะใช้สำหรับการใช้งานอย่างง่าย และไม่ได้มีการจัดการเกี่ยวกับข้อมูล JSON ที่ซับซ้อนนัก โดยเป็น
Library ที่ใช้สำหรับ แปลงข้อมูล JSON และ ข้อมูล UTF-8 (การเข้ารหัสตัวอักขระ ที่ข้อมูล JSON จำเป็นต้องใช้)
ส่วน package:json_serializable จะเป็นตัวที่ช่วยให้สามารถจัดการข้อมูล JSON ได้ดีขึ้น รองรับรูปแบบข้อมูล JSON ที่มีโครงสร้าง
ซับซ้อนเช่นมีการ ซ้อนกันของข้อมูลหลายๆ ขั้น ทำให้เราสามารถแปลงข้อมูล JSON มาเป็น Object ที่ถูกต้องและเรียกใช้งานได้ง่าย
รายละเอียดจะได้เพิ่มเติมในแต่ละหัวข้อ และสุดท้าย package:built_value เป็น package ที่เป็นอีกหนึ่งตัวเลือก ที่สามารถใช้แทน
json_serializable package ได้ เราจะได้ลงรายละเอียดในแต่ละตัว ตามความเหมาะสม
การใช้งาน dart:convert
dart:convert library ใช้แปลงข้อมูล JSON และ UTF-8 โดยในการใช้งานกับข้อมูล JSON จะเป็นการแปลงข้อมูลที่เป็นข้อความ
ที่มีรูปแบบโครงสร้างของ Object และอาเรย์หรือชุดข้อมูล ส่วนการใช้งาน UTF-8 จะเป็นการเข้ารหัส ถอดรหัสตัวอักขระในรูปแบบข้อมูล
Unicode
เราสามารถใช้งาน dart:convert ได้ทั้งการพัฒนา web หรือแบบ command line โดยหากมีการใช้งาน ต้องทำการ import ดังนี้
import 'dart:convert';
การ Decoding และ encoding JSON
การแปลงข้อมูล JSON String Data เป็น Dart Object ด้วยการ Decoding สามารถใช้คำสั่ง jsonDecode() ดูตัวอย่าง
การใช้งานการแปลงข้อมูล JSON มาเป็น List หรือ Dart Object
import 'dart:convert'; void main(){ // จำลองข้อมูล JSON String Data ไว้ในตัวแบ่งการกำหนดข้อความ // ในโปรแกรมภาษา Dart ในที่นี้ใช้ตัวคั่น เป็น (''') var jsonString = ''' [ {"score": 40}, {"score": 80} ] '''; // ทำการถอดรหัส โดยใช้คำสั่ง jsonDecode() var scores = jsonDecode(jsonString); // ทดสอบดูชนิดข้อมูล หรือ Dart Object ที่ได้จากการเแปลงข้อมูล print(scores.runtimeType); // List<dynamic> print(scores); // [{score: 40}, {score: 80}] print('First score is ${scores[0]['score']}'); // First score is 40 }
จะเห็นว่าข้อมูลที่ได้จากการแปลง JSON String Data จะได้เป็น List<dynamic> ตามรูปแบบโครงสร้างของข้อมูล ตามตัวอย่างจะได้
เป็น List<Map<String, dynamic>>
การแปลงข้อมูลจาก Dart Object ไปเป็น JSON String Data จะใช้คำสั่ง jsonEncode() ดูตัวอย่างด้านล่างประกอบ
import 'dart:convert'; void main(){ // ตัวแปร sources เป็น List ที่มีข้อมูลที่เป็นแบบ Map และ Set ด้านใน var scores = [ {'score': 40}, {'score': 80}, {'score': 100, 'overtime': true, 'special_guest': null} ]; // ทำการแปลง Dart Objext เป็น JSON String Data var jsonText = jsonEncode(scores); print(jsonText.runtimeType); // String print(jsonText); // [{"score":40},{"score":80},{"score":100,"overtime":true,"special_guest":null}] }
จะเห็นว่า เราใช้คำสั่ง jsonEncode() แปลงข้อมูล Dart Object มาเป็น JSON String Data ซึ่งเป็นข้อมูล String ตามตัวอย่าง
สังเกตว่า JSON จะมี key เป็น String และใช้ double qoute (") กำกับ
โดย Object ที่รองรับการแปลงเป็น JSON ได้แก่ int, double, String, bool, null, List หรือ Map (ที่มี key เป็น String)
การแปลงข้อมูลประเภท List และ Map จะเป็นลักษณะการวนทำซ้ำจนครบรายการ
กรณีที่เป็น Object ที่ไม่สามารถแปลงเป็น JSON โดยตรง สามารถแก้ไขได้ใน 2 แบบ คือแบบแรก เพิ่มฟังก์ชั่นสำหรับจัดการการ
ข้อมูลให้เป็นชนิดที่สามารถแปลงเป็น JSON ได้ เข้าไปเป็น argument ตัวที่ 2 ของคำสั่ง jsonEncode(arg1, arg2) หรือแบบที่ 2
สร้างฟังก์ชั่นใน model class ข้อมูล หรือในแบบจำลองข้อมูล ชื่อวา toJson() เมื่อเราเรียกใช้คำสั่ง jsonEncode() ฟังก์ชั่น toJson()
จะถูกเรียกใช้งานอัตโนมัติ
มาดูตัวอย่างข้อมูล Object ที่ไม่สามารถแปลงเป็น JSON ได้โดยตรง เช่น Address Object ตาม medel class ดังนี้
void main(){ // สร้าง Address Object var address = Address('My st.','New York'); // ทดสอบแปลง Address Object เป็น JSON จะเกิด Error ขึ้น var jsonText = jsonEncode(address); // JsonUnsupportedObjectError (Converting object to an encodable object failed: // Instance of 'Address') } class Address { String street; String city; Address(this.street, this.city); }
วิธีแก้ปัญหาแบบแรก
void main(){ var address = Address('My st.','New York'); // กำหนดรูปแบบข้อมูลที่สามารถแปลงเป็น JSON ได้ var encodableData = { 'street': address.street, 'city': address.city }; // เพิ่ม argument ตัวที่ 2 return ค่าข้อมูลที่สามรถแปลงเป็น JSON ได้ var jsonText = jsonEncode(address, toEncodable:(address){ return encodableData; }); print(jsonText.runtimeType); // String print(jsonText); } class Address { String street; String city; Address(this.street, this.city); }
วิธีแก้ปัญหาแบบที่สอง
void main(){ var address = Address('My st.','New York'); var jsonText = jsonEncode(address); print(jsonText.runtimeType); // String print(jsonText); } class Address { String street; String city; Address(this.street, this.city); // เพิ่มคำสั่ง toJson() คืนค่าเป็นข้อมูล Map<String, dynamic> // ที่สามารถแปลงเป็น JSON ได้ คำสั่งนี้จะถูกเรียกใช้งานอัตโนมัติ Map<String, dynamic> toJson() => { 'street': street, 'city': city, }; }
จะเห็นว่าวิธีที่ 2 เป็นรูปแบบที่ง่ายกว่า และเวลาเรียกใช้งาน ก็ใช้รูปแบบเดิม ไม่ต้องเพิ่มเติม argument
เราอาจจะเพิ่มเติม โดยการกำหนด named constructor ที่ทำให้สามารถสร้าง Object โดยใช้ข้อมูลที่สามารถแปลงเป็น JSON
หรือข้อมูล ในรูปแบบ Map<String, dynamic> ตัวอย่างเช่น
void main(){ // var address = Address('My st.','New York'); // สร้าง object โดยใช้ named constuctor var address = Address.fromJson({ 'street': 'My st.', 'city': 'New York' }); var jsonText = jsonEncode(address); print(jsonText.runtimeType); // String print(jsonText); } class Address { String street; String city; Address(this.street, this.city); Address.fromJson(Map<String, dynamic> json) : street = json['street'], city = json['city']; // named contructor แบบเต็ม จากด้านบน /* Address.fromJson(Map<String, dynamic> json) { street = json['street']; city = json['city']; } */ Map<String, dynamic> toJson() => { 'street': street, 'city': city, }; }
ถึงแม้ว่า จะจัดการกับ Object ที่ไม่สามารถแปลงเป็น JSON โดยตรงได้ ด้วย 2 วิธีข้างต้น ซึ่งเหมาะกับการใช้งานข้อมูลที่ไม่ซับซ้อน
และ การใช้งาน dart:convert ก็ถือว่าเพียงพอ สำหรับกรณีข้างต้น แต่ถ้าโปรแกรมของเรา มีความซับซ้อนมากขึ้น มีโครงสร้างข้อมูล
ที่ซับซ้อน เช่นข้อมูลในรูปแบบที่ซ้อนกันหลายๆ ชั้น ยกตัวอย่าง
{ 'name': 'Ebiwayo', 'address':{ 'street':'My St.', 'city':'New York' } }
และส่วนใหญ่ข้อมูลที่ใช้งานผ่าน Server ที่เป็น REST API หรือ Web Service ต่างๆ ก็จะมีลักษณะคล้ายๆ กับตัวอย่าง มีโครงสร้างข้อมูล
ที่ซ้อนกันหลายๆ ชั้น ดังนั้น จึงจำเป็นที่จะต้องใช้งาน Package ตัวอื่นเพิ่มเติม เพื่อให้การทำงานสะดวก และมีประสิทธิภาพในการจัดการ
ข้อมูล JSON มากขึ้น หัวข้อถัดไป เราจะไปดูต่อที่ การใช้งาน package:json_serializable
ก่อนไปหัวข้อถัดไป ข้อเพิ่มเติมการใช้งาน dart:convert ในส่วนของการแปลงข้อมูล UTF-8 เล็กน้อยดังนี้
เราสามารถแปลงข้อมูล String เป็นข้อมูล Unicode เข้ารหัส UTF-8 ด้วยคำสั่ง utf8.encode(mystr) โดยจะ return ค่ากลับมาเป็น
ข้อมูลประเภท List<int> หรือ อาเรย์ของกลุ่มตัวเลข interger หรือฐาน 10 ของแต่ละอักขระ เช่น
String mystr = 'ABC'; List<int> encoded = utf8.encode(mystr); print(encoded); // แสดงข้อมูล [65, 66, 67] encoded.forEach(print); // วนลูปแสดงแต่ละค่า
ข้อความ 'ABC' ประกอบด้วยตัวอักษร A B และ C มีทั้งหมด 3 ตัวอักษร ตัวอักขระละ 1 bytes (8 bit)
Hexadecimal: 0x41 0x42 0x43 Decimal: 65 66 67
มาดูกรณีเป็นภาษาไทย ตัว "ภ" ตัวเดียว จะเป็นเท่าไหร่
Hexadecimal: 0xe0 0xb8 0xa0 Decimal: 224 184 160
สังเกตว่าตัวอักษรภาษาไทย 1 ตัว มีค่าเท่ากับ 3 byte แต่ตัวอักษรภาษาอังกฤษ 1 ตัวเท่ากับ 1 byte
การใช้งาน UTF-8 encoder/decoder ก็คือการแปลงข้อมูลระหว่างข้อความ String กับ Byte
เราสามารถแปลงข้อมูลที่เข้ารหัส UTF-8 ที่อยู่ในรูปแบบ List<int> หรืออาเรย์ของข้อมูลตัวเลข ที่อาจจะเป็น Hexademical หรือ
Decimal มาเป็นข้อความ String โดยใช้คำสั่ง utf8.decode(utf8Bytes) ดูตัวอย่างเราแปลงข้อมูล 'ABC' ที่เป็น
ฐาน 16 กลับมาเป็นข้อความดังนี้
void main(){ var utf8HexBytes = [ 0x41, 0x42, 0x43 ]; var myStr = utf8.decode(utf8HexBytes); print(myStr); // ABC }
เราสามารถแปลงข้อมูล Stream ที่เข้ารหัส UTF-8 เป็นข้อความ String โดยใช้งานดังนี้
หากไม่เข้าใจเกี่ยวกับข้อมูล Stream ดูเพิ่มเติมได้ที่บทความ
ข้อมูล Stream การสร้าง และใช้งาน Stream ในภาษา Dart เบื้องต้น http://niik.in/962
https://www.ninenik.com/content.php?arti_id=962 via @ninenik
import 'dart:convert'; void main() async { var stream = genStream(); // จำลองสร้างข้อมูล Stream // ทำการถอดรหัสข้อมูล UTF8 และแยกข้อมูลเป็นบรรทัดๆ (ถ้ามีหลายบรรทัด) // ตัวแปร lines คือ Stream ข้อมูลใหม่ที่ได้จากการแปลงข้อมูลด้วย Transformer var lines = utf8.decoder .bind(stream) .transform(LineSplitter()); try { // เรียกใช้งานข้อมูล Stream await for (var line in lines) { print(line); // ABC print('Got ${line.length} characters from stream'); // Got 3 characters from stream } } catch (e) { print(e); } } // ฟังก์ชั่น จำลองสร้างข้อมูล Stream เป็น List<int> เลขฐาน Stream<List<int>> genStream() async* { // ข้อมูลข้อความ "ABC" เข้ารหัส UTF8 ใน List<int> var utf8Bits = [ 0x41, 0x42, 0x43 ]; // หน่วงเวลา 1 วินาที await Future.delayed(const Duration(seconds: 1)); yield utf8Bits; // ส่งออกข้อมูล Stream }
จากตัวอย่างข้างต้น เราสร้างฟังก์ชั่นชื่อ genStream() ที่ทำการส่งออกข้อมูล Stream เป็น List<int> รายการเข้ารหัสข้อมูล UTF8
ในตัวอย่างคือค่าเข้ารหัส UTF8 ในเลขฐาน 16 ของตัว "ABC"
เราทดลองใช้งานโดยสร้างตัวแปรชื่อ stream รับค่าข้อมูล Stream จากนั้นใช้ตัว utf8.decoder ซึ่งเป็นตัว Transformer หรือตัว
แปลงข้อมูล จับ Stream ที่ต้องการแปลงด้วยคำสั่ง bind() เริ่มต้น ตัว utf8.decoder จะทำการแปลงข้อมูลใน Stream จาก UTF8 ก่อน
แล้ว ต่อมาก็ใช้ฟังก์ชั่น transform() แปลงเป็นข้อมูล Stream ใหม่ โดยแยกๆ เป็นบรรทัดๆ ตามการขึ้นบรรทัดใหม่ของข้อมูล (ถ้ามี)
ด้วยคำสั่ง LineSplitter()
ในขั้นตอนการแปลงข้อมูล Stream เราสามารถใช้ฟังก์ชั่น transform() ทั้งสองครั้งก็ได้ โดยกำหนด Transfromer ที่ต้องการใช้งาน
var lines = stream.transform(Utf8Decoder()) .transform(LineSplitter());
Transfromer ที่เราจะคุ้นตาบ้างก็เช่น JsonDecoder(), JsonEncoder(), LineSplitter(), Utf8Decoder(), Utf8Encoder(),
AsciiDecoder(), AsciiEncoder(), Base64Decoder(), Base64Encoder(), HtmlEscape() เป็นต้น
สามารถดู property และ method และการใช้งานเพิ่มเดิมได้ที่ dart:convert library
การใช้งาน package:json_serializable
จากที่เกริ่นในหัวข้อการใช้งาน dart:convert เพื่อจัดการข้อมูล JSON ที่ว่าเมื่อโปรแกรมของเรามีความซับซ้อน โครงสร้างข้อมูล JSON
มีโครงสร้างการซ้อนกันหลายๆ ชั้น การใช้งาน dart:convert อาจจะไม่เพียงพอ และไม่สะดวกในการจัดการ ดังนั้น จึงมีอีกวิธีการ นั่น
ก็คือการใช้งาน json_serializable package
json_serializable เป็น package ที่ช่วยสร้าง generate code หรือสร้างโค้ดสำหรับกำหนดรูปแบบข้อมูลในการใช้งาน และจัดการ
ข้อมูล JSON โดยในการ generate code จะใช้วิธีการ กำหนดคำอธิบาย หรือคำจำกัดความ ที่ตัว package มีมาให้ เพื่อสร้างโค้ดจากคำ
หรือหมายเหตุนั้นๆ เช่น ใช้ข้อความ "@JsonSerializable" สำหรับสร้างโค้ด ถอดรหัสหรือเข้ารหัส JSON สามารถใช้ข้อความ "@JsonKey"
เพื่อกำหนดฟิลด์ข้อมูลที่ต้องการ เป็นต้น
ก่อนใช้งาน ให้เราทำการติดตั้ง package ก่อน โดยแก้ไขไฟล์ pubspec.yaml ดังนี้
เราทำการเพิ่ม json_serializable: ^3.2.5 เข้าไปในส่วนของ dependencies เสร็จแล้วกด Save ตัว VSCode จะทำการดาวน์โหลด
และติดตั้งให้อัตโนมัติ หรือถ้าไม่มีการทำงานอัตโนมัติ หรือไม่มีอะไรเกิดขึ้น ให้ใช้คำสั่ง flutter pub get ในส่วนของ Terminal ของโปรเจ็ค
โดยนอกจากจะดาวนโหลด json_serializable package ที่ใช้ generate code แล้ว ก็ยังดาวน์โหลด json_annotation package ที่ใช้
กำหนดคำอธิบาย หรือคำจำกัดความสำหรับใช้ในการ generate code ด้วย
ในหัวข้อที่ผ่านมาเราสร้าง Address model class หรือต้นแบบข้อมูลของ Address ทบทวนโค้ดดังนี้
class Address { String street; String city; Address(this.street, this.city); Address.fromJson(Map<String, dynamic> json) : street = json['street'], city = json['city']; Map<String, dynamic> toJson() => { 'street': street, 'city': city, }; }
ทีนี้เราจะใช้วิธีการ generate code แทน เริ่มตนให้เราสร้างไฟล์ address.dart และกำหนดโค้ดดังนี้
import 'package:json_annotation/json_annotation.dart'; part 'address.g.dart'; @JsonSerializable() class Address { String street; String city; Address(this.street, this.city); factory Address.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json); Map<String, dynamic> toJson() => _$AddressToJson(this); }
จะเห็นโค้ดข้างต้น จะมี error ขึ้นเตือน เพราะยังไม่สมบูรณ์ เราจะใช้ไฟล์นี้ในการ generate code แต่ก่อนอื่น ให้เราติดตั้ง
build_runner packgage ก่อน ทำเหมือนเดิมในไฟล์ pubspec.yaml
จริงๆ แล้วเราสามารถกำหนดทั้ง json_serializable และ build_runner ไว้ในส่วนของ dev_dependencies เพราะ package นี้เราใช้สำหรับ generate code
เท่านั้น ไม่ได้มีใช้งานโปรแกรมหรือ App ของเราโดยตรง หรือใช้เฉพาะในขั้นตอนการพัฒนา อย่างไรก็ตาม ให้คงแบบตามรูปก่อนก็ได้
เมื่อติดตั้ง build_runner เราก็พร้อมทำการ สร้างไฟล์ โดยใช้คำสั่ง flutter pub run build_runner build ในส่วนของ Terminal ของ
โปรเจ็ค App ของเรา จะได้เป็นดังนี้
รอสักครู่ เราก็ได้จะได้ไฟล์ address.g.dart และส่วนของไฟล์ address.dart ก็ไม่เกิด error แล้ว
เราลองเปิดไฟล์ address.g.dart จะได้เป็นดังนี้
// GENERATED CODE - DO NOT MODIFY BY HAND part of 'address.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** Address _$AddressFromJson(Map<String, dynamic> json) { return Address( json['street'] as String, json['city'] as String, ); } Map<String, dynamic> _$AddressToJson(Address instance) => <String, dynamic>{ 'street': instance.street, 'city': instance.city, };
จะเห็นว่า แทนที่เราจะต้องกำหนดฟังก์ชั่นสำหรับจัดการรูปแบบข้อมูล JSON ของ Address model class เองทั้งหมด เราก็แค่ ทำการ
กำหนดการใช้งาน คำอธิบาย @JsonSerializable() ในไฟล์ class ต้นฉบับตามรูปแบบที่กำหนด แล้วทำการ generate ซึ่งถ้าเป็นโครงสร้าง
ข้อมูลง่ายๆ อย่างข้างต้น ก็อาจจะดูไม่ค่อยเห็นผลอะไร แต่ถ้าเป็น class ข้อมูลที่มีความซับซ้อน มีหลายชั้น การใช้งาน json_serializable
ก็จะทำให้การจัดการข้อมูลสำหรับใช้งาน JSON มีประสิทธิภาพ สะดวก และรวดเร็วขึ้น
เรามาลองทดสอบเรียกใช้งาน Address class ในไฟล์ main() ดูการทำงาน ดังนี้
import 'dart:convert'; import 'address.dart'; void main() { var address = Address('My str.','New York'); var jsonData = jsonEncode(address); print(jsonData.runtimeType); // String print(jsonData); // {"street":"My str.","city":"New York"} Map addressDecode = jsonDecode(jsonData); print(addressDecode.runtimeType); // Map<String, dynamic> print(addressDecode); // {street: My str., city: New York} var address2 = Address.fromJson(addressDecode); print(address2.runtimeType); // Address print(address2.street); // My str. }
เราสามารถใช้งานคำสั่ง jsonDecode() และ jsonEncode() ร่วมกับข้อมูลโดยไม่ต้องแก้ไขอะไร จากหัวข้อการใช้งาน dart:convert
นอกจากนั้น เรายังสามารถสร้าง Object จากข้อมูล Map โดยใช้ factory named constructor ชื่อ Address.fromJson()
เรามาลองอีกตัวอย่าง ให้มีความซับซ้อนมากขึ้น เช่น เราสร้าง User model class ที่มีการใช้งาน Address ภายใน ให้กำหนดไฟล์
user.dart ดังนี้
import 'address.dart'; import 'package:json_annotation/json_annotation.dart'; part 'user.g.dart'; @JsonSerializable() class User { String firstName; Address address; User(this.firstName, this.address); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this); }
ทำเหมือนเดิม รันคำสั่ง flutter pub run build_runner build เพื่อสร้างไฟล์ user.g.dart
จะได้ไฟล์ user.g.dart เริ่มมีความซับซ้อนมากขึ้น ตามโครงสร้างของข้อมูล
// GENERATED CODE - DO NOT MODIFY BY HAND part of 'user.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** User _$UserFromJson(Map<String, dynamic> json) { return User( json['firstName'] as String, json['address'] == null ? null : Address.fromJson(json['address'] as Map<String, dynamic>), ); } Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{ 'firstName': instance.firstName, 'address': instance.address, };
ทดสอบใช้งานในไฟล์ main.dart
import 'dart:convert'; import 'address.dart'; import 'user.dart'; void main() { var address = Address('My str.','New York'); var user = User('Ebiwayo', address); var jsonData = jsonEncode(user); print(jsonData.runtimeType); // String print(jsonData); // {"firstName":"Ebiwayo","address":{"street":"My str.","city":"New York"}} }
คำว่า "part of" เป็นการระบุว่าไฟล์นั้นๆ เป้นส่วนหนึ่งของ package หรือ library หรือ ไฟล์ใดๆ เพื่อให้สามารถเขียนโค้ดแยกเป็นอีก
ไฟล์ได้ ซึ่งปกติ package หรือ library มักจะเป็นไฟล์ๆ หนึ่ง การแยกอีกไฟล์ก็เหมือนอีก package แต่ การแยกอีกไฟล์โดยการกำหนด
การใช้งาน "part of" จะเป็นการแยกไฟล์ที่ไม่ใช้อีก package แต่เป็นส่วนหนึ่งของ package ตามชื่อหรือ path ที่ระบุนั่นเอง
เราลงลึกรายละเอียดเกี่ยวกับการใช้งานข้อมูล JSON String Data ในภาษา Dart ไปมากพอสมควร ถึงแม้จะยังเหลืออีกหนึ่งหัวข้อ
แต่นั้นก็เป็นเพียงตัวเลือก เราจะข้ามไปก่อน หากสนใจ สามารถศึกษา และใช้งานได้ที่ package:built_value
ซึ่งถ้ามีความน่าสนใจยังไง อาจจะมาเพิ่มเติมเนื้อหาให้ภายหลัง
หวังว่าจะเป็นประโยชน์ในการทำความเข้าใจ และประยุกต์ใช้งาน JSON ในภาษา Dart รวมถึงการนำไปประยุกต์ใช้กับ Flutter ต่อไป