Json Annotation FS
A safe JSON annotation package with error handling for Flutter/Dart. Extends the Dart json_annotation package with robust parsing that prevents crashes on malformed or incomplete JSON data.
Overview
Json Annotation FS is a Flutter/Dart package that extends the standard json_annotation package with safe JSON parsing capabilities. It prevents application crashes when dealing with incomplete or malformed JSON data by providing intelligent defaults and type coercion.
The Problem
The standard json_annotation package throws exceptions when:
- A field is missing from the JSON
- The value type doesn't match (e.g., a string instead of a number)
nullis received for a non-nullable field- An array contains elements of incorrect types
This leads to crashes in production.
The Solution
The package provides safe parsers that:
-
Return default values instead of throwing exceptions:
String→ empty string''int→0double→0.0bool→falseDateTime→DateTime(2000)List<T>→ empty list[]
-
Support nullable fields:
- Return
nullwhen a field is missing or invalid - Require explicit annotations like
@SafeParseStringNullable(), etc.
- Return
-
Perform type conversion:
- Convert string
"123"→ number123 - Convert any value → string via
toString() - Convert string
"true"→ booleantrue
- Convert string
How It Works
Instead of the standard annotation:
@JsonSerializable() // May crash on bad data
class Person {
final String name;
}
Use:
@JsonSerializableFS() // Safe parsing
class Person {
final String name; // Returns '' if missing
@SafeParseStringNullable()
final String? email; // Returns null if missing
}
Who Is This For
- FarmSpace projects where APIs may return incomplete data
- Applications working with unstable APIs
- Teams that need resilience to JSON parsing errors
Key Features
- ✅ Safe defaults - Never crashes, always returns valid values
- ✅ Type coercion - Automatically converts incompatible types
- ✅ Nullable support - Explicit nullable field handling
- ✅ Collection safety - Handles arrays with mixed or missing types
- ✅ Drop-in replacement - Easy migration from
@JsonSerializable() - ✅ Zero configuration - Works out of the box with sensible defaults
Technical Details
- Extends
JsonSerializablefromjson_annotation - Uses the
JsonConverterpattern for safe parsing - Defaults to renaming fields to
snake_case - Fully compatible with
json_serializableandbuild_runner - Distributed via pub.dev and Azure DevOps Git repository
Bottom line: The package makes JSON parsing resilient to errors, preventing application crashes due to malformed data.
Installation
From pub.dev (Recommended)
Install the package and its dependencies:
dart pub add json_annotation_fs json_annotation
dart pub add --dev build_runner json_serializable
Or add to your pubspec.yaml:
dependencies:
json_annotation: ^4.9.0
json_annotation_fs: ^0.1.0
dev_dependencies:
build_runner: ^2.3.3
json_serializable: ^6.8.0
From Git (Alternative)
If you need to use the Azure DevOps repository instead:
dependencies:
json_annotation: ^4.9.0
json_annotation_fs:
git:
# repository URL. Azure login required
url: https://AfricaFarmersNetwork-DevOps@dev.azure.com/AfricaFarmersNetwork-DevOps/Json_annotation_package-flutter/_git/Json_annotation_package-flutter
# desired repository branch
ref: main
dev_dependencies:
build_runner: ^2.3.3
json_serializable: ^6.8.0
Usage
Basic Usage
Annotate your class with @JsonSerializableFS() instead of @JsonSerializable(). All properties and methods behave the same, but with built-in parsing safety.
The default fieldRename property is set to FieldRename.snake, so your JSON fields will automatically be converted from snake_case to camelCase in Dart.
Available Annotations
For Nullable Primitive Types
All nullable primitive fields must be annotated with the corresponding safe parser:
| Type | Annotation |
|---|---|
String? |
@SafeParseStringNullable() |
int? |
@SafeParseIntNullable() |
double? |
@SafeParseDoubleNullable() |
bool? |
@SafeParseBoolNullable() |
DateTime? |
@SafeParseDateTimeNullable() |
For Nullable Collections
All nullable list fields must be annotated:
| Type | Annotation |
|---|---|
List<String>? |
@SafeParseStringListNullable() |
List<int>? |
@SafeParseIntListNullable() |
List<double>? |
@SafeParseDoubleListNullable() |
List<bool>? |
@SafeParseBoolListNullable() |
For Custom Object Lists
All non-nullable lists of custom objects must have a default value:
@JsonKey(defaultValue: [])
final List<Person> family;
Nullable lists of custom objects work without annotation:
final List<Person>? friends; // No annotation needed
Migration from json_annotation
To migrate from @JsonSerializable() to @JsonSerializableFS():
- Replace
@JsonSerializable()with@JsonSerializableFS() - Add nullable annotations to nullable fields (see table above)
- Add
@JsonKey(defaultValue: [])to non-nullable custom object lists - Run
dart run build_runner build --delete-conflicting-outputs
That's it! Your code will now handle malformed JSON gracefully.
Example
Complete Example
Here's a complete example showing all features of the package:
import 'package:json_annotation_fs/json_annotation_fs.dart';
part 'person_model.g.dart';
@JsonSerializableFS()
class Person {
Person({
required this.firstName,
this.lastName,
required this.age,
this.email,
required this.childrenNames,
this.petNames,
required this.family,
this.friends,
});
// Non-nullable fields: return defaults if missing
final String firstName; // Returns '' if missing
final int age; // Returns 0 if missing
final List<String> childrenNames; // Returns [] if missing
// Nullable fields: must be annotated, return null if missing
@SafeParseStringNullable()
final String? lastName; // Returns null if missing
@SafeParseStringNullable()
final String? email; // Returns null if missing
@SafeParseStringListNullable()
final List<String>? petNames; // Returns null if missing
// Custom object lists: non-nullable requires default
@JsonKey(defaultValue: [])
final List<Person> family; // Returns [] if missing
// Nullable custom object lists: no annotation needed
final List<Person>? friends; // Returns null if missing
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
Map<String, dynamic> toJson() => _$PersonToJson(this);
}
Handling Malformed JSON
The package gracefully handles various JSON issues:
// JSON with missing fields and wrong types
final json = {
'first_name': 'John',
// 'age' is missing
'email': 12345, // Wrong type (number instead of string)
'children_names': ['Alice', 'Bob'],
// 'pet_names' is missing
};
final person = Person.fromJson(json);
print(person.firstName); // 'John' (from JSON)
print(person.age); // 0 (default, field was missing)
print(person.email); // '12345' (converted from number to string)
print(person.childrenNames); // ['Alice', 'Bob'] (from JSON)
print(person.petNames); // null (missing, nullable field)
Code Generation
After writing your model, generate the serialization code:
dart run build_runner build --delete-conflicting-outputs
For continuous development, use watch mode:
dart run build_runner watch --delete-conflicting-outputs
Additional Resources
License
This package is licensed under the MIT License. See the LICENSE file for details.
Related Packages
- json_annotation - Base annotation package
- json_serializable - Code generator for JSON serialization
- build_runner - Build system for Dart code generation