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

Support adding custom annotations for conversion #43

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
### Added
- Added unknown_enum_value support
- Added non_final support
- Added support for json_converter

## [2.3.0] - 2020-10-04
### Added
Expand Down
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Example of the `model_generator/config.yaml` file
```
UserModel:
path: webservice/user
converters:
- DateTimeConverter
properties:
id:
type: int
Expand Down Expand Up @@ -69,9 +71,11 @@ UserModel:
mutableField:
non_final: true #Field will not be marked final
type: string
changedAt:
type: datetime

Address:
path: webservice/user
path: webservice/user #Can also be package:... and/or end with the actual file (.dart)
properties:
street:
type: string
Expand All @@ -83,6 +87,12 @@ CustomBaseDirectoryObject:
properties:
path:
type: string

#Custom json converter. Use with converters property on models
DateTimeConverter:
type: json_converter
path: converter/

```

## Enum support
Expand Down Expand Up @@ -141,4 +151,22 @@ CustomObjectFromToJson:
{Model_Name} handle{Model_Name}FromJson(object) => {Model_Name}.fromJson(object);

{Original_Type} handle{Model_Name}ToJson({Model_Name} data) => data.toJson();
```
```

## JsonConverter support
You can specify custom json converters to be used for types that match
```
UserModel:
path: webservice/user
converters:
- DateTimeConverter
properties:
changedAt:
type: datetime
```
Specify the custom JsonConverter object as a known type to resolve it
```
DateTimeConverter:
type: json_converter
path: converter/
```
8 changes: 6 additions & 2 deletions bin/model_generator.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import 'dart:io';

import 'package:path/path.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart';

import 'src/config/pubspec_config.dart';
import 'src/config/yml_generator_config.dart';
import 'src/model/model/custom_model.dart';
import 'src/model/model/enum_model.dart';
import 'src/model/model/json_converter_model.dart';
import 'src/model/object_model.dart';
import 'src/writer/enum_model_writer.dart';
import 'src/writer/object_model_writer.dart';
Expand Down Expand Up @@ -44,9 +45,12 @@ void writeToFiles(
}
String content;
if (model is ObjectModel) {
content = ObjectModelWriter(pubspecConfig, model).write();
content =
ObjectModelWriter(pubspecConfig, model, modelGeneratorConfig).write();
} else if (model is EnumModel) {
content = EnumModelWriter(model).write();
} else if (model is JsonConverterModel) {
return;
}
if (model is! CustomModel && content == null) {
throw Exception(
Expand Down
49 changes: 26 additions & 23 deletions bin/src/config/yml_generator_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import '../model/item_type/string_type.dart';
import '../model/model/custom_from_to_json_model.dart';
import '../model/model/custom_model.dart';
import '../model/model/enum_model.dart';
import '../model/model/json_converter_model.dart';
import '../model/model/model.dart';
import '../model/object_model.dart';
import '../util/type_checker.dart';
Expand All @@ -29,14 +30,17 @@ class YmlGeneratorConfig {
value['base_directory'] ?? pubspecConfig.baseDirectory;
final String path = value['path'];
final YamlMap properties = value['properties'];
final YamlList converters = value['converters'];
final String type = value['type'];
if (type == 'custom') {
models.add(CustomModel(key, path, baseDirectory));
return;
}
if (type == 'custom_from_to_json') {
} else if (type == 'custom_from_to_json') {
models.add(CustomFromToJsonModel(key, path, baseDirectory));
return;
} else if (type == 'json_converter') {
models.add(JsonConverterModel(key, path, baseDirectory));
return;
}
if (properties == null) {
throw Exception('Properties can not be null. model: $key');
Expand All @@ -59,12 +63,15 @@ class YmlGeneratorConfig {
}
fields.add(getField(propertyKey, propertyValue));
});
models.add(ObjectModel(key, path, baseDirectory, fields));
final mappedConverters =
converters?.map((element) => element.toString())?.toList() ??
<String>[];
models.add(
ObjectModel(key, path, baseDirectory, fields, mappedConverters));
}
});

checkIfTypesAvailable();
addPathsToFields(pubspecConfig);
}

Field getField(String name, YamlMap property) {
Expand Down Expand Up @@ -135,25 +142,21 @@ class YmlGeneratorConfig {
}
}

void addPathsToFields(PubspecConfig pubspecConfig) {
models.forEach((model) {
if (model is ObjectModel) {
model.fields.forEach((field) {
final foundModels =
models.where((model) => model.name == field.type.name).toList();
if (foundModels.isNotEmpty) {
final foundModel = foundModels[0];
final baseDirectory =
foundModel.baseDirectory ?? pubspecConfig.baseDirectory;
if (foundModel.path == null) {
field.path = '$baseDirectory';
} else {
field.path = '$baseDirectory/${foundModel.path}';
}
}
});
} else if (model is EnumModel) {}
});
String getPathForField(PubspecConfig pubspecConfig, Field field) {
return getPathForName(pubspecConfig, field.type.name);
}

String getPathForName(PubspecConfig pubspecConfig, String name) {
final foundModel =
models.firstWhere((model) => model.name == name, orElse: () => null);
if (foundModel == null) return null;
final baseDirectory =
foundModel.baseDirectory ?? pubspecConfig.baseDirectory;
if (foundModel.path == null) {
return '$baseDirectory';
} else {
return '$baseDirectory/${foundModel.path}';
}
}

void checkIfTypesAvailable() {
Expand Down
12 changes: 0 additions & 12 deletions bin/src/model/field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,6 @@ class Field {
final bool nonFinal;
final String unknownEnumValue;

String _path;

set path(String path) {
if (path != null && path.endsWith('/')) {
_path = path.substring(0, path.length - 1);
} else {
_path = path;
}
}

String get path => _path;

Field(
{String name,
this.type,
Expand Down
9 changes: 9 additions & 0 deletions bin/src/model/model/json_converter_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'model.dart';

class JsonConverterModel extends Model {
JsonConverterModel(
String name,
String path,
String baseDirectory,
) : super(name, path, baseDirectory);
}
2 changes: 2 additions & 0 deletions bin/src/model/object_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import 'model/model.dart';

class ObjectModel extends Model {
final List<Field> fields;
final List<String> converters;

ObjectModel(
String name,
String path,
String baseDirectory,
this.fields,
this.converters,
) : super(name, path, baseDirectory);
}
57 changes: 39 additions & 18 deletions bin/src/writer/object_model_writer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,43 @@ import '../util/type_checker.dart';
class ObjectModelWriter {
final PubspecConfig pubspecConfig;
final ObjectModel jsonModel;
final YmlGeneratorConfig yamlConfig;

const ObjectModelWriter(this.pubspecConfig, this.jsonModel);
const ObjectModelWriter(this.pubspecConfig, this.jsonModel, this.yamlConfig);

String write() {
final sb = StringBuffer();
final imports = Set<String>(); // ignore: prefer_collection_literals

final containsRequiredFields =
jsonModel.fields.where((item) => item.required).toList().isNotEmpty;
if (containsRequiredFields) {
sb.writeln("import 'package:flutter/material.dart';");
imports.add("import 'package:flutter/material.dart';");
}

sb.writeln("import 'package:json_annotation/json_annotation.dart';");
imports.add("import 'package:json_annotation/json_annotation.dart';");

final projectName = pubspecConfig.projectName;
jsonModel.fields.forEach((field) {
if (!TypeChecker.isKnownDartType(field.type.name)) {
final reCaseFieldName = CaseUtil(field.type.name);
String import;
if (field.path == null) {
import =
"import 'package:$projectName/${jsonModel.baseDirectory}/${reCaseFieldName.snakeCase}.dart';";
} else {
import =
"import 'package:$projectName/${field.path}/${reCaseFieldName.snakeCase}.dart';";
}
if (!sb.toString().contains(import)) {
sb.writeln(import);
}
imports.add(_getImportFromPath(field.type.name));
}
});
jsonModel.converters.forEach((converter) {
imports.add(_getImportFromPath(converter));
});
imports.forEach(sb.writeln);

sb
..writeln()
..writeln("part '${jsonModel.fileName}.g.dart';")
..writeln()
..writeln('@JsonSerializable()')
..writeln('class ${jsonModel.name} {');
..writeln('@JsonSerializable()');

jsonModel.converters.forEach((converter) {
sb.writeln('@$converter()');
});

sb.writeln('class ${jsonModel.name} {');

jsonModel.fields.sort((a, b) {
final b1 = a.required ? 1 : 0;
Expand Down Expand Up @@ -116,4 +115,26 @@ class ObjectModelWriter {
..writeln('}');
return sb.toString();
}

String _getImportFromPath(String name) {
final projectName = pubspecConfig.projectName;
final reCaseFieldName = CaseUtil(name);
final path = yamlConfig.getPathForName(pubspecConfig, name);
if (path == null) {
return "import 'package:$projectName/${jsonModel.baseDirectory}/${reCaseFieldName.snakeCase}.dart';";
}

String pathWithPackage;
if (path.startsWith('package:')) {
pathWithPackage = path;
} else {
pathWithPackage = 'package:$projectName/$path';
}

if (path.endsWith('.dart')) {
return "import '$pathWithPackage';";
} else {
return "import '$pathWithPackage/${reCaseFieldName.snakeCase}.dart';";
}
}
}
17 changes: 17 additions & 0 deletions example/lib/model/converter/date_time_converter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:json_annotation/json_annotation.dart';

class DateTimeConverter implements JsonConverter<DateTime, String> {
const DateTimeConverter();

@override
DateTime fromJson(String json) {
var actualJson = json;
if (json.contains('.')) {
actualJson = json.substring(0, json.length - 1);
}
return DateTime.parse(actualJson);
}

@override
String toJson(DateTime json) => json.toIso8601String();
}
5 changes: 5 additions & 0 deletions example/lib/model/ogm.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:model_generator_example/model/converter/date_time_converter.dart';

part 'ogm.g.dart';

@JsonSerializable()
@DateTimeConverter()
class OGM {
@JsonKey(name: 'structuredMessage', required: true)
final String structuredMessage;
Expand All @@ -21,6 +23,8 @@ class OGM {
final String securityRole;
@JsonKey(name: 'mutableProperty', nullable: true)
String mutableProperty;
@JsonKey(name: 'dateChange', nullable: true)
final DateTime dateChange;

OGM({
@required this.structuredMessage,
Expand All @@ -31,6 +35,7 @@ class OGM {
@required this.someThinGHuGE,
this.securityRole,
this.mutableProperty,
this.dateChange,
});

factory OGM.fromJson(Map<String, dynamic> json) => _$OGMFromJson(json);
Expand Down
3 changes: 3 additions & 0 deletions example/lib/model/ogm.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading