Open moseskarunia opened 3 years ago
Why you're not using Hive.box(..).values
?
Future<List<Map<String, dynamic>>> readFromHive() async {
final box = await Hive.openBox('storageName');
final result = box.values.cast<Map<String, dynamic>>();
return result;
/// Let's say the result is:
/// ```
/// [ { name: 'John Doe', email: 'johndoe@gmail.com'} ]
/// ```
}
box.values.cast<Map<String, dynamic>>()
Hi, thanks for the response @TheMisir , it worked. Thanks.
@TheMisir turns out crashes on the real usage (succeed on test)
try {
final box = await hive.openBox(storageName);
return box.values.cast<Map<String, dynamic>>().toList(); // crashes here
catch (e) {
throw CleanException(name: 'UNEXPECTED_ERROR', group: 'hive'); // Got here
}
Error
_CastError (type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast)
However, this works
final box = await hive.openBox(storageName);
final result = box.toMap().map(
(k, e) => MapEntry(
k.toString(),
Map<String, dynamic>.from(e),
),
);
return result.values.toList();
@TheMisir turns out crashes on the real usage (succeed on test)
try { final box = await hive.openBox(storageName); return box.values.cast<Map<String, dynamic>>().toList(); // crashes here catch (e) { throw CleanException(name: 'UNEXPECTED_ERROR', group: 'hive'); // Got here }
Error
_CastError (type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast)
However, this works
final box = await hive.openBox(storageName); final result = box.toMap().map( (k, e) => MapEntry( k.toString(), Map<String, dynamic>.from(e), ), ); return result.values.toList();
still got error type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast
my code
Stream<List<Map<String,` dynamic>>> watchList(T dataSource) async* {
final _box = await _openBox(dataSource);
final result = _box.toMap().map((key, value) =>
MapEntry(key.toString(), Map<String, dynamic>.from(value)));
yield* _box.watch().map((event) => result.values.toList());
}
@Harrys76
I'm currently using Hive with a package named freezed and it seems to work seamlessly.
@Harrys76 due to dart's type system limitations, Hive can not persist generic types. Instead of Map<String, dynamic> hive returns Map<dynamic, dynamic> so you have to cast it manually.
@moseskarunia @TheMisir could you help me to solve it?
I sometimes got 'subtype of type' error when run this code
Stream<List<Map<String,` dynamic>>> watchList(T dataSource) async* {
final _box = await _openBox(dataSource);
yield* _box.watch().map((event) => _box.values.toList());
}
the error occur in _box.values.toList()
@moseskarunia @TheMisir could you help me to solve it?
I sometimes got 'subtype of type' error when run this code
Stream<List<Map<String,` dynamic>>> watchList(T dataSource) async* { final _box = await _openBox(dataSource); yield* _box.watch().map((event) => _box.values.toList()); }
the error occur in
_box.values.toList()
Parse the map object with freezed first to produce a new sane list of dart object. Don't access it directly.
@moseskarunia
i tried two things:
make a freezed class (BoxRecords) and parse the value from _box.values
like this:
Stream<List<BoxRecords>> watchList(T dataSource) async* {
final _box = await _openBox(dataSource);
yield* _box.watch().map((event) => _box.values.map((value) => BoxRecords(records: value)).toList());
}
Stream<List<BoxRecords>> watchList(T dataSource) async* {
final _box = await _openBox(dataSource);
yield* _box.watch().map((event) => _box.values.toList());
}
still got this error:
/flutter (27820): type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'BoxRecords' in type cast
I/flutter (27820):
I/flutter (27820): #0 Keystore.getValues.<anonymous closure> (package:hive/src/box/keystore.dart:122:45)
I/flutter (27820): #1 MappedIterator.moveNext (dart:_internal/iterable.dart:392:20)
I/flutter (27820): #2 new List.from (dart:core-patch/array_patch.dart:50:19)
I/flutter (27820): #3 new List.of (dart:core-patch/array_patch.dart:68:17)
I/flutter (27820): #4 Iterable.toList (dart:core/iterable.dart:404:12)
I/flutter (27820): #5 DataSourceRecordBox.watchList.<anonymous closure> (package:jojotask/src/repositories/cache/hive/box/form_record_box.dart:35:52)
I/flutter (27820): #6 _MapStream._handleData (dart:async/stream_pipe.dart:219:31)
I/flutter (27820): #7 _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:157:13)
I/flutter (27820): #8 _rootRunUnary (dart:async/zone.dart:1198:47)
I/flutter (27820): #9 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
I/flutter (27820): #10 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
I/flutter (27820): #11 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:357:11)
I/flutter (27820): #12 _DelayedData.perform (dart:async/stream_impl.dart:611:14)
I/flutter (27820): #13 _StreamImplEvents.handleNext (dart:async/stream_impl
@Harrys76 you have to parse it using the freezed class's fromJson function (annotate your factory using @JsonSerializable, with at least both of anyMap and checked set to true) and then add factory from json to your freezed class, calling generated fromJson code. That's what makes it work in my case
thank you @moseskarunia for pointing out a solution. Parsing with fromJson()
from freezed class is works. I already try to reproduce the error by kill app and open it again, the error gone.
so in my case, I'm using BoxRecords
class as TypeAdapter and this is my BoxRecordsAdapter
class:
@freezed
abstract class BoxRecords with _$BoxRecords {
static const fromJsonFactory = _$BoxRecordsFromJson;
@JsonSerializable(anyMap: true, checked: true)
factory BoxRecords({
Map<String, dynamic> record,
}) = _BoxRecords;
factory BoxRecords.fromJson(Map<String, dynamic> json) =>
_$BoxRecordsFromJson(json);
}
class BoxRecordsAdapter extends TypeAdapter<BoxRecords> {
@override
final typeId = 0;
@override
BoxRecords read(BinaryReader reader) {
final result = Map<String, dynamic>.from(reader.read());
return BoxRecords.fromJson({'records': result});
}
@override
void write(BinaryWriter writer, BoxRecords obj) {
writer.write<Map<String, dynamic>>(obj.records);
}
}
@TheMisir with all due respect, solving it using other library doesn't solve the actual problem within the library itself. Therefore, I'm reopening it.
If I need to cast it myself with just the built-in cast, then sure it's solved. But it isn't possible without copying freezed / json_serializable parsing logic (which is cumbersome to do without code generation).
I had similar problems, we have a LOT of DTO Classes, many of them are deeply nested and also Unions generated with freezed.
What works for me is that I use the .to/fromJson() of the classes wrap them with a Class that has a generated TypeAdapter.
Directly using the Map<String,dynamic> of .to/fromJson() with a Box<Map<String, dynamic>>
or Box<Map<String, dynamic>?>
produced the "not a subtype errors", AFTER restart. Also the suggested cast() etc. did not work for me.
This works for me:
Box<DtoJsonHiveWrapper> _box = await Hive.openBox(
"box",
encryptionCipher: HiveAesCipher(encryptionKey),
);
import 'package:hive_flutter/hive_flutter.dart';
part 'dto_hive_wrapper.g.dart';
@HiveType(typeId: 7)
class DtoJsonHiveWrapper {
@HiveField(0)
final Map<String, dynamic> dtoJson;
DtoJsonHiveWrapper({required this.dtoJson});
}
[✓] Flutter (Channel beta, 2.5.0-5.2.pre, on macOS 11.5.1 20G80 darwin-arm, locale en-DE)
• Flutter version 2.5.0-5.2.pre at /Users/r0/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 19c61fed0d (12 days ago), 2021-08-18 17:10:31 -0700
• Engine revision 7a4c4505f6
• Dart version 2.14.0 (build 2.14.0-377.7.beta)
@rokk4 This solution doesn't work for nested classes in my case.
I have just setup Hive, I'm having the same issue here.
I'm storing Map<String, dynamic> in the box, but I've got a cast exception when reading values
.
Any idea how to solve this, after more than a year ?
EDIT :
I have the same issue with toMap()
alone
type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast
BoxImpl.toMap (package:hive/src/box/box_impl.dart:102:36)
For me I can't found any solution rather than doing quick fix jsonEncode then decode it back. This worked for me even I have nested map inside map, But performance is my concern.
Map<String, dynamic> _parser(dynamic hiveMap){
final jsonString = jsonEncode(hiveMap);
return jsonDecode(jsonString);
}
Future<AppSetting?> getAppSetting() async {
try {
final result = await _box.get(_appSetting);
if (result == null) return null;
return AppSetting.fromJson(_parser(result));
} on Exception catch (e) {
debugPrint(e.toString());
return null;
}
}
I've added a custom type adapter that worked for me, in nested Map structures. I generated a adapter and just extended it and overrode the read method for this:
@override
FooBar read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return FooBar(
fields[0] as String,
_castInternalMap(fields[1]), // changed just this
);
}
dynamic _castInternalMap(dynamic value) {
if (value != null) {
if (value is Map) {
return value.cast().map(
(key, value) => MapEntry(key, _castInternalMap(value)),
);
}
if (value is List) {
return value.map(_castInternalMap).toList();
}
}
return value;
}
The _castInternalMap
will check the value and handle it recursively
Version
Hello guys, so I'm facing a difficulty understanding what type of Map does Hive returns. Below is my code:
Code sample
Here's the weird things:
Map<String,dynamic>
why doesn't it crash the moment it exits thereadFromHive()
?Map<String,dynamic>
why doesprint(results[0]['name']);
prints null, whileprint(results[0])
prints correct result?@JsonSerializable
package. And I always need to passanyMap: true
in order for thefromJson
to work properly. I never feels to passanyMap: true
if I don't deal with Hive.results[0]
, the type truly saysMap<String,dynamic>
So, the question is like the title, why does it feels like Hive Map<String,dynamic> is not really acts like a Map<String,dynamic>? And how to make it a "true" `Map<String,dynamic>? Or is this intended? Maybe I misunderstand how Hive works?
Thanks
P.S. I can't use Hive's built in json parser since in a clean architecture, the entity and data source should not even care which local storage library I use.