flutterdartjson-serializable

How to Pass a Custom Parameter Name in a Generic Data Model with JsonSerializable in Flutter?


I have a generic data model in Flutter using the JsonSerializable. I want to pass a custom parameter name (e.g., "statistics") instead of a fixed name in the fromJson and toJson methods. Here's my current implementation:

import 'package:json_annotation/json_annotation.dart';

part 'data_model.g.dart';

@JsonSerializable(genericArgumentFactories: true)
class DataModel<T, P> {
  final int? total;
  final List<T> items;
  final P? param;

  DataModel({
    this.total,
    required this.items,
    this.param,
  });

  factory DataModel.fromJson(
    Map<String, dynamic> json,
    T Function(Object?) fromJsonT,
    P Function(Object?) fromJsonP,
  ) {
    final List<dynamic> itemsJson = json['items'] as List<dynamic>;
    final List<T> items = itemsJson
        .map<T>((item) => fromJsonT(item as Map<String, dynamic>))
        .toList();

    final dynamic paramJson = json['statistics'] as dynamic; // <- pass the param name instead of 'statistics'
    final P? pamPam = fromJsonP(paramJson);

    return DataModel<T, P>(
      total: (json['total'] != null && json['total'] is int)
          ? json['total'] as int
          : null,
      param: pamPam,
      items: items,
    );
  }

  Map<String, dynamic> toJson(Object? Function(T) toJsonT, Object? Function(P) toJsonP) {
    final List<dynamic> itemsJson =
        items.map<dynamic>((item) => toJsonT(item)).toList();

    return {
      'total': total,
      'statistics': toJsonP, // <- pass the param name instead of 'statistics'
      'items': itemsJson,
    };
  }
}

Here is JSON example how it works now:

{
   "total":2,
   "items":[
      {
         "id":1,
         "name":"John"
      },
      {
         "id":2,
         "name":"Max"
      }
   ],
   "statistics":{
      "views":50,
      "downloads":100
   }
}

My objective is to make field statistics more generic. The aim is to establish a logic wherein an alternative object can be passed as a generic parameter alongside the respective JSON key name. For instance, "other" could be passed instead of "statistics." Example:

{
   "total":2,
   "items":[
      {
         "id":1,
         "name":"John"
      },
      {
         "id":2,
         "name":"Max"
      }
   ],
   "other":{
      "invites":50
   }
}

Maybe we can create one more parameter like 'N' name and parse it in fromJson and toJson methods. Or maybe it is not possible at all.


Solution

  • You can turn the field name in a parameter/property, and keep 'statistics' as a default.

    import 'package:json_annotation/json_annotation.dart';
    
    part 'data_model.g.dart';
    
    @JsonSerializable(genericArgumentFactories: true)
    class DataModel<T, P> {
      final int? total;
      final List<T> items;
      final P? param;
      final String paramName;
    
      DataModel({
        this.total,
        required this.items,
        this.param,
        required this.paramName,
      });
    
      factory DataModel.fromJson(
        Map<String, dynamic> json,
        T Function(Object?) fromJsonT,
        P Function(Object?) fromJsonP,
        {String paramName = 'statistics'}
      ) {
        final List<dynamic> itemsJson = json['items'] as List<dynamic>;
        final List<T> items = itemsJson
            .map<T>((item) => fromJsonT(item as Map<String, dynamic>))
            .toList();
    
        final dynamic paramJson = json[paramName] as dynamic;
        final P? pamPam = fromJsonP(paramJson);
    
        return DataModel<T, P>(
          total: (json['total'] != null && json['total'] is int)
              ? json['total'] as int
              : null,
          param: pamPam,
          items: items,
          paramName: paramName,
        );
      }
    
      Map<String, dynamic> toJson(Object? Function(T) toJsonT, Object? Function(P) toJsonP) {
        final List<dynamic> itemsJson =
            items.map<dynamic>((item) => toJsonT(item)).toList();
    
        return {
          'total': total,
          paramName: toJsonP,
          'items': itemsJson,
        };
      }
    }
    

    (By the way, JsonSerializable is not really used here, you are just parsing and formatting it without using its generated code. Are you sure you need it?)