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 Crimson with a generic class #9

Open
simc opened this issue Jan 1, 2023 Discussed in #8 · 4 comments
Open

Using Crimson with a generic class #9

simc opened this issue Jan 1, 2023 Discussed in #8 · 4 comments
Labels
enhancement New feature or request

Comments

@simc
Copy link
Owner

simc commented Jan 1, 2023

Discussed in #8

Originally posted by Reprevise December 31, 2022
I have a class like this:

class ApiResponse<T> extends Equatable {
  final List<CustomError>? errors;
  final T? data;
  const ApiResponse({
    this.errors,
    this.data,
  });

  T get dataOrThrow => data ?? (throw error!);

  CustomError? get error {
    // ...
  }

  factory ApiResponse.fromJson(
    Map<String, dynamic> map,
    T Function(Map<String, dynamic> json) create,
  ) {
    return ApiResponse<T>(
      errors: map['errors'] != null
          ? List<CustomError>.from(
              map['errors']?.map((x) => CustomError.fromJson(x)),
            )
          : null,
      data: map['data'] != null ? create(map['data']) : null,
    );
  }

  factory ApiResponse.fromJsonList(
    Map<String, dynamic> map,
    T Function(List<Map<String, dynamic>>) create, {
    List<dynamic> Function(Map<String, dynamic> json)? list,
  }) {
    T? getData() {
      if (map['data'] == null) return null;
      final items = List<Map?>.from(
        list?.call(map['data'] as Map<String, dynamic>) ??
            map['data'] as List<dynamic>,
      );
      return create(
        items.whereNotNull().map((e) => e as Map<String, dynamic>).toList(),
      );
    }

    return ApiResponse<T>(
      errors: map['errors'] != null
          ? List<CustomError>.from(
              map['errors'].map((x) => CustomError.fromJson(x)),
            )
          : null,
      data: getData(),
    );
  }

  @override
  List<Object?> get props => [errors, data];
}

I use it as so:

return ApiResponse.fromJsonList(
  response.data as Map<String, dynamic>,
  (json) => json.map(MyModel.fromJson).toList(),
  list: (json) => List<Map?>.from(json['items']),
).data!;

How can I do this with Crimson?

@simc
Copy link
Owner Author

simc commented Jan 1, 2023

This is not possible to support currently unfortuantely. The reason is that Crimson needs to know all possible types of T at compile time to generate optimized code.

Do you think something like this would be helpful:

// define possible types of T

@json
typedef StringApiResponse = ApiResponse<String>;

@json
typedef ListApiResponse = ApiResponse<List<String>>;

void main() {
  // use one of the generated decoders
  Crimson(bytes).readStringApiResponse();
}

Another option would be waiting for sealed class support in dart and requiring generic types to be subtypes of sealed classes. That would be problematic with String for example however.

What do you think @Reprevise?

@Reprevise
Copy link

For my case specifically, it's not like I'm giving it a type and expecting it to parse it on its own as I'm providing it the method that it can parse it with as you can see with my create callback passed into my fromJson factory method. So how it works with my usecase is that it would parse the errors field and then use the method returned from the create callback to parse the data field. The issue that I'm facing is that Crimson only parses things via bytes (which I presume is where it gets its performance from).

@simc
Copy link
Owner Author

simc commented Jan 1, 2023

Is there a reason you want to write the fromJson method yourself? It's not doing anything special and could be created by either Crimson or json_serializable.

@Reprevise
Copy link

If you're talking about ApiResponse's fromJson method, then yeah, it can be generated.

@simc simc added the enhancement New feature or request label Jan 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants