validateJson function
void
validateJson(
- dynamic instance,
- Map<String, dynamic> schema, {
- String at = r'$',
})
Implementation
void validateJson(
dynamic instance,
Map<String, dynamic> schema, {
String at = r'$',
}) {
bool isType(dynamic v, String t) => switch (t) {
'object' => v is Map,
'array' => v is List,
'string' => v is String,
'number' => v is num,
'integer' => v is int,
'boolean' => v is bool,
'null' => v == null,
_ => true, // unknown type keyword => skip
};
// enum
final enums = schema['enum'];
if (enums is List && !enums.contains(instance)) {
throw JsonSchemaValidationException(
'$at: value $instance not in enum $enums',
);
}
// oneOf
final oneOf = schema['oneOf'];
if (oneOf is List) {
var ok = false;
for (final s in oneOf.whereType<Map>()) {
try {
validateJson(instance, s.cast<String, dynamic>(), at: at);
ok = true;
break;
} catch (_) {}
}
if (!ok) {
throw JsonSchemaValidationException(
'$at: value does not match any oneOf schemas',
);
}
return;
}
// anyOf
final anyOf = schema['anyOf'];
if (anyOf is List) {
var ok = false;
for (final s in anyOf.whereType<Map>()) {
try {
validateJson(instance, s.cast<String, dynamic>(), at: at);
ok = true;
break;
} catch (_) {}
}
if (!ok) {
throw JsonSchemaValidationException(
'$at: value does not match any anyOf schemas',
);
}
return;
}
// allOf
final allOf = schema['allOf'];
if (allOf is List) {
for (final s in allOf.whereType<Map>()) {
validateJson(instance, s.cast<String, dynamic>(), at: at);
}
}
// type
final type = schema['type'];
if (type is String) {
if (!isType(instance, type)) {
throw JsonSchemaValidationException(
'$at: expected $type, got ${instance.runtimeType}',
);
}
} else if (type is List) {
final ok = type.whereType<String>().any((t) => isType(instance, t));
if (!ok) {
throw JsonSchemaValidationException(
'$at: expected one of $type, got ${instance.runtimeType}',
);
}
}
// object props
final props = schema['properties'];
if (type == 'object' || props is Map) {
if (instance is! Map) {
throw JsonSchemaValidationException(
'$at: expected object, got ${instance.runtimeType}',
);
}
final properties = (props is Map)
? props.cast<String, dynamic>()
: const <String, dynamic>{};
final required =
(schema['required'] as List?)?.whereType<String>().toList() ??
const <String>[];
for (final k in required) {
if (!instance.containsKey(k)) {
throw JsonSchemaValidationException(
'$at: missing required property "$k"',
);
}
}
final additional = schema['additionalProperties'];
for (final entry in instance.entries) {
final k = entry.key;
final v = entry.value;
final sub = properties[k];
if (sub is Map) {
validateJson(v, sub.cast<String, dynamic>(), at: '$at.$k');
} else if (additional == false) {
throw JsonSchemaValidationException(
'$at: additional property "$k" not allowed',
);
} else if (additional is Map) {
validateJson(v, additional.cast<String, dynamic>(), at: '$at.$k');
}
}
}
// array items
final items = schema['items'];
if (type == 'array' || items != null) {
if (instance is! List) {
throw JsonSchemaValidationException(
'$at: expected array, got ${instance.runtimeType}',
);
}
if (items is Map) {
for (var i = 0; i < instance.length; i++) {
validateJson(instance[i], items.cast<String, dynamic>(), at: '$at[$i]');
}
}
}
}