Fix JSON serialization of unsigned 64-bit fields. (#229)

Fix JSON serialization of unsigned 64-bit fields.

The current behavior treats signed and unsigned 64-bit fields the same, which relies on users needing to know to flip the negative appropriately. With the introduction of toStringUnsigned in fixnum v 0.10.9, we can handle this correctly.
diff --git a/protobuf/CHANGELOG.md b/protobuf/CHANGELOG.md
index 26131fb..74b26e5 100644
--- a/protobuf/CHANGELOG.md
+++ b/protobuf/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.13.8
+
+* Fix JSON serialization of unsigned 64-bit fields.
+
 ## 0.13.7
 
 * Override `operator ==` and `hashCode` in `PbMap` so that two `PbMap`s are equal if they have equal key/value pairs.
diff --git a/protobuf/lib/src/protobuf/json.dart b/protobuf/lib/src/protobuf/json.dart
index f665608..23eb4fb 100644
--- a/protobuf/lib/src/protobuf/json.dart
+++ b/protobuf/lib/src/protobuf/json.dart
@@ -30,10 +30,11 @@
         return fieldValue.value; // assume |value| < 2^52
       case PbFieldType._INT64_BIT:
       case PbFieldType._SINT64_BIT:
-      case PbFieldType._UINT64_BIT:
-      case PbFieldType._FIXED64_BIT:
       case PbFieldType._SFIXED64_BIT:
         return fieldValue.toString();
+      case PbFieldType._UINT64_BIT:
+      case PbFieldType._FIXED64_BIT:
+        return fieldValue.toStringUnsigned();
       case PbFieldType._GROUP_BIT:
       case PbFieldType._MESSAGE_BIT:
         return fieldValue.writeToJsonMap();
diff --git a/protobuf/pubspec.yaml b/protobuf/pubspec.yaml
index 63f0a13..26678f2 100644
--- a/protobuf/pubspec.yaml
+++ b/protobuf/pubspec.yaml
@@ -1,5 +1,5 @@
 name: protobuf
-version: 0.13.7
+version: 0.13.8
 author: Dart Team <misc@dartlang.org>
 description: >
   Runtime library for protocol buffers support.
@@ -8,7 +8,7 @@
 environment:
   sdk: '>=2.0.0-dev.17.0 <3.0.0'
 dependencies:
-  fixnum: '>=0.9.0 <0.11.0'
+  fixnum: ^0.10.9
 dev_dependencies:
   test: '>=1.2.0'
   benchmark_harness: any
diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml
index c3ec0d3..9adcaf4 100644
--- a/protoc_plugin/pubspec.yaml
+++ b/protoc_plugin/pubspec.yaml
@@ -1,5 +1,5 @@
 name: protoc_plugin
-version: 16.0.4
+version: 16.0.4-dev-1
 author: Dart Team <misc@dartlang.org>
 description: Protoc compiler plugin to generate Dart code
 homepage: https://github.com/dart-lang/protobuf
@@ -8,9 +8,9 @@
   sdk: '>=2.0.0 <3.0.0'
 
 dependencies:
-  fixnum: ^0.10.5
+  fixnum: ^0.10.9
   path: ^1.0.0
-  protobuf: ^0.13.7
+  protobuf: ^0.13.8
   dart_style: ^1.0.6
 
 dev_dependencies:
diff --git a/protoc_plugin/test/json_test.dart b/protoc_plugin/test/json_test.dart
index 98b46e6..1ed35f6 100755
--- a/protoc_plugin/test/json_test.dart
+++ b/protoc_plugin/test/json_test.dart
@@ -5,6 +5,7 @@
 
 library json_test;
 
+import 'package:fixnum/fixnum.dart';
 import 'package:protobuf/protobuf.dart';
 import 'package:test/test.dart';
 
@@ -39,6 +40,20 @@
         (message) => message.writeToJson() == expectedJson, 'Incorrect output');
   }
 
+  test('testUnsignedOutput', () {
+    TestAllTypes message = TestAllTypes();
+    // These values selected because:
+    // (1) large enough to set the sign bit
+    // (2) don't set all of the first 10 bits under the sign bit
+    // (3) are near each other
+    message.optionalUint64 = Int64.parseHex("f0000000ffff0000");
+    message.optionalFixed64 = Int64.parseHex("f0000000ffff0001");
+
+    String expectedJsonValue =
+        '{"4":"17293822573397606400","8":"17293822573397606401"}';
+    expect(message.writeToJson(), expectedJsonValue);
+  });
+
   test('testOutput', () {
     expect(getAllSet().writeToJson(), TEST_ALL_TYPES_JSON);
 
@@ -105,6 +120,26 @@
     expect(optionalBytes(':"MTE2",', ':"YQ==",'), 'a');
   });
 
+  test('testParseUnsigned', () {
+    TestAllTypes parsed = TestAllTypes.fromJson(
+        '{"4":"17293822573397606400","8":"17293822573397606401"}');
+    TestAllTypes expected = TestAllTypes();
+    expected.optionalUint64 = Int64.parseHex("f0000000ffff0000");
+    expected.optionalFixed64 = Int64.parseHex("f0000000ffff0001");
+
+    expect(parsed, expected);
+  });
+
+  test('testParseUnsignedLegacy', () {
+    TestAllTypes parsed = TestAllTypes.fromJson(
+        '{"4":"-1152921500311945216","8":"-1152921500311945215"}');
+    TestAllTypes expected = TestAllTypes();
+    expected.optionalUint64 = Int64.parseHex("f0000000ffff0000");
+    expected.optionalFixed64 = Int64.parseHex("f0000000ffff0001");
+
+    expect(parsed, expected);
+  });
+
   test('testParse', () {
     expect(TestAllTypes.fromJson(TEST_ALL_TYPES_JSON), getAllSet());
   });