flutter
flutter

Reputation: 6766

How to set a default value for an object in json serialisable?

I want to set a default value for AvailableService. It straight forward enough with primitives. How would I do it with a custom Object

class Submenu extends Equatable {
     @JsonKey(defaultValue: "")
        final String name;
      @JsonKey(defaultValue: new AvailableService(false,false,false,false))
        final AvailableService availableService;
    
    }

the custom Object:

AvailableService {
bool supportsDelivery;
  bool supportsTableService;
  bool supportsCollection;
  bool supportsChat;
 
}

And the compile time error is

Arguments of a constant creation must be constant expressions.
Try making the argument a valid constant, or use 'new' to call the constructor

Upvotes: 7

Views: 10781

Answers (3)

dphans
dphans

Reputation: 1673

For those who want to use constants to manage source code more optimally.

Because the json_serializable handler will try to call a "constant" function when passing a label to defaultValue if it is not "literal".

So what you need to do is create a static function that returns the default value:

@JsonSerializable(explicitToJson: true)
class MetadataDto {
  @JsonKey(name: "version_name")
  final String versionName;

  @JsonKey(name: "version_code")
  final int versionCode;

  const MetadataDto({
    this.versionName = "1.0.0",
    this.versionCode = 1,
  });

  static MetadataDto kDefault() => const MetadataDto();
}

In another instance, set the singleton function as defaultValue as it name (without parentheses).

@JsonSerializable(explicitToJson: true)
class ContentDto {
  @JsonKey(name: "metadata", defaultValue: MetadataDto.kDefault)
  final MetadataDto info;

  const ContentDto({
     required this.info,
  });
}

Check the .g.dart (generated file) after run build_runner:

ContentDto _$ContentDto FromJson(Map<String, dynamic> json) =>
    ContentDto(
      info: json['metadata'] == null
          ? MetadataDto.kDefault()
          : MetadataDto.fromJson(json['metadata'] as Map<String, dynamic>),
    );

That'is it.

Upvotes: 1

Pavel
Pavel

Reputation: 5876

Starting from json_serializable 5, generated fromJson uses default parameters from constructor, so you can do this

@JsonSerializable()
class Cart {
  final List<int> items;

  const Cart({
    this.items = const [],
  });

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

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

@JsonSerializable()
class User {
  final Cart cart;

  const User({
    this.cart = const Cart(),
  });

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

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

Then Cart.fromJson({}).items or User.fromJson({}).cart.items will give you []

Upvotes: 6

Mabsten
Mabsten

Reputation: 1988

In general, as an annotation argument you can only pass constants, so you cannot pass an object created with new but only with a const constructor (you should define all AvailableService fields as final, and define a const constructor). In json_Serializable, however, defaultValue currently has some additional constraints:

  • You can't even use a custom object created with a const constructor (or you get the error "it must be a literal"). There is an open request to remove this constraint: #741 - Const as default value
  • As defaultValue only literals are allowed; you could then pass a Map literal (ie: @JsonKey(defaultValue: {"supportsDelivery" : false, "supportsTableService" : false, /* etc */})), but even this possibility is currently only usable for the default value of fields with Map type, and not for fields with custom types (like AvailableService). In this case, you not get an error, but the defaultValue will never be used (by looking at the code generated in the .g.dart file, you can understand why). There are open requests for this issue as well: #676 - Allow default value encoded as JSON and #789 - how to set default value to nested JSON objects?

At the moment, therefore, until the aforementioned requests are followed up, the only workaround I have found is handle the defaultValue in the fromJson() factory:

  factory AvailableService.fromJson(Map<String, dynamic> json) =>
      json != null 
      ? _$AvailableServiceFromJson(json)
      : AvailableService(false, false, false, false);

In short, JsonKey.defaultValue currently only supports literals -not even accepting constants- and can only be used for the default value of primitive type fields (or List, Map, Set).

Upvotes: 11

Related Questions