Based on my experience, Dart does not provide convert an Object
type to a Map
. So, I try create function like toMap()
that manually convert the object to a key-value pair of map. In this post, I try create it dynamic
Overview
For example:
class Icon {
final String name;
final int color;
Icon({required this.name, required this.color});
Map<String, dynamic> toMap() => {
'name': name,
'color': color,
};
}
So, later when you have a Icon
object, I just can call icon.toMap()
.
I do this in most of my entity classes.
In this post, I try create method toMap()
dynamic and so I reuse method toMap()
for in most of my entity classes.
Gen method toMap
for Object
class
The approach that I use in this post is using dart:mirrors
library.
Basic reflection in Dart, with support for introspection and dynamic invocation.
-
Introspection is that subset of reflection by which a running program can examine its own structure. For example, a function that prints out the names of all the members of an arbitrary object.
-
Dynamic invocation refers the ability to evaluate code that has not been literally specified at compile time, such as calling a method whose name is provided as an argument (because it is looked up in a database, or provided interactively by the user).
dart:mirrors
library provide ClassMirror
class which has declarations
property.
declarations
property is a Map<Symbol, DeclarationMirror>
, returns an immutable map of the declarations actually given in the class declaration.
This map includes all regular methods, getters, setters, fields, constructors and type variables actually declared in the class. Both static and instance members are included, but no inherited members are included. The map is keyed by the simple names of the declarations.
A DeclarationMirror
reflects some entity declared in a Dart program.
Implementers: LibraryMirror
, MethodMirror
, TypeMirror
, and VariableMirror
.
Main approach: InstanceMirror
-> ClassMirror
-> DeclarationMirror
-> VariableMirror
(field name and value)
Map<String, dynamic> map = {}
Object -> InstanceMirror -> ClassMirror -> declarations.values -> List<DeclarationMirror>
for v in List<DeclarationMirror>
v is VariableMirror
fieldName
filedValue
map.put(fieldName, filedValue)
Dart Code
import 'dart:mirrors';
import 'dart:convert';
extension ObjectExtensition on Object {
Map<String, dynamic> _toMapWithValidFiledName({
bool Function(String)? validFiledName,
}) {
final InstanceMirror instanceMirror = reflect(this);
final ClassMirror classMirror = instanceMirror.type;
final Map<String, dynamic> map = {};
for (var v in classMirror.declarations.values) {
final String name = MirrorSystem.getName(v.simpleName);
final bool isValid = validFiledName?.call(name) ?? true;
if (v is VariableMirror && isValid) {
final InstanceMirror field = instanceMirror.getField(v.simpleName);
if (field.hasReflectee) {
final childMap = (field.reflectee as Object).toMap();
map.putIfAbsent(
name,
() => childMap.isEmpty ? field.reflectee : childMap,
);
}
}
}
return map;
}
Map<String, dynamic> toMap() {
return _toMapWithValidFiledName(
validFiledName: (_) => true,
);
}
String toJsonString() => json.encode(toMap());
String toJsonStringWithFormat() {
final JsonEncoder encoder = JsonEncoder.withIndent(' ');
return encoder.convert(toMap());
}
Map<String, dynamic> toMapIncludeFiledNames(
List<String>? onlyIncludeFiledNames,
) {
final finalOnlyIncludeFiledNames = onlyIncludeFiledNames ?? [];
return _toMapWithValidFiledName(
validFiledName: (name) =>
finalOnlyIncludeFiledNames.isEmpty ||
finalOnlyIncludeFiledNames.contains(name),
);
}
Map<String, dynamic> toMapExceptFiledNames(
List<String>? exceptFiledNames,
) {
final finalExceptFiledNames = exceptFiledNames ?? [];
return _toMapWithValidFiledName(
validFiledName: (name) =>
finalExceptFiledNames.isEmpty ||
!finalExceptFiledNames.contains(name),
);
}
}
Warning: The dart:mirrors library is unstable and its API might change slightly as a result of user feedback. This library is only supported by the Dart VM and only available on some platforms.
Tutorial
Create class MennuItem
and convert an Object
type to a Map
, so the variables become key/value pairs with method toMap()
Input
class Icon {
final String name;
final int color;
Icon({required this.name, required this.color});
}
class MenuItem {
final String title;
final bool isEnable;
final int index;
final Icon icon;
final List<String> listTrackingCode;
MenuItem({
required this.title,
required this.isEnable,
required this.index,
required this.icon,
required this.listTrackingCode,
});
}
Try
void main(List<String> arguments) {
final MenuItem item = MenuItem(
title: "New tab",
isEnable: true,
index: 0,
icon: Icon(
color: 0xffffff,
name: "icNewTab",
),
listTrackingCode: ["new_tab"],
);
print("toMap()");
print(item.toMap());
print("toMapIncludeFiledNames()");
print(item.toMapIncludeFiledNames(["listTrackingCode", "icon"]));
print("toMapExceptFiledNames()");
print(item.toMapExceptFiledNames(["listTrackingCode", "icon"]));
print("toJsonStringWithFormat()");
print(item.toJsonStringWithFormat());
}
Output
toMap()
{title: New tab, isEnable: true, index: 0, icon: {name: icNewTab, color: 16777215}, listTrackingCode: [new_tab]}
toMapIncludeFiledNames()
{icon: {name: icNewTab, color: 16777215}, listTrackingCode: [new_tab]}
toMapExceptFiledNames()
{title: New tab, isEnable: true, index: 0}
toJsonStringWithFormat()
{
"title": "New tab",
"isEnable": true,
"index": 0,
"icon": {
"name": "icNewTab",
"color": 16777215
},
"listTrackingCode": [
"new_tab"
]
}
More
// import 'dart:math';
print(Point(3, 4).toMap());
// {x: 3, y: 4}
print(Rectangle(1, 2, 3, 4).toMap());
// {left: 1, top: 2, width: 3, height: 4}