Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IMap: _TypeError (type 'String' is not a subtype of type 'num / bool / int' in type cast) #1332

Closed
JoanSchi opened this issue Jun 27, 2023 · 1 comment

Comments

@JoanSchi
Copy link

JoanSchi commented Jun 27, 2023

Hi,

If json_serializable is used with IMap a Type error occurs when string is converted in the into bool / int/ double (num) or duration (FromJson). However DateTime, BigInt or Uri for example are parsed as expected.

json_serializable: ^6.7.0
fast_immutable_collections: ^9.1.5

FromJson
Looking to the generated code some types are not parsed correctly with the function FromJson:

Type error
IMap<X, String>.fromJson
X = int: (value) => value as bool,
X = bool: (value) => value as bool,
X double: (value) => (value as num).toDouble(),

Works good
IMap<X, String>.fromJson
X: (value) => DateTime.parse(value as String),
X: (value) => BigInt.parse(value as String),

ToJson
I do not known if it is necessary, but not everything is converted to string:

Not converted to string (problem?):
double/int/bool: (value) => value (Should this not be a String?)

Converted to string (works good)
DateTime: (value) => value.toIso8601String()
BigInt: (value) => value.toString(),

If this is resolved, I can resolve marcglasberg/fast_immutable_collections#58.

Thank you.

Example

import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:imap_test/example.dart';



void main() {
 
  final SerializableExample serializable = SerializableExample(

      // Works as espected:

      iMap: {DateTime(2023, 6, 25): 'IMap<DateTime,String>'}.lock,
      iEnumMap: {TestEnum.testValue: 'IMap<Enum,String>'}.lock,
      iStringMap: {'stringKey': 'IMap<String,String>'}.lock,
      iBigIntMap: {BigInt.from(8) : 'IMap<BigInt,String>'}.lock,
      
      iUriMap: {Uri(
    scheme: 'https',
    host: 'dart.dev',
    path: '/guides/libraries/library-tour',
    fragment: 'numbers') : 'Imap wit uri'}.lock,
      
      // Bug value as Type -> error

      // iDoubleMap :  {5.5: 'IMap<double,String>'}.lock,
      // iDurationMap: {Duration(milliseconds:200): 'IMap<Duration,String>'}.lock,
      // iIntMap: {2: 'IMap<Int,String>'}.lock,
      // iboolMap: {true : 'IMap<bool,String>'}.lock,
      );

  final Map<String, dynamic> json = serializable.toJson();

  final fromJsonTestMap = SerializableExample.fromJson(json);

  print('Output test serializableExample:\n ${fromJsonTestMap.toString()}');

}

SerializableExample


import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'example.g.dart';

enum TestEnum {
  testValue,
}

//  BigInt, bool, DateTime, double, Duration, Enum, int, Iterable, List, Map, num, Object, Record, Set, String, Uri
@JsonSerializable()
class SerializableExample {
  /// The generated code assumes these values exist in JSON.
  final IMap<DateTime, String> iMap;
  final IMap<TestEnum, String> iEnumMap;
  final IMap<String, String> iStringMap;
  final IMap<BigInt, String> iBigIntMap;
  final IMap<bool, String> iboolMap;
  final IMap<int, String> iIntMap;
  final IMap<Duration, String> iDurationMap;
  final IMap<double, String> iDoubleMap;
  final IMap<Object, String> iObjectMap;
final IMap<Uri, String> iUriMap;
  final Map<int, String> intMap;


  SerializableExample({
     this.iMap = const IMapConst({}),
     this.iEnumMap= const IMapConst({}),
     this.iStringMap = const IMapConst({}),
     this.iBigIntMap = const IMapConst({}),
     this.iboolMap = const IMapConst({}),
     this.iIntMap = const IMapConst({}),
    this.iDurationMap = const IMapConst({}),
     this.iDoubleMap = const IMapConst({}),
     this.iObjectMap = const IMapConst({}),
     this.iUriMap = const IMapConst({}),
     this.intMap = const{}
  });

  /// Connect the generated [_$PersonFromJson] function to the `fromJson`
  /// factory.
  factory SerializableExample.fromJson(Map<String, dynamic> json) =>
      _$SerializableExampleFromJson(json);

  /// Connect the generated [_$PersonToJson] function to the `toJson` method.
  Map<String, dynamic> toJson() => _$SerializableExampleToJson(this);
 

  @override
  String toString() {
    return 'SerializableExample(iMap: $iMap, iEnumMap: $iEnumMap, iStringMap: $iStringMap, iBigIntMap: $iBigIntMap, iboolMap: $iboolMap, iIntMap: $iIntMap, iDurationMap: $iDurationMap, iDoubleMap: $iDoubleMap, iObjectMap: $iObjectMap, iUriMap: $iUriMap, intMap: $intMap)';
  }
}

Generator code

part of 'example.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

SerializableExample _$SerializableExampleFromJson(Map<String, dynamic> json) =>
    SerializableExample(
      iMap: json['iMap'] == null
          ? const IMapConst({})
          : IMap<DateTime, String>.fromJson(
              json['iMap'] as Map<String, dynamic>,
              (value) => DateTime.parse(value as String),
              (value) => value as String),
      iEnumMap: json['iEnumMap'] == null
          ? const IMapConst({})
          : IMap<TestEnum, String>.fromJson(
              json['iEnumMap'] as Map<String, dynamic>,
              (value) => $enumDecode(_$TestEnumEnumMap, value),
              (value) => value as String),
      iStringMap: json['iStringMap'] == null
          ? const IMapConst({})
          : IMap<String, String>.fromJson(
              json['iStringMap'] as Map<String, dynamic>,
              (value) => value as String,
              (value) => value as String),
      iBigIntMap: json['iBigIntMap'] == null
          ? const IMapConst({})
          : IMap<BigInt, String>.fromJson(
              json['iBigIntMap'] as Map<String, dynamic>,
              (value) => BigInt.parse(value as String),
              (value) => value as String),
      iboolMap: json['iboolMap'] == null
          ? const IMapConst({})
          : IMap<bool, String>.fromJson(
              json['iboolMap'] as Map<String, dynamic>,
              (value) => value as bool,
              (value) => value as String),
      iIntMap: json['iIntMap'] == null
          ? const IMapConst({})
          : IMap<int, String>.fromJson(json['iIntMap'] as Map<String, dynamic>,
              (value) => value as int, (value) => value as String),
      iDurationMap: json['iDurationMap'] == null
          ? const IMapConst({})
          : IMap<Duration, String>.fromJson(
              json['iDurationMap'] as Map<String, dynamic>,
              (value) => Duration(microseconds: value as int),
              (value) => value as String),
      iDoubleMap: json['iDoubleMap'] == null
          ? const IMapConst({})
          : IMap<double, String>.fromJson(
              json['iDoubleMap'] as Map<String, dynamic>,
              (value) => (value as num).toDouble(),
              (value) => value as String),
      iObjectMap: json['iObjectMap'] == null
          ? const IMapConst({})
          : IMap<Object, String>.fromJson(
              json['iObjectMap'] as Map<String, dynamic>,
              (value) => value as Object,
              (value) => value as String),
      iUriMap: json['iUriMap'] == null
          ? const IMapConst({})
          : IMap<Uri, String>.fromJson(
              json['iUriMap'] as Map<String, dynamic>,
              (value) => Uri.parse(value as String),
              (value) => value as String),
      intMap: (json['intMap'] as Map<String, dynamic>?)?.map(
            (k, e) => MapEntry(int.parse(k), e as String),
          ) ??
          const {},
    );

Map<String, dynamic> _$SerializableExampleToJson(
        SerializableExample instance) =>
    <String, dynamic>{
      'iMap': instance.iMap.toJson(
        (value) => value.toIso8601String(),
        (value) => value,
      ),
      'iEnumMap': instance.iEnumMap.toJson(
        (value) => _$TestEnumEnumMap[value]!,
        (value) => value,
      ),
      'iStringMap': instance.iStringMap.toJson(
        (value) => value,
        (value) => value,
      ),
      'iBigIntMap': instance.iBigIntMap.toJson(
        (value) => value.toString(),
        (value) => value,
      ),
      'iboolMap': instance.iboolMap.toJson(
        (value) => value,
        (value) => value,
      ),
      'iIntMap': instance.iIntMap.toJson(
        (value) => value,
        (value) => value,
      ),
      'iDurationMap': instance.iDurationMap.toJson(
        (value) => value.inMicroseconds,
        (value) => value,
      ),
      'iDoubleMap': instance.iDoubleMap.toJson(
        (value) => value,
        (value) => value,
      ),
      'iObjectMap': instance.iObjectMap.toJson(
        (value) => value,
        (value) => value,
      ),
      'iUriMap': instance.iUriMap.toJson(
        (value) => value.toString(),
        (value) => value,
      ),
      'intMap': instance.intMap.map((k, e) => MapEntry(k.toString(), e)),
    };

const _$TestEnumEnumMap = {
  TestEnum.testValue: 'testValue',
};
@TimWhiting
Copy link

TimWhiting commented Jun 28, 2023

I believe this commit changed the behavior such that a portion of the workaround is no longer necessary: 5422fd4

It is a partial fix for #396. My PR is out of date. I'd have to look into it some more to see exactly how much of that issue it addresses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants