Add a document-by-document JSON transformer.
This makes it easy to send JSON over WebSocket connections, for example.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1643843002 .
diff --git a/lib/src/json_document_transformer.dart b/lib/src/json_document_transformer.dart
new file mode 100644
index 0000000..8d8dcce
--- /dev/null
+++ b/lib/src/json_document_transformer.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:async/async.dart';
+
+import '../stream_channel.dart';
+import 'stream_channel_transformer.dart';
+
+/// The canonical instance of [JsonDocumentTransformer].
+final jsonDocument = new JsonDocumentTransformer();
+
+/// A [StreamChannelTransformer] that transforms JSON documents—strings that
+/// contain individual objects encoded as JSON—into decoded Dart objects.
+///
+/// This decodes JSON that's emitted by the transformed channel's stream, and
+/// encodes objects so that JSON is passed to the transformed channel's sink.
+class JsonDocumentTransformer
+ implements StreamChannelTransformer<String, Object> {
+ /// The underlying codec that implements the encoding and decoding logic.
+ final JsonCodec _codec;
+
+ /// Creates a new transformer.
+ ///
+ /// The [reviver] and [toEncodable] arguments work the same way as the
+ /// corresponding arguments to [new JsonCodec].
+ JsonDocumentTransformer({reviver(key, value), toEncodable(object)})
+ : _codec = new JsonCodec(reviver: reviver, toEncodable: toEncodable);
+
+ JsonDocumentTransformer._(this._codec);
+
+ StreamChannel bind(StreamChannel<String> channel) {
+ var stream = channel.stream.map(_codec.decode);
+ var sink = new StreamSinkTransformer.fromHandlers(handleData: (data, sink) {
+ sink.add(_codec.encode(data));
+ }).bind(channel.sink);
+ return new StreamChannel(stream, sink);
+ }
+}
diff --git a/lib/stream_channel.dart b/lib/stream_channel.dart
index e7fb055..f914188 100644
--- a/lib/stream_channel.dart
+++ b/lib/stream_channel.dart
@@ -8,6 +8,7 @@
export 'src/delegating_stream_channel.dart';
export 'src/isolate_channel.dart';
+export 'src/json_document_transformer.dart';
export 'src/multi_channel.dart';
export 'src/stream_channel_completer.dart';
export 'src/stream_channel_transformer.dart';
diff --git a/test/json_document_transformer_test.dart b/test/json_document_transformer_test.dart
new file mode 100644
index 0000000..fec3d2a
--- /dev/null
+++ b/test/json_document_transformer_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:stream_channel/stream_channel.dart';
+import 'package:test/test.dart';
+
+import 'utils.dart';
+
+void main() {
+ var streamController;
+ var sinkController;
+ var channel;
+ setUp(() {
+ streamController = new StreamController();
+ sinkController = new StreamController();
+ channel = new StreamChannel(
+ streamController.stream, sinkController.sink);
+ });
+
+ test("decodes JSON emitted by the channel", () {
+ var transformed = channel.transform(jsonDocument);
+ streamController.add('{"foo": "bar"}');
+ expect(transformed.stream.first, completion(equals({"foo": "bar"})));
+ });
+
+ test("encodes objects added to the channel", () {
+ var transformed = channel.transform(jsonDocument);
+ transformed.sink.add({"foo": "bar"});
+ expect(sinkController.stream.first,
+ completion(equals(JSON.encode({"foo": "bar"}))));
+ });
+
+ test("supports the reviver function", () {
+ var transformed = channel.transform(
+ new JsonDocumentTransformer(reviver: (key, value) => "decoded"));
+ streamController.add('{"foo": "bar"}');
+ expect(transformed.stream.first, completion(equals("decoded")));
+ });
+
+ test("supports the toEncodable function", () {
+ var transformed = channel.transform(
+ new JsonDocumentTransformer(toEncodable: (object) => "encoded"));
+ transformed.sink.add(new Object());
+ expect(sinkController.stream.first, completion(equals('"encoded"')));
+ });
+}