google / json_serializable.dart

Generates utilities to aid in serializing to/from JSON.
https://pub.dev/packages/json_serializable
BSD 3-Clause "New" or "Revised" License
1.54k stars 392 forks source link

Suggestion: Better support for `createPerFieldToJson` #1392

Open akvgergo opened 5 months ago

akvgergo commented 5 months ago

As far as I can tell, createPerFieldToJson is currently mostly used by other code generators, and is rather impractical to use in other cases. As the name suggests, I would like to be able to only serialize some parts of a model, but the abstract class generated does not expose any methods that would make it easy to do this. If I were to use them in the way that the name suggests, my options are:

  1. Copy-paste the generated code with the key strings, and manually create the methods I need to only serialize some fields of a model.

  2. Mark most fields as nullable and not required, and simply copyWith(), with the unnecessary values nulled.

  3. Create models that interface with the main ones, that have special json handling.

  4. Create another library that does the heavy lifting, generating methods for me that only serialize the given values.

The first option is not great for obvious reasons. The second and third options are better, but it would lead to a lot of unnecessary null handling and boilerplate. The fourth option is ideal, but I honestly lack the expertise to achieve it.

For clarity, here's an example of what I'm trying to achieve. This is a model that we're using:

/// Stores data related to a single storage entry, and the associated product.
@Freezed()
class Stock with _$Stock {
  const Stock._();

  @JsonSerializable(createPerFieldToJson: true)
  const factory Stock({
    required final int id,
    required final int storeId,
    required final int productId,
    required final int amount,
    required final int netPrice,
    @JsonKey(toJson: Util.boolToInt)
    required final bool onDiscount,.
    required final int? discountPercent,
    required final int? discountPrice,
    required final int minimumLevel,
    required final String created,
    required final String modified,
    required final int notificationLevel,
    required final int optimalLevel,
    required final int fullLevel,
    required final String? changelog,
    required final int? purchasePrice,
    @JsonKey(toJson: Util.boolToInt)
    required final bool selectedForPrinting,
    @JsonKey(toJson: Util.boolToInt)
    required final bool active,

    required final String slug,
    required final String? productBarcode,
    required final String? productName,
    required final Product? product,
    required final bool selectedForPurchase,
  }) = _Stock;

  factory Stock.fromJson(dynamic json) => _$StockFromJson(json);

  factory Stock.fromJsonString(String jsonString) => _$StockFromJson(json.decode(jsonString));

  String toJsonString() => json.encode(toJson());

  Future<Product?> loadProduct() async {
    return (await Products.read(productId)).result;
  }
}

I would like to be able to make a method like this:

  static dynamic createPartialJson({
    int? amount,
    int? netPrice,
    bool? onDiscount,
    int? discountPercent,
    int? discountPrice,
  }) {
    ...
  }

So I can do this:

    createPartialJson(
      amount: 20,
      netPrice: 199
    );
    // result: { "amount": 20, net_price: 199}

Use case:

With how our API operates, always sending the entire model is undesirable. Trying to update the changelog, lastModified, or created fields for example will lead to errors. Also, during normal operation, there will be quite a few cases where multiple people will send requests that update the same models. Only sending the minimum of what was changed will help a lot avoiding possible data loss from multiple clients overwriting changes.

Is it possible/within reason to implement this functionality?

small note: Feel free to point me to a dependent library that achieves this, if there are any. 😅