freezed icon indicating copy to clipboard operation
freezed copied to clipboard

Use of custom converter in freezed class

Open prajwal27 opened this issue 2 years ago • 2 comments

Describe the bug I have json responses like these and these need to be serialized to BlockId Class.

{"block_hash": "0x3871c8a0c3555687515a07f365f6f5b1d8c2ae953f7844575b8bde2b2efed27"}
{"block_number": 1}

and

"block_tag"

These are the 3 types of responses that I want to deserialize. I don't have a problem with the first two. It's the 3rd one that is causing an issue. The BlockId for the 1st two will have respective attributes, but for the 3rd one, itself should be a String value.

This is my current code, but it's giving me wrong output. toJson for the 3rd type should yield a String and not a JSON.

@freezed
class BlockId with _$BlockId {
  const factory BlockId.blockHash({
    required StarknetFieldElement blockHash,
  }) = BlockIdHash;
  const factory BlockId.blockNumber({
    required int blockNumber,
  }) = BlockIdNumber;
  const factory BlockId.blockTag({
    required String blockTag,
  }) = BlockIdTag;

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

}

I came across documentation for using Converter and I tried this

class BlockIdConverter implements JsonConverter<BlockId, dynamic> {
  const BlockIdConverter();

  // BlockId can be constructed from a Map as well as a String.
  @override
  BlockId fromJson(dynamic value) {
    if (value is Map) {
      Map<String, dynamic> json = value as Map<String, dynamic>;

      // you need to find some condition to know which type it is. e.g. check the presence of some field in the json
      if (json.containsKey('block_hash')) {
        return BlockId.blockHash(blockHash: json['block_hash']);
      } else if (json.containsKey('block_number')) {
        return BlockId.blockNumber(blockNumber: json['block_number']);
      }
    } else if (value is String) {
      return BlockId.blockTag(blockTag: value);
    }

    throw Exception('Could not determine the BlockId from given value: $json');
  }

  @override
  dynamic toJson(BlockId data) {
    return data.when<dynamic>(blockHash: (StarknetFieldElement blockHash) {
      return <String, dynamic>{'block_hash': blockHash};
    }, blockNumber: (int blockNumber) {
      return <String, dynamic>{'block_number': blockNumber};
    }, blockTag: (String blockTag) {
      return blockTag;
    });
  }
}

It's still giving me error, when I try to perform this

BlockId blockId = valid_object;
json.encode(blockId)

It says that the toJson method is missing from the object.

Expected behavior I would expect the 3 different types of deserialized and serialized the same via the BlockId class.

prajwal27 avatar Aug 01 '22 13:08 prajwal27

@rrousselGit Do you have any thoughts on this?

prajwal27 avatar Aug 07 '22 14:08 prajwal27

@rrousselGit Apologies for tagging. Would like to know if you could look into this!

prajwal27 avatar Aug 17 '22 05:08 prajwal27

My guess is that you need to enable explicit_to_json for json_serializable

Cf the documentation:

Note: In order to serialize nested lists of freezed objects, you are supposed to either specify a @JsonSerializable(explicitToJson: true) or change explicit_to_json inside your build.yaml file (see the documentation).

rrousselGit avatar Sep 27 '22 14:09 rrousselGit