@@ -22,52 +22,98 @@ class JsonReader {
22
22
T get<T extends Object?>(String key) {
23
23
final value = json[key];
24
24
if (value is T) return value;
25
- final pathString = _jsonPathToString([key]);
26
- if (value == null) {
27
- throw FormatException("No value was provided for '$pathString'.");
28
- }
29
25
throwFormatException(value, T, [key]);
30
26
}
31
27
28
+ List<String> validate<T extends Object?>(String key) {
29
+ final value = json[key];
30
+ if (value is T) return [];
31
+ return [
32
+ errorString(value, T, [key]),
33
+ ];
34
+ }
35
+
32
36
List<T> list<T extends Object?>(String key) =>
33
37
_castList<T>(get<List<Object?>>(key), key);
34
38
39
+ List<String> validateList<T extends Object?>(String key) {
40
+ final listErrors = validate<List<Object?>>(key);
41
+ if (listErrors.isNotEmpty) {
42
+ return listErrors;
43
+ }
44
+ return _validateListElements(get<List<Object?>>(key), key);
45
+ }
46
+
35
47
List<T>? optionalList<T extends Object?>(String key) =>
36
48
switch (get<List<Object?>?>(key)?.cast<T>()) {
37
49
null => null,
38
50
final l => _castList<T>(l, key),
39
51
};
40
52
53
+ List<String> validateOptionalList<T extends Object?>(String key) {
54
+ final listErrors = validate<List<Object?>?>(key);
55
+ if (listErrors.isNotEmpty) {
56
+ return listErrors;
57
+ }
58
+ final list = get<List<Object?>?>(key);
59
+ if (list == null) {
60
+ return [];
61
+ }
62
+ return _validateListElements(list, key);
63
+ }
64
+
41
65
/// [List.cast] but with [FormatException]s.
42
66
List<T> _castList<T extends Object?>(List<Object?> list, String key) {
43
- var index = 0;
44
- for (final value in list) {
67
+ for (final (index, value) in list.indexed) {
45
68
if (value is! T) {
46
69
throwFormatException(value, T, [key, index]);
47
70
}
48
- index++;
49
71
}
50
72
return list.cast();
51
73
}
52
74
53
- List<T>? optionalListParsed<T extends Object?>(
75
+ List<String> _validateListElements<T extends Object?>(
76
+ List<Object?> list,
54
77
String key,
55
- T Function(Object?) elementParser,
56
78
) {
57
- final jsonValue = optionalList(key);
58
- if (jsonValue == null) return null;
59
- return [for (final element in jsonValue) elementParser(element)];
79
+ final result = <String>[];
80
+ for (final (index, value) in list.indexed) {
81
+ if (value is! T) {
82
+ result.add(errorString(value, T, [key, index]));
83
+ }
84
+ }
85
+ return result;
60
86
}
61
87
62
88
Map<String, T> map$<T extends Object?>(String key) =>
63
89
_castMap<T>(get<Map<String, Object?>>(key), key);
64
90
91
+ List<String> validateMap<T extends Object?>(String key) {
92
+ final mapErrors = validate<Map<String, Object?>>(key);
93
+ if (mapErrors.isNotEmpty) {
94
+ return mapErrors;
95
+ }
96
+ return _validateMapElements<T>(get<Map<String, Object?>>(key), key);
97
+ }
98
+
65
99
Map<String, T>? optionalMap<T extends Object?>(String key) =>
66
100
switch (get<Map<String, Object?>?>(key)) {
67
101
null => null,
68
102
final m => _castMap<T>(m, key),
69
103
};
70
104
105
+ List<String> validateOptionalMap<T extends Object?>(String key) {
106
+ final mapErrors = validate<Map<String, Object?>?>(key);
107
+ if (mapErrors.isNotEmpty) {
108
+ return mapErrors;
109
+ }
110
+ final map = get<Map<String, Object?>?>(key);
111
+ if (map == null) {
112
+ return [];
113
+ }
114
+ return _validateMapElements<T>(map, key);
115
+ }
116
+
71
117
/// [Map.cast] but with [FormatException]s.
72
118
Map<String, T> _castMap<T extends Object?>(
73
119
Map<String, Object?> map_,
@@ -81,18 +127,40 @@ class JsonReader {
81
127
return map_.cast();
82
128
}
83
129
130
+ List<String> _validateMapElements<T extends Object?>(
131
+ Map<String, Object?> map_,
132
+ String parentKey,
133
+ ) {
134
+ final result = <String>[];
135
+ for (final MapEntry(:key, :value) in map_.entries) {
136
+ if (value is! T) {
137
+ result.add(errorString(value, T, [parentKey, key]));
138
+ }
139
+ }
140
+ return result;
141
+ }
142
+
84
143
List<String>? optionalStringList(String key) => optionalList<String>(key);
85
144
145
+ List<String> validateOptionalStringList(String key) =>
146
+ validateOptionalList<String>(key);
147
+
86
148
List<String> stringList(String key) => list<String>(key);
87
149
150
+ List<String> validateStringList(String key) => validateList<String>(key);
151
+
88
152
Uri path$(String key) => _fileSystemPathToUri(get<String>(key));
89
153
154
+ List<String> validatePath(String key) => validate<String>(key);
155
+
90
156
Uri? optionalPath(String key) {
91
157
final value = get<String?>(key);
92
158
if (value == null) return null;
93
159
return _fileSystemPathToUri(value);
94
160
}
95
161
162
+ List<String> validateOptionalPath(String key) => validate<String?>(key);
163
+
96
164
List<Uri>? optionalPathList(String key) {
97
165
final strings = optionalStringList(key);
98
166
if (strings == null) {
@@ -101,6 +169,9 @@ class JsonReader {
101
169
return [for (final string in strings) _fileSystemPathToUri(string)];
102
170
}
103
171
172
+ List<String> validateOptionalPathList(String key) =>
173
+ validateOptionalStringList(key);
174
+
104
175
static Uri _fileSystemPathToUri(String path) {
105
176
if (path.endsWith(Platform.pathSeparator)) {
106
177
return Uri.directory(path);
@@ -115,12 +186,22 @@ class JsonReader {
115
186
Object? value,
116
187
Type expectedType,
117
188
List<Object> pathExtension,
189
+ ) {
190
+ throw FormatException(errorString(value, expectedType, pathExtension));
191
+ }
192
+
193
+ String errorString(
194
+ Object? value,
195
+ Type expectedType,
196
+ List<Object> pathExtension,
118
197
) {
119
198
final pathString = _jsonPathToString(pathExtension);
120
- throw FormatException(
121
- "Unexpected value '$value' (${value.runtimeType}) for '$pathString'. "
122
- 'Expected a $expectedType.',
123
- );
199
+ if (value == null) {
200
+ return "No value was provided for '$pathString'."
201
+ ' Expected a $expectedType.';
202
+ }
203
+ return "Unexpected value '$value' (${value.runtimeType}) for '$pathString'."
204
+ ' Expected a $expectedType.';
124
205
}
125
206
}
126
207
0 commit comments