JSON Error Clarity πŸ”

pub package License: MIT

Stop wrestling with cryptic JSON parsing errors. json_error_clarity is a small, pure-Dart package that turns unhelpful cast failures into messages that tell you exactly which field failed, what type it expected, what it actually got, and how to fix it.

type 'String' is not a subtype of type 'int' in type cast

…becomes a message that names the field, shows the value, and hands you copy-paste fixes.

What it is (and isn't)

It's a set of extension methods on Map<String, dynamic> β€” getSafeInt, getSafeString, getSafeList, requireKeys, and friends β€” that you call from your own fromJson. Nothing else runs; there is no build step.

  • βœ… Pure Dart. No Flutter dependency. Works on server, CLI, Flutter, and web.
  • βœ… Four dependencies: json_annotation, meta, args, path.
  • βœ… Zero overhead on success β€” the detailed diagnosis is only built when a parse actually fails.
  • 🚫 No code generation. The old @SafeJsonParsing() generator was removed in 0.2.0 (see Migrating from 0.1.x).

Install

dependencies:
  json_error_clarity: ^0.2.0
dart pub add json_error_clarity

Quick start

Call the safe accessors inside your own fromJson:

import 'package:json_error_clarity/json_error_clarity.dart';

class User {
  final int id;
  final String name;
  final String? email;
  final List<String> tags;

  User({required this.id, required this.name, this.email, required this.tags});

  factory User.fromJson(Map<String, dynamic> json) => User(
        id: json.getSafeInt('id'),
        name: json.getSafeString('name'),
        email: json.getNullableSafeString('email'), // null-safe: missing/null is fine
        tags: json.getSafeList('tags', (v) => v as String),
      );
}

When the data is well-formed, these return the value and get out of your way. When it isn't, you get a precise error instead of a stack trace.

Before vs after

final json = {'id': '42', 'name': 'Ada'}; // id arrived as a String

// Plain Dart:
json['id'] as int;
// β†’ type 'String' is not a subtype of type 'int' in type cast

// With json_error_clarity:
json.getSafeInt('id');
// β†’ throws FormatException whose message reads:
//
//   🚨 OOPS! There's a problem with your JSON data:
//
//   πŸ” EXACT PROBLEM DIAGNOSIS:
//      πŸ“ Field name: 'id'
//      βœ… Property EXISTS in JSON: YES
//      ❌ Data type MATCHES model: NO
//
//   πŸ“Š TYPE COMPARISON:
//      🎯 Your model expects: a whole number (like 42)
//      πŸ˜• JSON response contains: text (like 'hello')
//      πŸ“„ The actual value: 42
//
//   πŸ”§ How to fix this (3 easy options): …copy-paste solutions…

The message distinguishes the three ways parsing actually fails:

Situation What the message says
Key present, wrong type field name, expected vs actual type, value, fixes
Key missing that the property is not in the JSON, plus "did you mean" suggestions
Key present but null that the value is null β€” not a type mismatch
Bad element in a list the failing index and that element's value

API at a glance

// Required β€” throw a clear FormatException on failure:
json.getSafe<T>('key', (v) => ...);   // custom parser
json.getSafeInt('key');
json.getSafeDouble('key');
json.getSafeString('key');
json.getSafeBool('key');               // accepts true/false, 1/0, 'true'/'yes'/…
json.getSafeDateTime('key');           // ISO 8601, or unix seconds/millis
json.getSafeList<T>('key', (v) => ...);
json.getSafeObject<T>('key', T.fromJson);
json.getSafeObjectList<T>('key', T.fromJson);

// Nullable β€” return null when the key is missing or the value is null:
json.getNullableSafeInt('key');        // …and String/Double/Bool/DateTime/List/Object

// Up-front validation:
json.requireKeys(['id', 'name', 'email']);

Handling the error

Every failure throws a FormatException whose message is the detailed diagnosis. Catch it once, at your data-source or repository layer:

try {
  final user = User.fromJson(payload);
} on FormatException catch (error) {
  logger.error(error.message);   // the full, human-readable diagnosis
  throw DataParseFailure(error.message);
}

There's a runnable demo:

dart run example/main.dart

Migrating from 0.1.x

0.2.0 removes the @SafeJsonParsing() code generator and the Flutter SDK dependency. If you used the generator, replace generated fromJsonSafe() calls with explicit accessors in your own fromJson:

// 0.1.x β€” generated
final user = UserSafeJsonParsing.fromJsonSafe(json);

// 0.2.0 β€” explicit, no build step
final user = User(
  id: json.getSafeInt('id'),
  name: json.getSafeString('name'),
  email: json.getNullableSafeString('email'),
);

Then remove the json_error_clarity builder from your build.yaml and the part '...safe_json_parsing.g.dart'; directives.

The SafeJsonParsing, SafeJsonField, and SafeJsonConfig annotations are deprecated and will be removed in 0.3.0. Nothing reads them; they remain only so existing annotated source keeps compiling.

License

MIT β€” see LICENSE.

Built to complement json_annotation and json_serializable.

Libraries

json_error_clarity