Json Annotation FS

pub.dev License: MIT

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)
  • null is 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:

  1. Return default values instead of throwing exceptions:

    • String → empty string ''
    • int → 0
    • double → 0.0
    • bool → false
    • DateTime → DateTime(2000)
    • List<T> → empty list []
  2. Support nullable fields:

    • Return null when a field is missing or invalid
    • Require explicit annotations like @SafeParseStringNullable(), etc.
  3. Perform type conversion:

    • Convert string "123" → number 123
    • Convert any value → string via toString()
    • Convert string "true" → boolean true

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 JsonSerializable from json_annotation
  • Uses the JsonConverter pattern for safe parsing
  • Defaults to renaming fields to snake_case
  • Fully compatible with json_serializable and build_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

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():

  1. Replace @JsonSerializable() with @JsonSerializableFS()
  2. Add nullable annotations to nullable fields (see table above)
  3. Add @JsonKey(defaultValue: []) to non-nullable custom object lists
  4. 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.


Libraries

json_annotation_fs