fromJson, toJson, copyWith और भरोसेमंद वैल्यू समानता की मदद से JSON को अपरिवर्तनीय Dart क्लास में बदलें।
इस पाठ को Kodokon में खोलेंइस स्तर पर, असली विषय अब क्लास का सिंटैक्स नहीं, बल्कि बाहरी दुनिया और आपके कोड के बीच की सीमा है। जब तक कोई API प्रतिक्रिया Map<String, dynamic> के रूप में इधर-उधर घूमती रहती है, तब तक हर फ़ील्ड का पठन एक रनटाइम जुआ है। पेशेवर नियम: JSON को टाइप किए गए ऑब्जेक्ट में उसी क्षण बदल दें जब आप उसे प्राप्त करते हैं, फिर केवल उन्हीं ऑब्जेक्ट को इधर-उधर पास करें। इसे अपरिवर्तनीयता के साथ जोड़ें - final फ़ील्ड, const कंस्ट्रक्टर - और आप बग के एक पूरे परिवार को समाप्त कर देते हैं: अब कोई भी आपकी जानकारी के बिना किसी साझा ऑब्जेक्ट को नहीं बदल सकता, और दो एक जैसे const इंस्टेंस को कंपाइलर द्वारा कैननिकलाइज़ कर दिया जाता है।
class User {
const User({
required this.id,
required this.name,
this.email,
});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: (json['id'] as num).toInt(),
name: json['name'] as String,
email: json['email'] as String?,
);
}
final int id;
final String name;
final String? email;
}तीन विकल्पों को स्पष्ट रूप से समझाना ज़रूरी है। पहला, (json['id'] as num).toInt(): कई API किसी पूर्णांक को बैकएंड के आधार पर कभी 3 के रूप में तो कभी 3.0 के रूप में सीरियलाइज़ करते हैं; सीधे int में कास्ट करने पर double पर यह क्रैश हो जाता है। इसके बाद, यदि फ़ील्ड गायब हो तो as String तुरंत एक स्पष्ट TypeError के साथ विफल हो जाता है: यह जानबूझकर किया गया है। पार्स के समय जल्दी विफल हो जाना उस null से बेहतर है जो फैलता है और तीन स्क्रीन बाद फट पड़ता है। अंत में, as String? टाइप में यह दर्ज कर देता है कि फ़ील्ड वास्तव में वैकल्पिक है।
class User {
const User({required this.id, this.email});
final int id;
final String? email;
Map<String, dynamic> toJson() => {
'id': id,
'email': email,
};
User copyWith({int? id, String? email}) {
return User(
id: id ?? this.id,
email: email ?? this.email,
);
}
}अपरिवर्तनीयता के लिए वैल्यू समानता ज़रूरी होती है। डिफ़ॉल्ट रूप से, Dart रेफ़रेंस की तुलना करता है: फ़ील्ड-दर-फ़ील्ड एक जैसे दो User को "अलग" माना जाता है। यह एक ठोस समस्या है जैसे ही आप स्टेट की तुलना करते हैं - टेस्ट में, Provider के select में, किसी सूची में खोज में। == और hashCode को हमेशा एक साथ फिर से परिभाषित करें।
class Point {
const Point(this.x, this.y);
final int x;
final int y;
@override
bool operator ==(Object other) =>
other is Point && other.x == x && other.y == y;
@override
int get hashCode => Object.hash(x, y);
}json['id'] as int के बजाय (json['id'] as num).toInt() क्यों लिखें?as int, as num से धीमा है।3.0 के रूप में सीरियलाइज़ कर सकता है: सीधे int में कास्ट करना double पर विफल हो जाएगा।json['id'] हमेशा एक स्ट्रिंग होती है।copyWith कार्यान्वयन के साथ user.copyWith(email: null) क्या उत्पन्न करता है?email फ़ील्ड को अनुरोध के अनुसार null पर रीसेट कर दिया जाता है।email बनाए रखता है: null ?? this.email मौजूदा वैल्यू पर वापस चला जाता है।== और hashCode को फिर से क्यों परिभाषित करें?const इंस्टेंस के निर्माण को तेज़ करने के लिए।final घोषित की जाती है, यह अनिवार्य हो जाता है।