json_type_guard 1.0.4
json_type_guard: ^1.0.4 copied to clipboard
A tiny, zero-codegen, runtime-safe JSON parsing helper for Dart & Flutter with precise field-level error messages.
π‘οΈ json_type_guard #
Protector of Models. Warden of Types. Keeper of the Seven JSON Kingdoms. #
A tiny, zero-codegen, runtime-safe JSON parsing helper for Dart & Flutter.
Stop guessing which field broke your .fromJson.
Stop seeing βNull is not a subtype ofβ¦β with no clue where it came from.
json_type_guard gives you precise, field-level errors, safe parsing, optional fields, defaults, and full type validation β all without build runners, annotations, or boilerplate.
π Features #
- π‘οΈ Runtime type-safe JSON access
- π Clear, explicit error messages
- πΊοΈ Path tracking in nested structures (e.g.,
"user.profile.email") - β No code generation
- π§© No annotations
- π― Zero boilerplate
- π Detects wrong data types per field
- π Fallback/default value support
- β Optional fields & nullable types
- π Supports all Dart types
- π§± Consistent & predictable API
π¦ Installation #
dependencies:
json_type_guard: ^1.0.0
π― Quick Start #
import 'package:json_type_guard/json_type_guard.dart';
class User {
final String name;
final int age;
final String? email;
final String role;
User.fromJson(Map json)
: name = json.guard<String>("name"),
age = json.guard<int>("age"),
email = json.guardOrNull<String>("email"),
role = json.guard<String>("role", defaultValue: "user");
}
void main() {
final json = {
'name': 'Alice',
'age': 30,
'email': 'alice@example.com',
};
final user = User.fromJson(json);
print(user.name); // Alice
}
π Usage #
Basic Type Extraction #
final json = {'name': 'Bob', 'age': 25};
// Required fields
final name = json.guard<String>('name');
final age = json.guard<int>('age');
Optional Fields #
// Returns null if missing or null
final email = json.guardOrNull<String>('email');
Default Values #
// Uses default if field is missing
final role = json.guard<String>('role', defaultValue: 'user');
Nested Objects #
class Address {
final String city;
Address.fromJson(Map json) : city = json.guard<String>('city');
}
class Person {
final String name;
final Address address;
Person.fromJson(Map json)
: name = json.guard<String>('name'),
address = json.guardObject<Address>('address', Address.fromJson);
}
Lists #
// List of primitives
final tags = json.guardList<String>('tags', (v) => v as String);
// List of objects
final users = json.guardList<User>('users', (v) => User.fromJson(v as Map));
DateTime Fields #
class Event {
final String name;
final DateTime createdAt;
final DateTime? updatedAt;
Event.fromJson(Map json)
: name = json.guard<String>('name'),
createdAt = json.guardDateTime('created_at'),
updatedAt = json.guardDateTimeOrNull('updated_at');
}
// Works with ISO8601 strings
final json1 = {
'name': 'Meeting',
'created_at': '2024-12-07T10:30:00Z',
};
// Works with Unix timestamps (seconds)
final json2 = {
'name': 'Conference',
'created_at': 1701950400,
};
// Works with Unix timestamps (milliseconds)
final json3 = {
'name': 'Workshop',
'created_at': 1701950400000,
};
π¨ Error Handling #
When parsing fails, you get precise, readable errors:
final json = {'name': 'Charlie', 'age': '30'}; // age is String, not int
try {
final user = User.fromJson(json);
} catch (e) {
print(e);
// Output:
// JsonGuardError:
// Field: "age"
// Expected: int
// Received: String (30)
}
Path Tracking in Nested Structures #
For deeply nested JSON, errors show the full path to the problematic field:
class Address {
final String city;
Address.fromJson(Map json) : city = json.guard<String>('city');
}
class Profile {
final String email;
final Address address;
Profile.fromJson(Map json)
: email = json.guard<String>('email'),
address = json.guardObject<Address>('address', Address.fromJson);
}
class User {
final String name;
final Profile profile;
User.fromJson(Map json)
: name = json.guard<String>('name'),
profile = json.guardObject<Profile>('profile', Profile.fromJson);
}
// JSON with nested error
final json = {
'name': 'Alice',
'profile': {
'email': 'alice@example.com',
'address': {
'city': 12345 // Wrong type!
}
}
};
try {
final user = User.fromJson(json);
} catch (e) {
print(e);
// Output shows FULL PATH:
// JsonGuardError:
// Field: "profile.address.city"
// Expected: String
// Received: int (12345)
}
This makes debugging nested structures significantly easier - you know exactly which field failed!
Custom Error Themes #
By default, errors use plain text (no colors) for maximum compatibility:
// Default output (plain text)
// JsonGuardError:
// Field: "age"
// Expected: int
// Received: String (30)
Enable Colors for Terminal Output:
GuardTheme.enableColors(); // Enable ANSI colors for terminal
Custom Labels:
GuardTheme.setTheme(
errorPrefix: 'π¨ CUSTOM ERROR:',
fieldLabel: 'π Field',
expectedLabel: 'π― Expected',
receivedLabel: 'π¦ Received',
);
Disable Colors:
GuardTheme.disableColors(); // Back to plain text
π§ API Reference #
Guard Methods #
| Method | Description |
|---|---|
guard<T>(key, {defaultValue}) |
Extract required field with optional default |
guardOrNull<T>(key) |
Extract optional/nullable field |
guardObject<T>(key, builder) |
Parse nested object |
guardList<T>(key, convert) |
Parse list with converter |
Static Methods #
// Use Guard class directly
Guard.value<String>(json, 'name');
Guard.valueOrNull<int>(json, 'age');
Guard.object<Address>(json, 'address', Address.fromJson);
Guard.list<String>(json, 'tags', (v) => v as String);
Debug Mode #
Guard.setDebugLogging(true); // Enable debug logs
Guard.setDebugLogging(false); // Disable debug logs
π Comparison #
| Feature | json_type_guard | json_serializable | Manual parsing |
|---|---|---|---|
| Code generation | β | β | β |
| Build runner | β | β | β |
| Annotations | β | β | β |
| Clear errors | β | β | β |
| Runtime safety | β | β | β |
| Setup time | Instant | Minutes | Instant |
| Boilerplate | Minimal | Medium | High |
β‘ VS Code Snippets #
Boost your productivity with built-in code snippets! The .vscode folder contains snippets for quick model generation.
Installation #
Option 1: Copy to User Snippets (Recommended)
- Open VS Code
- Press
Cmd+Shift+P(Mac) orCtrl+Shift+P(Windows/Linux) - Type "Preferences: Configure User Snippets"
- Select "dart.json"
- Copy the contents from .vscode/json_type_guard.code-snippets
Option 2: Use in Your Project
- Copy the
.vscodefolder to your project root - Snippets will work automatically in that workspace
Available Snippets #
| Prefix | Description |
|---|---|
jtg-import |
Import json_type_guard package |
jtg-model |
Create a basic model with fromJson |
jtg-model-full |
Complete model with multiple field types |
jtg-parse |
Parse a required field |
jtg-parse-opt |
Parse an optional field |
jtg-parse-default |
Parse with default value |
jtg-parse-object |
Parse nested object |
jtg-parse-list |
Parse list of primitives |
jtg-parse-list-obj |
Parse list of objects |
Quick Example #
Type jtg-model and press Tab:
class User {
final String name;
User.fromJson(Map json)
: name = json.guard<String>('name');
@override
String toString() => 'User(name: $name)';
}
See .vscode/README.md for more details.
π€ Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
π License #
MIT License - see the LICENSE file for details.
π Show Your Support #
If you find this package helpful, please give it a β on GitHub!