├── .DS_Store
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin
├── build.sh
├── json2dartc.dart
└── json_to_dart
│ ├── helpers.dart
│ ├── json_to_dart.dart
│ ├── model_generator.dart
│ ├── syntax.dart
│ └── warning.dart
├── build
├── json2dart
└── json2dart.exe
├── media
└── example.gif
├── pubspec.lock
└── pubspec.yaml
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pacifio/json2dart/eb09c60dbf51df1474292148f9852cc7e0d249e7/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .dart_tool/
2 | .packages
3 | doc/api/
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.0.0
2 |
3 | - Initial version, created by Stagehand
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2021 Adib Mohsin
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Json 2 Dart Command line utility
2 |
3 | ## Important note
4 |
5 | There is already a package called `json2dart` so this package will be called `json2dartc` !
6 |
7 |
8 |
9 | This project was made using [javiercbk's json_to_dart package](https://github.com/javiercbk/json_to_dart) ! This CLI was made to directly convert JSON stuctures into Dart classes . I personally don't like build runners and json serializers so I made this for my workflow . Feel free to open issues and submit PRs .
10 |
11 | ## How to use
12 |
13 | Install this via `pub`
14 |
15 | `pub global activate json2dartc`
16 |
17 | ## Example
18 |
19 | `json2dartc -u https://reqres.in/api/users -m get -e data -n Example`
20 |
21 | ## Null safety
22 |
23 | To turn on null safe code generation , add the flag `--null-safe` , Example :
24 |
25 | `json2dartc -u https://reqres.in/api/users -m get -n Example --null-safe`
26 |
27 | ## Options
28 |
29 | ```
30 | -u, --api API Endpoint required to grab the json from
31 | -e, --entry Entry point for json data structure , e.g data.data will get the nested data array/object from API response
32 | -n, --name Name of your data class
33 | (defaults to "AutoGenerated")
34 | -h, --headers Headers for your API endpoint
35 | -m, --method Method for http request , defaults to GET
36 | (defaults to "GET")
37 | ```
38 |
39 | | Option | required | default | example | note |
40 | | ------ | -------- | --------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------- |
41 | | -u | true | required field | https://reqres.in/api/users | |
42 | | -e | false | '' | data | it's used for special access in json data , e.g data.data will access nested data object within data object |
43 | | -n | false | 'AutoGenerated' | Example | |
44 | | -h | false | {} | access-token=01234,foo=bar | |
45 | | -m | false | GET | GET/POST | |
46 |
47 | ## Upcoming plans
48 |
49 | - [x] Null safety support
50 | - [ ] Tool itself written with null safety
51 | - [ ] Private memmbers option
52 | - [ ] Option to load json from a file
53 |
--------------------------------------------------------------------------------
/bin/build.sh:
--------------------------------------------------------------------------------
1 | dart compile exe ./json2dartc.dart -o ../build/json2dartc && dart compile exe ./json2dartc.dart -o ../build/json2dartc.exe
--------------------------------------------------------------------------------
/bin/json2dartc.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021, Adib Mohsin
2 | // Credits to https://github.com/javiercbk for making `json_to_dart` package
3 |
4 | import 'dart:convert';
5 | import 'dart:io';
6 | import 'package:args/args.dart';
7 | import 'package:http/http.dart' as http;
8 |
9 | import 'json_to_dart/json_to_dart.dart';
10 |
11 | void quit(String msg) {
12 | print(msg ?? 'Something went wrong');
13 | exit(0);
14 | }
15 |
16 | Map parseHeaders(String headers) {
17 | try {
18 | final result = {};
19 | final split = headers.split(',');
20 | split.forEach((header) {
21 | final data = header.split('=');
22 |
23 | result[data[0]] = data[1];
24 | });
25 |
26 | return result;
27 | } catch (_) {
28 | return {};
29 | }
30 | }
31 |
32 | void main(List arguments) async {
33 | final stopwatch = Stopwatch()..start();
34 | final parser = ArgParser();
35 |
36 | parser.addOption('api',
37 | help: 'API Endpoint required to grab the json from', abbr: 'u');
38 | parser.addOption('entry',
39 | help:
40 | 'Entry point for json data structure , e.g data.data will get the nested data array/object from API response',
41 | abbr: 'e');
42 | parser.addOption('name',
43 | help: 'Name of your data class', abbr: 'n', defaultsTo: 'AutoGenerated');
44 | parser.addOption('headers', help: 'Headers for your API endpoint', abbr: 'h');
45 | parser.addOption('method',
46 | help: 'Method for http request , defaults to GET',
47 | abbr: 'm',
48 | defaultsTo: 'GET');
49 | parser.addFlag('null-safe',
50 | negatable: false,
51 | defaultsTo: false,
52 | help:
53 | 'Add this flag if you want to generate null safe code , by default we will generate without null safety !');
54 |
55 | try {
56 | final results = parser.parse(arguments);
57 |
58 | if (results['null-safe']) {
59 | print('You have turned on null safety !');
60 | }
61 |
62 | if (results['api'] != null) {
63 | try {
64 | String body;
65 | int statusCode;
66 |
67 | try {
68 | final url = Uri.parse(results['api']);
69 | final header = parseHeaders(results['headers']);
70 |
71 | if (results['method'].toString().trim().toLowerCase() == 'get') {
72 | final response =
73 | await http.get(url, headers: Map.from(header));
74 | body = response.body;
75 | statusCode = response.statusCode;
76 | } else if (results['method'].toString().trim().toLowerCase() ==
77 | 'post') {
78 | final response =
79 | await http.post(url, headers: Map.from(header));
80 | body = response.body;
81 | statusCode = response.statusCode;
82 | } else {
83 | quit('Only get and post methods are supported');
84 | }
85 | } catch (e) {
86 | quit('Could not send request , quitting !');
87 | }
88 |
89 | if (statusCode != null) {
90 | if (statusCode == 200) {
91 | try {
92 | final parsed = jsonDecode(body);
93 |
94 | dynamic finalData;
95 |
96 | if (results['entry'] != null) {
97 | try {
98 | final split = results['entry'].toString().trim().split('.');
99 |
100 | if (split.length == 1) {
101 | finalData = parsed[split[0]];
102 | } else {
103 | finalData = parsed;
104 | split.forEach((element) {
105 | finalData = finalData[element];
106 | });
107 | }
108 | } catch (_) {
109 | print('Entry is not valid , using default data');
110 | finalData = parsed;
111 | }
112 | } else {
113 | finalData = parsed;
114 | }
115 |
116 | if (finalData != null) {
117 | final name = results['name'];
118 | final encoder = JsonEncoder.withIndent(' ');
119 |
120 | print('FILTERED JSON RESPONSE \n');
121 | print(encoder.convert(finalData));
122 |
123 | try {
124 | final stringify = jsonEncode(finalData);
125 | final classGenerator = ModelGenerator(name);
126 | final dartCode = classGenerator.generateDartClasses(
127 | stringify,
128 | nullSafe: results['null-safe'],
129 | );
130 |
131 | try {
132 | var file = File('$name.dart');
133 |
134 | file.writeAsString(dartCode.code).then((_) {
135 | print('Model created successfully\n');
136 | print(
137 | 'Took ${stopwatch.elapsed.inMilliseconds / 1000} seconds !');
138 | });
139 | } catch (_) {
140 | quit('Could not write to file');
141 | }
142 | } catch (e) {
143 | print("\n");
144 | print(e);
145 | quit('Could not generate models !');
146 | }
147 | } else {
148 | quit('Something went wrong , could not parse JSON');
149 | }
150 | } catch (_) {
151 | quit('Error parsing JSON body , quitting !');
152 | }
153 | } else if (statusCode == 400) {
154 | quit('Server not found , quitting !');
155 | } else if (statusCode == 401) {
156 | quit('Authorization error , consider providing an access token');
157 | } else {
158 | quit('Server didn\'nt respond , status code $statusCode');
159 | }
160 | }
161 | } on Exception catch (_) {
162 | quit('Could not parse API endpoint , quitting !');
163 | }
164 | } else {
165 | quit('API is an empty string , quitting !');
166 | }
167 | } on FormatException catch (_) {
168 | print(parser.usage);
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/bin/json_to_dart/helpers.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert' as Convert;
2 | import 'dart:math';
3 | import 'package:json_ast/json_ast.dart'
4 | show Node, ObjectNode, ArrayNode, LiteralNode;
5 |
6 | import './syntax.dart';
7 |
8 | const Map PRIMITIVE_TYPES = const {
9 | 'int': true,
10 | 'double': true,
11 | 'String': true,
12 | 'bool': true,
13 | 'DateTime': false,
14 | 'List': false,
15 | 'List': true,
16 | 'List': true,
17 | 'List': true,
18 | 'List': true,
19 | 'Null': true,
20 | };
21 |
22 | enum ListType { Object, String, Double, Int, Null }
23 |
24 | class MergeableListType {
25 | final ListType listType;
26 | final bool isAmbigous;
27 |
28 | MergeableListType(this.listType, this.isAmbigous);
29 | }
30 |
31 | MergeableListType mergeableListType(List list) {
32 | ListType t = ListType.Null;
33 | bool isAmbigous = false;
34 | list.forEach((e) {
35 | ListType inferredType;
36 | if (e.runtimeType == 'int') {
37 | inferredType = ListType.Int;
38 | } else if (e.runtimeType == 'double') {
39 | inferredType = ListType.Double;
40 | } else if (e.runtimeType == 'string') {
41 | inferredType = ListType.String;
42 | } else if (e is Map) {
43 | inferredType = ListType.Object;
44 | }
45 | if (t != ListType.Null && t != inferredType) {
46 | isAmbigous = true;
47 | }
48 | t = inferredType;
49 | });
50 | return MergeableListType(t, isAmbigous);
51 | }
52 |
53 | String camelCase(String text) {
54 | String capitalize(Match m) =>
55 | m[0].substring(0, 1).toUpperCase() + m[0].substring(1);
56 | String skip(String s) => "";
57 | return text.splitMapJoin(RegExp(r'[a-zA-Z0-9]+'),
58 | onMatch: capitalize, onNonMatch: skip);
59 | }
60 |
61 | String camelCaseFirstLower(String text) {
62 | final camelCaseText = camelCase(text);
63 | final firstChar = camelCaseText.substring(0, 1).toLowerCase();
64 | final rest = camelCaseText.substring(1);
65 | return '$firstChar$rest';
66 | }
67 |
68 | decodeJSON(String rawJson) {
69 | return Convert.json.decode(rawJson);
70 | }
71 |
72 | WithWarning