Skip to content

Commit

Permalink
Support ObjectId and Uuid out-of-the-box
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsenko committed Sep 18, 2023
1 parent 6d56ada commit 9a21a62
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,6 @@
"coroutine": "cpp",
"ranges": "cpp",
"span": "cpp"
}
},
"dart.flutterSdkPath": "/Users/kasper/Projects/flutter/stable"
}
29 changes: 29 additions & 0 deletions ejson/packages/ejson/lib/src/decoding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
//
////////////////////////////////////////////////////////////////////////////////
import 'dart:typed_data';
import 'dart:convert';

import 'package:collection/collection.dart';
import 'package:ejson_annotation/ejson_annotation.dart';
import 'package:objectid/objectid.dart';
import 'package:sane_uuid/uuid.dart';
import 'package:type_plus/type_plus.dart';

import 'types.dart';
Expand All @@ -35,8 +40,10 @@ const commonDecoders = {
double: _decodeDouble,
num: _decodeNum,
int: _decodeInt,
ObjectId: _decodeObjectId,
String: _decodeString,
Symbol: _decodeSymbol,
Uuid: _decodeUuid,
Undefined: _decodeUndefined,
UndefinedOr: _decodeUndefinedOr,
};
Expand All @@ -51,6 +58,8 @@ final decoders = () {
TypePlus.addFactory(<T>(f) => f<Defined<T>>(), superTypes: [undefinedOr]);
TypePlus.addFactory(<T>(f) => f<Undefined<T>>(), superTypes: [undefinedOr]);
TypePlus.add<Key>();
TypePlus.add<ObjectId>();
TypePlus.add<Uuid>();

return CombinedMapView([customDecoders, commonDecoders]);
}();
Expand Down Expand Up @@ -88,6 +97,8 @@ dynamic _decodeAny(EJsonValue ejson) {
{'\$regex': _} => _decodeString(ejson),
{'\$symbol': _} => _decodeSymbol(ejson),
{'\$undefined': _} => _decodeUndefined(ejson),
{'\$oid': _} => _decodeObjectId(ejson),
{'\$binary': {'base64': _, 'subType': '04'}} => _decodeUuid(ejson),
List _ => _decodeArray(ejson),
Map _ => _tryDecodeCustom(ejson) ??
_decodeDocument(ejson), // other maps goes last!!
Expand Down Expand Up @@ -197,6 +208,13 @@ num _decodeNum(EJsonValue ejson) {
};
}

ObjectId _decodeObjectId(EJsonValue ejson) {
return switch (ejson) {
{'\$oid': String s} => ObjectId.fromHexString(s),
_ => raiseInvalidEJson(ejson),
};
}

String _decodeString(EJsonValue ejson) {
return switch (ejson) {
String s => s,
Expand Down Expand Up @@ -225,6 +243,17 @@ UndefinedOr<T> _decodeUndefinedOr<T>(EJsonValue ejson) {
};
}

Uuid _decodeUuid(EJsonValue ejson) =>
Uuid.fromBytes(_decodeBinary(ejson, "04"));

ByteBuffer _decodeBinary(EJsonValue ejson, String subType) {
return switch (ejson) {
{'\$binary': {'base64': String s, 'subType': String t}} when t == subType =>
base64.decode(s).buffer,
_ => raiseInvalidEJson(ejson),
};
}

class InvalidEJson<T> implements Exception {
final Object? value;

Expand Down
28 changes: 28 additions & 0 deletions ejson/packages/ejson/lib/src/encoding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
//
////////////////////////////////////////////////////////////////////////////////
import 'dart:convert';
import 'dart:typed_data';

import 'package:ejson_annotation/ejson_annotation.dart';
import 'package:objectid/objectid.dart';
import 'package:sane_uuid/uuid.dart';
import 'package:type_plus/type_plus.dart';

import 'types.dart';
Expand All @@ -42,9 +47,11 @@ EJsonValue _encodeAny(Object? value) {
Key k => _encodeKey(k),
List l => _encodeArray(l),
Map m => _encodeDocument(m),
ObjectId o => _encodeObjectId(o),
String s => _encodeString(s),
Symbol s => _encodeSymbol(s),
Undefined u => _encodeUndefined(u),
Uuid u => _encodeUuid(u),
_ => _encodeCustom(value),
};
}
Expand Down Expand Up @@ -106,6 +113,17 @@ EJsonValue _encodeSymbol(Symbol value) => {'\$symbol': value.name};

EJsonValue _encodeUndefined(Undefined undefined) => {'\$undefined': 1};

EJsonValue _encodeUuid(Uuid uuid) => _encodeBinary(uuid.bytes, "04");

EJsonValue _encodeBinary(ByteBuffer buffer, String subtype) => {
'\$binary': {
'base64': base64.encode(buffer.asUint8List()),
'subType': subtype
},
};

EJsonValue _encodeObjectId(ObjectId objectId) => {'\$oid': objectId.hexString};

class MissingEncoder implements Exception {
final Object value;

Expand Down Expand Up @@ -160,6 +178,11 @@ extension NullableObjectEJsonEncoderExtension on Object? {
EJsonValue toEJson() => _encodeAny(this);
}

extension ObjectIdEJsonEncoderExtension on ObjectId {
@pragma('vm:prefer-inline')
EJsonValue toEJson() => _encodeObjectId(this);
}

extension StringEJsonEncoderExtension on String {
@pragma('vm:prefer-inline')
EJsonValue toEJson() => _encodeString(this);
Expand All @@ -179,3 +202,8 @@ extension UndefinedEJsonEncoderExtension on Undefined {
@pragma('vm:prefer-inline')
EJsonValue toEJson() => _encodeUndefined(this);
}

extension UuidEJsonEncoderExtension on Uuid {
@pragma('vm:prefer-inline')
EJsonValue toEJson() => _encodeUuid(this);
}
2 changes: 2 additions & 0 deletions ejson/packages/ejson/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ environment:
dependencies:
collection: ^1.17.0
ejson_annotation: ^0.1.0
objectid: ^3.0.0
sane_uuid: ^1.0.0-alpha.5
type_plus: ^2.0.0

dev_dependencies:
Expand Down
11 changes: 11 additions & 0 deletions ejson/packages/ejson/test/ejson_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import 'dart:convert';

import 'package:ejson/ejson.dart';
import 'package:ejson_annotation/ejson_annotation.dart';
import 'package:objectid/objectid.dart';
import 'package:sane_uuid/uuid.dart';
import 'package:test/test.dart';

import 'ejson_serialization_setup.g.dart';
Expand Down Expand Up @@ -148,6 +150,15 @@ void main() {
_testCase(const Defined<int?>(null), null);
_testCase(Defined<int?>(42), {'\$numberLong': 42}, 42);
_testCase(Defined<int?>(null), null);
_testCase(ObjectId.fromValues(1, 2, 3),
{'\$oid': '000000000000000002000003'});
final uuid = Uuid.v4();
_testCase(uuid, {
'\$binary': {
'base64': base64.encode(uuid.bytes.asUint8List()),
'subType': '04'
}
});
// a complex nested generic type
_testCase<Map<String, Map<String, List<num?>?>>>(
{
Expand Down

0 comments on commit 9a21a62

Please sign in to comment.