json_serializable.dart icon indicating copy to clipboard operation
json_serializable.dart copied to clipboard

Custom Map Types

Open TimWhiting opened this issue 1 year ago • 3 comments

@kevmoo Iterated as requested on #997

Fixes: https://github.com/google/json_serializable.dart/issues/396 See hacky workaround that is forced to do this at runtime for the fast_immutable_collections package, and is not able to handle enum keys: https://github.com/marcglasberg/fast_immutable_collections/pull/25/files#diff-ba44d9486dfccb708bbdbbc167a5cfc096361c004eb01907d83fa4cfb2b8b889R1386

Custom map types with fromJson / toJson methods can now have keys automatically de/serialized to strings.

Usage:

Custom map types can make the fromJsonK / toJsonK parameters of their fromJson / toJson methods detected as map keys by using String? rather than Object? as the serialized type.

// Custom map type with custom serializers 
// (Example just wraps a map type, but imagine an immutable collections package)
class CustomMap<K, V> {
  final Map<K, V> map;

  CustomMap(this.map);

  factory CustomMap.fromJson(
    Map<String, dynamic> json,
    // Json serializable detects this as a custom Key value because of the (String?).
    K Function(String?) fromJsonK,  
    V Function(Object?) fromJsonV,
  ) =>
      CustomMap(json.map<K, V>(
          (key, value) => MapEntry(fromJsonK(key), fromJsonV(value))));

  Map<String?, dynamic> toJson( 
    // Json serializable detects this as a custom Key value because of the (String?).
    String? Function(K) toJsonK, 
    Object? Function(V) toJsonV,
  ) =>
      map.map((key, value) => MapEntry(toJsonK(key), toJsonV(value)));
}

// User of custom map type & JsonSerializable
@JsonSerializable()
class UseOfCustomMap {
  final CustomMap<int, String> map;

  UseOfCustomMap(this.map);

  factory UseOfCustomMap.fromJson(Map<String, dynamic> json) =>
      _$UseOfCustomMapFromJson(json);

  Map<String, dynamic> toJson() => _$UseOfCustomMapToJson(this);
}

TimWhiting avatar Oct 08 '22 16:10 TimWhiting

Hrm...I'm REALLY worried about the complexity added here – for a pretty narrow scenario.

kevmoo avatar Oct 18 '22 23:10 kevmoo

Hrm...I'm REALLY worried about the complexity added here – for a pretty narrow scenario.

The complexity of auto-detection of when this applies, or the complexity of the implementation?

I actually thought that the implementation and auto-detection were relatively straightforward. Plumbing through a flag from the annotation might be less 'magic' to the user, but would require far more changes, and would require more cognitive burden on the end user of the collection type rather than the author.

As far as it being a narrow scenario. The goal of json_serializable in my opinion is to automatically generate serialization methods for data models. If this feature was trying to serialize objects that aren't data models, I would see the value in your argument.

I would argue that using immutable collections in your data model is a fairly common practice, (e.g. built_value being one that is used within google right?) and should be a use case we support just as much as the standard map.

TimWhiting avatar Oct 19 '22 15:10 TimWhiting

@TimWhiting

DateTime, BigInt url for example as key works fine without hacky workaround, because these types are implemented correctly, double int, bool are not parsed correctly I think that works for the hacky woraround. See issue #1332. and issue: https://github.com/marcglasberg/fast_immutable_collections/issues/58.

With DateTime I get a cast error because the DateTime is parsed correctly with toJsonK and then _safeKeyToJson tries to parse the already converted String into DateTime again in a DatateTime. -> error.

Maybe it is possible to use only hacky workaround for int double bool, until #1332 is fixed. :)

JoanSchi avatar Jun 27 '23 17:06 JoanSchi