platform/packages/json_god/lib/src/reflection.dart

192 lines
6 KiB
Dart
Raw Normal View History

2021-03-07 15:56:09 +00:00
library json_god.reflection;
import 'dart:mirrors';
import 'package:json_god/json_god.dart';
const Symbol hashCodeSymbol = #hashCode;
const Symbol runtimeTypeSymbol = #runtimeType;
typedef Serializer(value);
2021-04-10 12:42:55 +00:00
typedef Deserializer(value, {Type? outputType});
2021-03-07 15:56:09 +00:00
List<Symbol> _findGetters(ClassMirror classMirror) {
List<Symbol> result = [];
classMirror.instanceMembers
.forEach((Symbol symbol, MethodMirror methodMirror) {
if (methodMirror.isGetter &&
symbol != hashCodeSymbol &&
symbol != runtimeTypeSymbol) {
logger.info("Found getter on instance: $symbol");
result.add(symbol);
}
});
return result;
}
serialize(value, Serializer serializer, [@deprecated bool debug = false]) {
logger.info("Serializing this value via reflection: $value");
Map result = {};
InstanceMirror instanceMirror = reflect(value);
ClassMirror classMirror = instanceMirror.type;
// Check for toJson
for (Symbol symbol in classMirror.instanceMembers.keys) {
if (symbol == #toJson) {
logger.info("Running toJson...");
var result = instanceMirror.invoke(symbol, []).reflectee;
logger.info("Result of serialization via reflection: $result");
return result;
}
}
for (Symbol symbol in _findGetters(classMirror)) {
String name = MirrorSystem.getName(symbol);
var valueForSymbol = instanceMirror.getField(symbol).reflectee;
try {
result[name] = serializer(valueForSymbol);
logger.info("Set $name to $valueForSymbol");
} catch (e, st) {
logger.severe("Could not set $name to $valueForSymbol", e, st);
}
}
logger.info("Result of serialization via reflection: $result");
return result;
}
deserialize(value, Type outputType, Deserializer deserializer,
[@deprecated bool debug = false]) {
logger.info("About to deserialize $value to a $outputType");
try {
if (value is List) {
List<TypeMirror> typeArguments = reflectType(outputType).typeArguments;
Iterable it;
if (typeArguments.isEmpty) {
it = value.map(deserializer);
} else {
it = value.map((item) =>
deserializer(item, outputType: typeArguments[0].reflectedType));
}
if (typeArguments.isEmpty) return it.toList();
logger.info('Casting list elements to ${typeArguments[0]
.reflectedType} via List.from');
var mirror = reflectType(List, [typeArguments[0].reflectedType]);
if (mirror is ClassMirror) {
var output = mirror.newInstance(#from, [it]).reflectee;
logger.info('Casted list type: ${output.runtimeType}');
return output;
} else {
throw new ArgumentError(
'${typeArguments[0].reflectedType} is not a class.');
}
} else if (value is Map)
return _deserializeFromJsonByReflection(value, deserializer, outputType);
else
return deserializer(value);
} catch (e, st) {
logger.severe('Deserialization failed.', e, st);
rethrow;
}
}
/// Uses mirrors to deserialize an object.
_deserializeFromJsonByReflection(
data, Deserializer deserializer, Type outputType,
[@deprecated bool debug = false]) {
// Check for fromJson
var typeMirror = reflectType(outputType);
if (typeMirror is! ClassMirror) {
throw new ArgumentError('$outputType is not a class.');
}
2021-04-10 12:42:55 +00:00
var type = typeMirror;
2021-03-07 15:56:09 +00:00
var fromJson =
new Symbol('${MirrorSystem.getName(type.simpleName)}.fromJson');
for (Symbol symbol in type.declarations.keys) {
if (symbol == fromJson) {
var decl = type.declarations[symbol];
if (decl is MethodMirror && decl.isConstructor) {
logger.info("Running fromJson...");
var result = type.newInstance(#fromJson, [data]).reflectee;
logger.info("Result of deserialization via reflection: $result");
return result;
}
}
}
ClassMirror classMirror = type;
InstanceMirror instanceMirror = classMirror.newInstance(new Symbol(""), []);
if (classMirror.isSubclassOf(reflectClass(Map))) {
var typeArguments = classMirror.typeArguments;
if (typeArguments.isEmpty ||
classMirror.typeArguments
.every((t) => t == currentMirrorSystem().dynamicType)) {
return data;
} else {
var mapType =
reflectType(Map, typeArguments.map((t) => t.reflectedType).toList())
as ClassMirror;
logger.info('Casting this map $data to Map of [$typeArguments]');
var output = mapType.newInstance(new Symbol(''), []).reflectee;
for (var key in data.keys) {
output[key] = data[key];
}
logger.info('Output: $output of type ${output.runtimeType}');
return output;
}
} else {
data.keys.forEach((key) {
try {
logger.info("Now deserializing value for $key");
logger.info("data[\"$key\"] = ${data[key]}");
var deserializedValue = deserializer(data[key]);
logger.info("I want to set $key to the following ${deserializedValue
.runtimeType}: $deserializedValue");
// Get target type of getter
Symbol searchSymbol = new Symbol(key.toString());
Symbol symbolForGetter = classMirror.instanceMembers.keys
.firstWhere((x) => x == searchSymbol);
Type requiredType = classMirror
2021-04-10 12:42:55 +00:00
.instanceMembers[symbolForGetter]!.returnType.reflectedType;
2021-03-07 15:56:09 +00:00
if (data[key].runtimeType != requiredType) {
logger.info("Currently, $key is a ${data[key].runtimeType}.");
logger.info("However, $key must be a $requiredType.");
deserializedValue =
deserializer(deserializedValue, outputType: requiredType);
}
logger.info(
"Final deserialized value for $key: $deserializedValue <${deserializedValue
.runtimeType}>");
instanceMirror.setField(new Symbol(key.toString()), deserializedValue);
logger.info("Success! $key has been set to $deserializedValue");
} catch (e, st) {
logger.severe('Could not set value for field $key.', e, st);
}
});
}
return instanceMirror.reflectee;
}