How to run a code-generator on the top of another code-generator?

℡╲_俬逩灬. 提交于 2020-12-29 05:28:47

问题


Using the source_gen stack to make a code generator, how can I make a generator that generates code that would be the input of another generator (more specifically json_serializable)?

For example, consider:

class Example extends Generator {
  @override
  String generate(LibraryReader library, BuildStep buildStep) {
    return '''
@JsonSerializable(nullable: false)
class Person {
  final String firstName;
  final String lastName;
  final DateTime dateOfBirth;
  Person({this.firstName, this.lastName, this.dateOfBirth});
  factory Person.fromJson(Map<String, dynamic> json) => _PersonFromJson(json);
  Map<String, dynamic> toJson() => _PersonToJson(this);
}
''';
  }
}

This is an example of a code-generator that output code which then needs to be sent to json_serializable

What can I do so that json_serializable correctly generates here?


回答1:


Check the build.yaml config file documentation for more info, but I think you should use the applies_builders param that allows to execute another build after the defined one.

The example shows a builder that generates .tar.gz files and then executes another build that takes the .tar.gz files as input

builders:
  # The regular builder config, creates .tar.gz files.
  regular_builder:
    import: "package:my_package/builder.dart"
    builder_factories: ["myBuilder"]
    build_extensions: {".dart": [".tar.gz"]}
    auto_apply: dependents
    apply_builders: [":archive_extract_builder"]
post_process_builders:
  # The post process builder config, extracts .tar.gz files.
  extract_archive_builder:
    import: "package:my_package/extract_archive_builder.dart"
    builder_factory: "myExtractArchiveBuilder"
    input_extensions: [".tar.gz"]

so with source_gen you should implement for your build

applies_builders: ["source_gen|combining_builder", "json_serializable"]

and configure the other builder

json_serializable:
    import: "package:json_serializable/builder.dart"
    builder_factories: ["jsonSerializable"]
    build_extensions: {".dart": ["json_serializable.g.part"]}
    auto_apply: dependents
    build_to: cache
    applies_builders: ["source_gen|combining_builder"]



回答2:


It's not possible just with annotation because there maybe two packages that both have the @JsonSerializable annotation

There are two situtations :

  • You know what other generators should run after your generator.

    • https://stackoverflow.com/a/59605830/6877472 is one of the solutions
    • you can use the other generator's code in your own generator and call their generator's.generate function. example code:

class Example extends Generator {
    @override
    String generate(LibraryReader library, BuildStep buildStep) {
      return JsonSerializable().generate('''
          @JsonSerializable(nullable: false)
          class Person {
            final String firstName;
            final String lastName;
            final DateTime dateOfBirth;
            Person({this.firstName, this.lastName, this.dateOfBirth});
            factory Person.fromJson(Map<String, dynamic> json) => _PersonFromJson(json);
            Map<String, dynamic> toJson() => _PersonToJson(this);
          }
        ''');
     }

}

  • You don't know what other generators should run after your generator.

Unfortunately currently there is no way to tell the source_gen that your generator may produce a code that needs code generation.

I created an issue here https://github.com/dart-lang/source_gen/issues/442 if you want to subscribe




回答3:


You can decode the JSON by calling the jsonDecode() function, with the JSON string as the method argument.

Map<String, dynamic> user = jsonDecode(jsonString);

print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');

Now, Use the User.fromJson() constructor, for constructing a new User instance from a map structure and a toJson() method, which converts a User instance into a map.

employee.dart

class Employee {
  final String name;
  final String id;

  Employee(this.name, this.id);

  Employee.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        id = json['id'];

  Map<String, dynamic> toJson() =>
    {
      'name': name,
      'id': id,
    };
}

json_serializable is an automated source code generator that generates the JSON serialization boilerplate for you.

You need one regular dependency, and two dev dependencies to include json_serializable in your project.

dependencies:
  json_annotation: ^0.2.3

dev_dependencies:
  build_runner: ^0.8.0
  json_serializable: ^0.5.0

For more details on JSON serialization you can refer here

you can also use the Smoke library.

It's a subset of the Mirrors functionality but has both a Mirrors-based and a Codegen-based implementation. It's written by the PolymerDart team, so it's as close to "Official" as we're going to get.

While developing, it'll use the Mirrors-based encoding/decoding; but for publishing you can create a small transformer that will generate code.

Seth Ladd created a code sample here, which I extended slightly to support child-objects:



来源:https://stackoverflow.com/questions/59503994/how-to-run-a-code-generator-on-the-top-of-another-code-generator

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!