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

Generic JsonConverter Not Working #1066

Closed
yousinix opened this issue Dec 13, 2021 · 9 comments
Closed

Generic JsonConverter Not Working #1066

yousinix opened this issue Dec 13, 2021 · 9 comments

Comments

@yousinix
Copy link


If we have a simple generic JsonConverter like this:

class IndexedConverter<T> implements JsonConverter<List<T>, List<dynamic>> {
  const IndexedConverter(this.fromJsonConverter);

  final T Function(Map<String, dynamic> onMap) fromJsonConverter;

  @override
  List<T> fromJson(List<dynamic> jsonItems) {
    return jsonItems.asMap().entries.map((entry) {
      final index = entry.key;
      final json = entry.value;
      return fromJsonConverter({'index': index, ...json});
    }).toList();
  }

  // ...
}

Then it was used on a class field like this:

@JsonSerializable()
class Question {
  // ...
  @IndexedConverter(Choice.fromJson)
  final List<Choice> choices;
}

The actual output is:

Question _$QuestionFromJson(Map<String, dynamic> json) => Question(
      choices: (json['choices'] as List<dynamic>)
          .map((e) => Choice.fromJson(e as Map<String, dynamic>))
          .toList(),
    );

While the expected output is:

Question _$QuestionFromJson(Map<String, dynamic> json) => Question(
      choices: IndexedConverter(Choice.fromJson).fromJson(json['choices'] as List<dynamic>),
    );
@qevka
Copy link

qevka commented Feb 7, 2022

I had the same problem. Still looking for a solution.

@kevmoo
Copy link
Collaborator

kevmoo commented Feb 8, 2022

Try using JsonKey(fromJson) instead?

@JsonSerializable()
class Question {
  const Question({
    required this.choices,
  });

  @JsonKey(fromJson: _choiceListFromJson)
  final List<Choice> choices;
}

List<Choice> _choiceListFromJson(List<dynamic> items) =>
    const IndexedConverter<Choice>(Choice.fromJson).fromJson(items);

@kevmoo kevmoo closed this as completed Feb 8, 2022
@yousinix
Copy link
Author

yousinix commented Feb 9, 2022

This is a workaround, but it doesn't fix the issue.

This issue is about annotating members directly with IndexedConverter, without the need to define a custom fromJson function for each one. Using this workaround contradicts with the idea of Custom Converters.

@DevNico
Copy link

DevNico commented May 31, 2022

I just wanted to open a similar issue. I don't feel like this is resolved. Can this issue get some attention again please?

@kevmoo
Copy link
Collaborator

kevmoo commented Jun 10, 2022

Please post the code you'd like to write here ⬇️

@yousinix
Copy link
Author

@kevmoo I have attached a reproduction repo in the issue's description, it might be helpful.

@kevmoo
Copy link
Collaborator

kevmoo commented Jul 30, 2022

Could you try upgrading to Dart 2.17?

I just ran your reproduction on my machine – everything ran cleanly!

@tlserver
Copy link

I am facing the same issue in json_annotation 4.8.1, json_serializable 6.7.1 and dart 3.1.5. Here is a MRE:

import 'package:json_annotation/json_annotation.dart';

part 'test.g.dart';

typedef JsonObject = Map<String, dynamic>;

// A data class to be converted
class UselessGeneric<T> {
  final T value;

  const UselessGeneric(this.value);
}

// The json converter which does not work currently
class UselessGenericJsonConverter<T>
    extends JsonConverter<UselessGeneric<T>, T> {
  const UselessGenericJsonConverter();

  @override
  UselessGeneric<T> fromJson(T json) => UselessGeneric(json);

  @override
  T toJson(UselessGeneric<T> object) => object.value;
}

// Workaround json converter
class StringUselessGenericJsonConverter
    extends UselessGenericJsonConverter<String> {
  const StringUselessGenericJsonConverter();
}

@JsonSerializable()
@UselessGenericJsonConverter<String>() // Not Work : Exception when `dart run build_runner build`
// @StringUselessGenericJsonConverter() // This work
class Example {
  final UselessGeneric<String> text;

  Example(this.text);

  factory Example.fromJson(JsonObject json) => _$ExampleFromJson(json);

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

I think @UselessGenericJsonConverter<String>() and @StringUselessGenericJsonConverter() should have exactly the same behavior, but actually not.

@black-rusuz
Copy link

black-rusuz commented Dec 18, 2023

Having the same problem.

I wrote in my model.dart:

    @Default(0) @cDouble double price,
    @cDouble double? oldPrice,

But in model.g.file:

      price: (json['price'] as num?)?.toDouble() ?? 0,
      oldPrice: cDouble.fromJson(json['old_price']),

What's interesting is that price has @Default annotation, but oldPrice doesn't.
Here's my converter:

const _digits = 6;
const cDouble = DoubleConverter();

class DoubleConverter extends JsonConverter<double?, dynamic> {
  const DoubleConverter();

  @override
  double? fromJson(dynamic json) {
    return parseDouble(json);
  }

  @override
  dynamic toJson(double? object) {
    final string = object?.toStringAsFixed(_digits) ?? '';
    return double.tryParse(string);
  }
}

double? parseDouble(dynamic value) {
  return value is num
      ? double.parse(value.toStringAsFixed(_digits))
      : double.tryParse(value.toString());
}

UPD: versions
Flutter 3.16.0
Dart 3.2.0
json_serializable: 6.7.1

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

No branches or pull requests

6 participants