Skip to content

Commit a5f78cd

Browse files
committed
Add JsonObject class and serializer.
1 parent 1d679f8 commit a5f78cd

19 files changed

+959
-3
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
## 0.5.6
44

55
- Add serializer for "DateTime" fields.
6+
- Add JsonObject class and serializer.
7+
- Add convenience methods Seralizers.serializeWith and deserializeWith.
8+
- Add example for using StandardJsonPlugin.
69

710
## 0.5.5
811

built_value/lib/json_object.dart

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Copyright (c) 2017, Google Inc. Please see the AUTHORS file for details.
2+
// All rights reserved. Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
import 'package:collection/collection.dart';
6+
7+
/// A JSON value.
8+
///
9+
/// This class is suitable for use in built_value fields. When serialized it
10+
/// maps directly onto JSON values.
11+
///
12+
/// Deep operator== and hashCode are provided, meaning the contents of a
13+
/// List or Map is used for equality and hashing.
14+
///
15+
/// List and Map classes are wrapped in [UnmodifiableListView] and
16+
/// [UnmodifiableMapView] so they won't be modifiable via this object. You
17+
/// must ensure that no updates are made via the original reference, as a
18+
/// copy is not made.
19+
abstract class JsonObject {
20+
/// The value, which may be a bool, a List, a Map, a num or a String.
21+
Object get value;
22+
23+
/// Whether the value is a [bool].
24+
bool get isBool => false;
25+
26+
/// The value as a [bool], or throw if not.
27+
bool get asBool => throw new StateError('Not a bool.');
28+
29+
/// Whether the value is a [List].
30+
bool get isList => false;
31+
32+
/// The value as a [List], or throw if not.
33+
List get asList => throw new StateError('Not a List.');
34+
35+
/// Whether the value is a [Map].
36+
bool get isMap => false;
37+
38+
/// The value as a [Map], or throw if not.
39+
Map get asMap => throw new StateError('Not a Map.');
40+
41+
/// Whether the value is a [num].
42+
bool get isNum => false;
43+
44+
/// The value as a [num], or throw if not.
45+
num get asNum => throw new StateError('Not a num.');
46+
47+
/// Whether the value is a [String].
48+
bool get isString => false;
49+
50+
/// The value as a [String], or throw if not.
51+
String get asString => throw new StateError('Not a String.');
52+
53+
/// Instantiates with [value], which must be a bool, a List, a Map, a num
54+
/// or a String. Otherwise, an [ArgumentError] is thrown.
55+
factory JsonObject(Object value) {
56+
if (value is num) {
57+
return new NumJsonObject(value);
58+
} else if (value is String) {
59+
return new StringJsonObject(value);
60+
} else if (value is bool) {
61+
return new BoolJsonObject(value);
62+
} else if (value is List<Object>) {
63+
return new ListJsonObject(value);
64+
} else if (value is Map<String, Object>) {
65+
return new MapJsonObject(value);
66+
} else {
67+
throw new ArgumentError.value(value, 'value',
68+
'Must be bool, List<Object>, Map<String, Object>, num or String.');
69+
}
70+
}
71+
72+
JsonObject._();
73+
74+
@override
75+
String toString() {
76+
return value.toString();
77+
}
78+
}
79+
80+
/// A [JsonObject] holding a bool.
81+
class BoolJsonObject extends JsonObject {
82+
@override
83+
final bool value;
84+
85+
BoolJsonObject(this.value) : super._();
86+
87+
@override
88+
bool get isBool => true;
89+
90+
@override
91+
bool get asBool => value;
92+
93+
@override
94+
bool operator ==(dynamic other) {
95+
if (identical(other, this)) return true;
96+
if (other is! BoolJsonObject) return false;
97+
return value == other.value;
98+
}
99+
100+
@override
101+
int get hashCode => value.hashCode;
102+
}
103+
104+
/// A [JsonObject] holding a List.
105+
class ListJsonObject extends JsonObject {
106+
@override
107+
final List<Object> value;
108+
109+
ListJsonObject(List<Object> value)
110+
: this.value = new UnmodifiableListView<Object>(value),
111+
super._();
112+
113+
@override
114+
bool get isList => true;
115+
116+
@override
117+
List<Object> get asList => value;
118+
119+
@override
120+
bool operator ==(dynamic other) {
121+
if (identical(other, this)) return true;
122+
if (other is! ListJsonObject) return false;
123+
return const DeepCollectionEquality().equals(value, other.value);
124+
}
125+
126+
@override
127+
int get hashCode => const DeepCollectionEquality().hash(value);
128+
}
129+
130+
/// A [JsonObject] holding a Map.
131+
class MapJsonObject extends JsonObject {
132+
@override
133+
final Map<String, Object> value;
134+
135+
MapJsonObject(Map<String, Object> value)
136+
: this.value = new UnmodifiableMapView(value),
137+
super._();
138+
139+
@override
140+
bool get isMap => true;
141+
142+
@override
143+
Map<String, Object> get asMap => value;
144+
145+
@override
146+
bool operator ==(dynamic other) {
147+
if (identical(other, this)) return true;
148+
if (other is! MapJsonObject) return false;
149+
return const DeepCollectionEquality().equals(value, other.value);
150+
}
151+
152+
@override
153+
int get hashCode => const DeepCollectionEquality().hash(value);
154+
}
155+
156+
/// A [JsonObject] holding a num.
157+
class NumJsonObject extends JsonObject {
158+
@override
159+
final num value;
160+
161+
NumJsonObject(this.value) : super._();
162+
163+
@override
164+
bool get isNum => true;
165+
166+
@override
167+
num get asNum => value;
168+
169+
@override
170+
bool operator ==(dynamic other) {
171+
if (identical(other, this)) return true;
172+
if (other is! NumJsonObject) return false;
173+
return value == other.value;
174+
}
175+
176+
@override
177+
int get hashCode => value.hashCode;
178+
}
179+
180+
/// A [JsonObject] holding a String.
181+
class StringJsonObject extends JsonObject {
182+
@override
183+
final String value;
184+
185+
StringJsonObject(this.value) : super._();
186+
187+
@override
188+
bool get isString => true;
189+
190+
@override
191+
String get asString => value;
192+
193+
@override
194+
bool operator ==(dynamic other) {
195+
if (identical(other, this)) return true;
196+
if (other is! StringJsonObject) return false;
197+
return value == other.value;
198+
}
199+
200+
@override
201+
int get hashCode => value.hashCode;
202+
}

built_value/lib/serializer.dart

+12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:built_collection/built_collection.dart';
66
import 'package:built_value/src/date_time_serializer.dart';
7+
import 'package:built_value/src/json_object_serializer.dart';
78
import 'package:built_value/src/num_serializer.dart';
89
import 'package:quiver/core.dart';
910

@@ -36,6 +37,7 @@ abstract class Serializers {
3637
..add(new DateTimeSerializer())
3738
..add(new DoubleSerializer())
3839
..add(new IntSerializer())
40+
..add(new JsonObjectSerializer())
3941
..add(new NumSerializer())
4042
..add(new StringSerializer())
4143
..addBuilderFactory(
@@ -72,6 +74,11 @@ abstract class Serializers {
7274
Object serialize(Object object,
7375
{FullType specifiedType: FullType.unspecified});
7476

77+
/// Convenience method for when you know the type you're serializing.
78+
/// Specify the type by specifying its [Serializer] class. Equivalent to
79+
/// calling [serialize] with a `specifiedType`.
80+
Object serializeWith<T>(Serializer<T> serializer, T object);
81+
7582
/// Deserializes [serialized].
7683
///
7784
/// A [Serializer] must have been provided for every type the object uses.
@@ -81,6 +88,11 @@ abstract class Serializers {
8188
Object deserialize(Object serialized,
8289
{FullType specifiedType: FullType.unspecified});
8390

91+
/// Convenience method for when you know the type you're deserializing.
92+
/// Specify the type by specifying its [Serializer] class. Equivalent to
93+
/// calling [deserialize] with a `specifiedType`.
94+
T deserializeWith<T>(Serializer<T> serializer, Object serialized);
95+
8496
/// Creates a new builder for the type represented by [fullType].
8597
///
8698
/// For example, if [fullType] is `BuiltList<int, String>`, returns a

built_value/lib/src/built_json_serializers.dart

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import 'package:built_collection/built_collection.dart';
77
import 'package:built_value/serializer.dart';
8+
import 'package:built_value/serializer.dart';
89

910
/// Default implementation of [Serializers].
1011
class BuiltJsonSerializers implements Serializers {
@@ -27,6 +28,18 @@ class BuiltJsonSerializers implements Serializers {
2728
BuiltJsonSerializers._(this._typeToSerializer, this._wireNameToSerializer,
2829
this._typeNameToSerializer, this._builderFactories, this._plugins);
2930

31+
@override
32+
T deserializeWith<T>(Serializer<T> serializer, Object serialized) {
33+
return deserialize(serialized,
34+
specifiedType: new FullType(serializer.types.first)) as T;
35+
}
36+
37+
@override
38+
Object serializeWith<T>(Serializer<T> serializer, T object) {
39+
return serialize(object,
40+
specifiedType: new FullType(serializer.types.first));
41+
}
42+
3043
@override
3144
Object serialize(Object object,
3245
{FullType specifiedType: FullType.unspecified}) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2017, Google Inc. Please see the AUTHORS file for details.
2+
// All rights reserved. Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
import 'package:built_collection/built_collection.dart';
6+
import 'package:built_value/json_object.dart';
7+
import 'package:built_value/serializer.dart';
8+
9+
class JsonObjectSerializer implements PrimitiveSerializer<JsonObject> {
10+
final bool structured = false;
11+
@override
12+
final Iterable<Type> types = new BuiltList<Type>([
13+
JsonObject,
14+
BoolJsonObject,
15+
ListJsonObject,
16+
MapJsonObject,
17+
NumJsonObject,
18+
StringJsonObject,
19+
]);
20+
@override
21+
final String wireName = 'JsonObject';
22+
23+
@override
24+
Object serialize(Serializers serializers, JsonObject jsonObject,
25+
{FullType specifiedType: FullType.unspecified}) {
26+
return jsonObject.value;
27+
}
28+
29+
@override
30+
JsonObject deserialize(Serializers serializers, Object serialized,
31+
{FullType specifiedType: FullType.unspecified}) {
32+
return new JsonObject(serialized);
33+
}
34+
}

built_value/lib/standard_json_plugin.dart

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:built_collection/built_collection.dart';
2+
import 'package:built_value/json_object.dart';
23
import 'package:built_value/serializer.dart';
34
import 'dart:convert' show JSON;
45

@@ -25,14 +26,16 @@ class StandardJsonPlugin implements SerializerPlugin {
2526

2627
@override
2728
Object afterSerialize(Object object, FullType specifiedType) {
28-
return object is List && specifiedType.root != BuiltList
29+
return object is List &&
30+
specifiedType.root != BuiltList &&
31+
specifiedType.root != JsonObject
2932
? _toMap(object, _alreadyHasStringKeys(specifiedType))
3033
: object;
3134
}
3235

3336
@override
3437
Object beforeDeserialize(Object object, FullType specifiedType) {
35-
return object is Map
38+
return object is Map && specifiedType.root != JsonObject
3639
? _toList(object, _alreadyHasStringKeys(specifiedType))
3740
: object;
3841
}

0 commit comments

Comments
 (0)