跪拜 Guibai
← All articles
Flutter

Stop Hand-Writing fromJson: @JsonKey Handles Every Mapping and Null Case

By SoaringHeart ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Flutter teams can drop repetitive hand-written serialization glue by annotating model fields, and the unknownEnumValue fallback keeps older client builds alive when backends add enum members.

Summary

Flutter's json_serializable generates fromJson and toJson methods from @JsonSerializable classes, but by default it expects Dart field names to match JSON keys exactly. The moment backends use snake_case, return strings where numbers belong, or omit keys entirely, developers fall back to hand-writing serialization code. That code is long, error-prone, and breaks on null and new enum values.

The @JsonKey annotation fills that gap declaratively. name renames the key, readValue accepts a custom map reader that can check several key variants, and fromJson/toJson attach conversion functions for types like DateTime. Required and disallowNullValue distinguish a missing key from a null value; defaultValue only fills in when the key is absent. unknownEnumValue catches server-side enum additions so old builds don't crash. ignore, includeFromJson, and includeToJson control whether a field participates in either direction, and includeIfNull removes nulls from the serialized output.

Grouping these attributes into mapping, JSON→Dart, Dart→JSON, and ignore categories makes the configuration predictable. A comprehensive example puts all the pieces together, showing a model that accepts legacy user_id keys, parses timestamps, sends updatedAt as UTC, omits null avatars, falls back to an unknown status, and keeps a server token read-only.

Takeaways
— @JsonKey(name: 'user_name') maps a Dart field to a different JSON key.
— readValue picks values from multiple possible keys, falling back in order; the second argument to readValue is the name value, not the field name.
— fromJson and toJson custom conversion functions handle type transforms like timestamp parsing and ISO string output.
— defaultValue only kicks in when the key is missing; an invalid type still throws, so use fromJson for that.
— required: true throws when the key is absent but allows null; disallowNullValue: true allows missing key but throws on null.
— unknownEnumValue falls back to a designated enum member when the backend sends an unknown value, preventing parse crashes.
— ignore is bidirectional (skips both fromJson and toJson); includeFromJson: false and includeToJson: false are one-directional.
— includeIfNull: false omits null fields from the generated JSON output; it doesn't affect deserialization.
Conclusions

The readValue callback receives the name value, not the Dart field name, a slot that catches people expecting the field name.

Separating required from disallowNullValue clarifies that a missing key and a null value are distinct failure modes; mixing them leads to unexpected exceptions.

unknownEnumValue acts as backward-compatibility insurance for long-lived APIs; without it, adding an enum value on the backend breaks every old client.

Categorizing @JsonKey options into mapping, JSON→Dart, Dart→JSON, and ignore flags gives a mental model that reduces mistakes when combining attributes.

Concepts & terms
@JsonKey
An annotation for Dart model fields that tells json_serializable how to map the field to and from JSON, covering name, conversion, null handling, and serialization inclusion.
readValue
A callback that overrides the JSON key lookup, allowing multiple keys to be tried in order, useful for legacy field names.
unknownEnumValue
An enum fallback assigned when the JSON contains a value not in the enum, preventing parsing failures in older clients.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗