json_extension_type 1.2.0
json_extension_type: ^1.2.0 copied to clipboard
JSON Extension Type Generator - Automatically generates extension types for JSON to Dart typedef records and classes conversion.
JSON Extension Type Generator - Examples #
Quick Start Example #
1. Create a Model File #
// lib/models/user_model.dart
enum UserRole {
admin,
user,
guest,
}
typedef User = ({
String id,
String name,
String email,
int age,
UserRole role,
List<String> permissions,
});
2. Generate Extensions #
jet generate lib/models
3. Generated Extension #
The tool adds this to your user_model.dart:
// ==========================================
// Generated by JSON Extension Type (JET)
// Do not modify manually
// Run "jet generate" to update
// ==========================================
extension type UserJson(dynamic data) {
User toModel() {
return (
id: data['id'] as String? ?? '',
name: data['name'] as String? ?? '',
email: data['email'] as String? ?? '',
age: data['age'] as int? ?? 0,
role: UserRole.values.byName(data['role'] as String? ?? 'admin'),
permissions: (data['permissions'] as List?)?.cast<String>() ?? [],
);
}
List<User> toList() {
final data = this.data as List;
return data.map((e) => UserJson(e).toModel()).toList();
}
}
4. Use It #
void main() {
final json = {
'id': '123',
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
'role': 'admin',
'permissions': ['read', 'write', 'delete'],
};
final user = UserJson(json).toModel();
print('Name: ${user.name}');
print('Role: ${user.role}');
print('Can write: ${user.permissions.contains('write')}');
}
Advanced Examples #
Example 1: Nested Types #
// product_model.dart
typedef Product = ({
String id,
String name,
double price,
Category category,
List<Review> reviews,
});
typedef Category = ({
String id,
String name,
});
typedef Review = ({
String author,
int rating,
String comment,
});
Usage:
final json = {
'id': 'p1',
'name': 'Laptop',
'price': 999.99,
'category': {
'id': 'c1',
'name': 'Electronics',
},
'reviews': [
{'author': 'Alice', 'rating': 5, 'comment': 'Great!'},
{'author': 'Bob', 'rating': 4, 'comment': 'Good'},
],
};
final product = ProductJson(json).toModel();
print('${product.name}: \$${product.price}');
print('Category: ${product.category.name}');
print('Reviews: ${product.reviews.length}');
Example 2: Custom Annotations #
// order_model.dart
typedef Order = ({
//#key:order_id
String id,
//#key:customer_name
String customerName,
//#key:total_amount #default:0.0
double total,
//#key:order_status
OrderStatus status,
});
enum OrderStatus {
pending,
processing,
shipped,
delivered,
}
This maps JSON keys differently:
final json = {
'order_id': 'ORD-123', // → id
'customer_name': 'Jane Smith', // → customerName
'total_amount': 299.99, // → total
'order_status': 'processing', // → status
};
final order = OrderJson(json).toModel();
Example 3: Nullable Fields #
typedef Profile = ({
String username,
String? bio, // Optional
String? avatarUrl, // Optional
Address? mailingAddress, // Optional custom type
List<String> badges, // Required list
});
typedef Address = ({
String street,
String city,
String? postalCode, // Optional
});
Usage:
final json1 = {
'username': 'johndoe',
'badges': ['verified', 'premium'],
};
final profile1 = ProfileJson(json1).toModel();
print(profile1.bio); // null
print(profile1.mailingAddress); // null
final json2 = {
'username': 'janedoe',
'bio': 'Software developer',
'avatarUrl': 'https://example.com/avatar.jpg',
'mailingAddress': {
'street': '123 Main St',
'city': 'New York',
},
'badges': [],
};
final profile2 = ProfileJson(json2).toModel();
print(profile2.bio); // 'Software developer'
print(profile2.mailingAddress?.city); // 'New York'
Example 4: API Integration #
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:yourapp/models/user_model.dart';
Future<List<User>> fetchUsers() async {
final response = await http.get(
Uri.parse('https://api.example.com/users'),
);
if (response.statusCode == 200) {
final jsonData = jsonDecode(response.body);
return UserJson(jsonData).toList();
} else {
throw Exception('Failed to load users');
}
}
Future<User> fetchUser(String id) async {
final response = await http.get(
Uri.parse('https://api.example.com/users/$id'),
);
if (response.statusCode == 200) {
final jsonData = jsonDecode(response.body);
return UserJson(jsonData).toModel();
} else {
throw Exception('Failed to load user');
}
}
Example 5: Local JSON Files #
import 'dart:convert';
import 'dart:io';
import 'package:yourapp/models/config_model.dart';
Future<Config> loadConfig() async {
final file = File('config.json');
final contents = await file.readAsString();
final jsonData = jsonDecode(contents);
return ConfigJson(jsonData).toModel();
}
Workflow #
Development Workflow #
-
Create Model
# Create model file touch lib/models/product_model.dart -
Define Types
typedef Product = ({ String id, String name, double price, }); -
Generate
jet generate lib/models -
Use
final product = ProductJson(json).toModel(); -
Update
- Modify typedef
- Run
jet generateagain - Extensions are automatically updated
Best Practices #
-
Organize Models
- Keep all models in
lib/models/ - One model per file
- Use descriptive names ending with
_model.dart
- Keep all models in
-
Use Annotations Wisely
- Only use
#keywhen JSON keys don't match - Use
#defaultfor non-standard defaults - Document custom annotations
- Only use
-
Regenerate Regularly
- After each model change
- Before committing code
- Part of build process
-
Type Safety
- Always use proper types
- Prefer non-nullable when possible
- Use enums for fixed sets of values
-
Testing
- Test with real JSON data
- Verify default values
- Check nullable behavior
Tips & Tricks #
Tip 1: Quick Regeneration #
Add to pubspec.yaml:
scripts:
generate: jet generate lib/models
Then run:
dart run generate
Tip 2: Multiple Directories #
Generate for multiple directories:
jet generate lib/models
jet generate lib/data/types
jet generate lib/api/responses
Tip 3: CI/CD Integration #
# .github/workflows/ci.yml
- name: Generate extensions
run: jet generate lib/models
- name: Verify no changes
run: |
if ! git diff --quiet; then
echo "Extensions are out of sync!"
exit 1
fi
Tip 4: Editor Integration #
Add VS Code task (.vscode/tasks.json):
{
"version": "2.0.0",
"tasks": [
{
"label": "Generate Extensions",
"type": "shell",
"command": "jet generate lib/models",
"group": "build"
}
]
}
Run with: Cmd+Shift+B → Select "Generate Extensions"