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

Using an int as a Map key #434

Closed
SylvainLosey opened this issue Apr 2, 2019 · 2 comments
Closed

Using an int as a Map key #434

SylvainLosey opened this issue Apr 2, 2019 · 2 comments

Comments

@SylvainLosey
Copy link

SylvainLosey commented Apr 2, 2019

I would like to create Maps of Objects with their Id as key, but I get this error message from json_serializable:

Could not generate `fromJson` code for `pickups` because of type `int`. Map keys must be of type `String`, enum, `Object` or `dynamic`.

Is there any reason for this ?

@kevmoo
Copy link
Collaborator

kevmoo commented Apr 2, 2019

I would like to create Maps of Objects with their Id as key, but I get this error message from json_serializable:

Could not generate `fromJson` code for `pickups` because of type `int`. Map keys must be of type `String`, enum, `Object` or `dynamic`.

Is there any reason for this ?

Yup. JSON requires keys to be String. We'd have to add logic to convert to/from ints to String.

Rolling this into #396

@kevmoo kevmoo closed this as completed Apr 2, 2019
@fzyzcjy
Copy link
Contributor

fzyzcjy commented Mar 7, 2021

Seems that now it is implemented! YEAH!

enum MyEnum { apple, banana, orange }

@JsonSerializable()
class HelloEntity {
  List<String>? list;
  Map<int, int>? intIntMap;
  Map<int, Object>? intObjMap;
  Map<MyEnum, HelloEntity>? enumRecursiveMap;
  Map<int, MyEnum>? intEnumMap;

  HelloEntity();

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

  Map<String, dynamic> toJson() => _$HelloEntityToJson(this);
}
HelloEntity _$HelloEntityFromJson(Map<String, dynamic> json) {
  return HelloEntity()
    ..list = (json['list'] as List<dynamic>?)?.map((e) => e as String).toList()
    ..intIntMap = (json['intIntMap'] as Map<String, dynamic>?)?.map(
      (k, e) => MapEntry(int.parse(k), e as int),
    )
    ..intObjMap = (json['intObjMap'] as Map<String, dynamic>?)?.map(
      (k, e) => MapEntry(int.parse(k), e as Object),
    )
    ..enumRecursiveMap =
        (json['enumRecursiveMap'] as Map<String, dynamic>?)?.map(
      (k, e) => MapEntry(_$enumDecode(_$MyEnumEnumMap, k),
          HelloEntity.fromJson(e as Map<String, dynamic>)),
    )
    ..intEnumMap = (json['intEnumMap'] as Map<String, dynamic>?)?.map(
      (k, e) => MapEntry(int.parse(k), _$enumDecode(_$MyEnumEnumMap, e)),
    );
}

Map<String, dynamic> _$HelloEntityToJson(HelloEntity instance) =>
    <String, dynamic>{
      'list': instance.list,
      'intIntMap': instance.intIntMap?.map((k, e) => MapEntry(k.toString(), e)),
      'intObjMap': instance.intObjMap?.map((k, e) => MapEntry(k.toString(), e)),
      'enumRecursiveMap': instance.enumRecursiveMap
          ?.map((k, e) => MapEntry(_$MyEnumEnumMap[k], e)),
      'intEnumMap': instance.intEnumMap
          ?.map((k, e) => MapEntry(k.toString(), _$MyEnumEnumMap[e])),
    };

K _$enumDecode<K, V>(
  Map<K, V> enumValues,
  Object? source, {
  K? unknownValue,
}) {
  if (source == null) {
    throw ArgumentError(
      'A value must be provided. Supported values: '
      '${enumValues.values.join(', ')}',
    );
  }

  return enumValues.entries.singleWhere(
    (e) => e.value == source,
    orElse: () {
      if (unknownValue == null) {
        throw ArgumentError(
          '`$source` is not one of the supported values: '
          '${enumValues.values.join(', ')}',
        );
      }
      return MapEntry(unknownValue, enumValues.values.first);
    },
  ).key;
}

P.S. seems to only support the following

Map keys must be one of: Object, dynamic, enum, String, BigInt, DateTime, int, Uri.

gnprice added a commit to gnprice/zulip-flutter that referenced this issue Oct 17, 2023
Calling just `int.parse(s)`, without a `radix:` argument, invokes
a special behavior where `int.parse` not only accepts decimal strings
like "42", but also hexadecimal strings like "0x2a".

That's a bit unexpected.  In any case it's definitely not something
we want when interpreting any part of the Zulip API.

Fix the one place we had this in our own code.  There remain two
places it appears in the code generated by `json_serializable`;
mark those with TODO comments.  It'd be nice to fix those too,
but realistically this quirk is unlikely to ever cause a problem,
so it's not worth a lot of effort to resolve.

(Note that this doesn't affect the bulk of places we have an int
in the API types, because most of those are handled by jsonDecode
before the `json_serializable`-generated code ever sees them.
It only affects the keys of `Map<int, …>` structures.)

It looks like there's no existing thread in the `json_serializable`
tracker for this issue.  The most closely related is from where the
handling of `Map<int, …>` types was added in the first place:
  google/json_serializable.dart#434
chrisbobbe pushed a commit to zulip/zulip-flutter that referenced this issue Oct 18, 2023
Calling just `int.parse(s)`, without a `radix:` argument, invokes
a special behavior where `int.parse` not only accepts decimal strings
like "42", but also hexadecimal strings like "0x2a".

That's a bit unexpected.  In any case it's definitely not something
we want when interpreting any part of the Zulip API.

Fix the one place we had this in our own code.  There remain two
places it appears in the code generated by `json_serializable`;
mark those with TODO comments.  It'd be nice to fix those too,
but realistically this quirk is unlikely to ever cause a problem,
so it's not worth a lot of effort to resolve.

(Note that this doesn't affect the bulk of places we have an int
in the API types, because most of those are handled by jsonDecode
before the `json_serializable`-generated code ever sees them.
It only affects the keys of `Map<int, …>` structures.)

It looks like there's no existing thread in the `json_serializable`
tracker for this issue.  The most closely related is from where the
handling of `Map<int, …>` types was added in the first place:
  google/json_serializable.dart#434
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

3 participants