2021-05-14 12:24:45 +00:00
|
|
|
library angel3_json_god.reflection;
|
2021-03-07 15:56:09 +00:00
|
|
|
|
|
|
|
import 'dart:mirrors';
|
2021-05-14 12:24:45 +00:00
|
|
|
import '../angel3_json_god.dart';
|
2021-03-07 15:56:09 +00:00
|
|
|
|
|
|
|
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();
|
2021-05-14 12:24:45 +00:00
|
|
|
logger.info(
|
|
|
|
'Casting list elements to ${typeArguments[0].reflectedType} via List.from');
|
2021-03-07 15:56:09 +00:00
|
|
|
|
|
|
|
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 {
|
2021-05-14 12:24:45 +00:00
|
|
|
throw ArgumentError(
|
2021-03-07 15:56:09 +00:00
|
|
|
'${typeArguments[0].reflectedType} is not a class.');
|
|
|
|
}
|
2021-05-15 01:31:17 +00:00
|
|
|
} else if (value is Map) {
|
2021-03-07 15:56:09 +00:00
|
|
|
return _deserializeFromJsonByReflection(value, deserializer, outputType);
|
2021-05-15 01:31:17 +00:00
|
|
|
} else {
|
2021-03-07 15:56:09 +00:00
|
|
|
return deserializer(value);
|
2021-05-15 01:31:17 +00:00
|
|
|
}
|
2021-03-07 15:56:09 +00:00
|
|
|
} catch (e, st) {
|
|
|
|
logger.severe('Deserialization failed.', e, st);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Uses mirrors to deserialize an object.
|
|
|
|
_deserializeFromJsonByReflection(
|
2021-05-15 06:46:26 +00:00
|
|
|
data, Deserializer deserializer, Type outputType) {
|
2021-03-07 15:56:09 +00:00
|
|
|
// Check for fromJson
|
|
|
|
var typeMirror = reflectType(outputType);
|
|
|
|
|
|
|
|
if (typeMirror is! ClassMirror) {
|
2021-05-14 12:24:45 +00:00
|
|
|
throw ArgumentError('$outputType is not a class.');
|
2021-03-07 15:56:09 +00:00
|
|
|
}
|
|
|
|
|
2021-04-10 12:42:55 +00:00
|
|
|
var type = typeMirror;
|
2021-05-14 12:24:45 +00:00
|
|
|
var fromJson = Symbol('${MirrorSystem.getName(type.simpleName)}.fromJson');
|
2021-03-07 15:56:09 +00:00
|
|
|
|
|
|
|
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;
|
2021-05-14 12:24:45 +00:00
|
|
|
InstanceMirror instanceMirror = classMirror.newInstance(Symbol(""), []);
|
2021-03-07 15:56:09 +00:00
|
|
|
|
|
|
|
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]');
|
2021-05-14 12:24:45 +00:00
|
|
|
var output = mapType.newInstance(Symbol(''), []).reflectee;
|
2021-03-07 15:56:09 +00:00
|
|
|
|
|
|
|
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]);
|
|
|
|
|
2021-05-14 12:24:45 +00:00
|
|
|
logger.info(
|
|
|
|
"I want to set $key to the following ${deserializedValue.runtimeType}: $deserializedValue");
|
2021-03-07 15:56:09 +00:00
|
|
|
// Get target type of getter
|
2021-05-14 12:24:45 +00:00
|
|
|
Symbol searchSymbol = Symbol(key.toString());
|
2021-03-07 15:56:09 +00:00
|
|
|
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(
|
2021-05-14 12:24:45 +00:00
|
|
|
"Final deserialized value for $key: $deserializedValue <${deserializedValue.runtimeType}>");
|
|
|
|
instanceMirror.setField(Symbol(key.toString()), deserializedValue);
|
2021-03-07 15:56:09 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|