Partially move JSON encoding/decoding to protobuf_serializers
diff --git a/protobuf/lib/protobuf.dart b/protobuf/lib/protobuf.dart
index 13db3bd..ae6d6ba 100644
--- a/protobuf/lib/protobuf.dart
+++ b/protobuf/lib/protobuf.dart
@@ -29,7 +29,6 @@
 part 'src/protobuf/field_type.dart';
 part 'src/protobuf/generated_message.dart';
 part 'src/protobuf/generated_service.dart';
-part 'src/protobuf/json.dart';
 part 'src/protobuf/pb_list.dart';
 part 'src/protobuf/pb_map.dart';
 part 'src/protobuf/protobuf_enum.dart';
diff --git a/protobuf/lib/src/protobuf/builder_info.dart b/protobuf/lib/src/protobuf/builder_info.dart
index 1372c39..ea5324a 100644
--- a/protobuf/lib/src/protobuf/builder_info.dart
+++ b/protobuf/lib/src/protobuf/builder_info.dart
@@ -156,7 +156,8 @@
 
   // Repeated, not a message, group, or enum.
   void p<T>(int tagNumber, String name, int fieldType, {String? protoName}) {
-    assert(!_isGroupOrMessage(fieldType) && !_isEnum(fieldType));
+    assert(
+        !isGroupOrMessageFieldType(fieldType) && !isEnumFieldType(fieldType));
     addRepeated<T>(tagNumber, name, fieldType, getCheckFunction(fieldType),
         null, null, null,
         protoName: protoName);
@@ -169,7 +170,7 @@
       List<ProtobufEnum>? enumValues,
       ProtobufEnum? defaultEnumValue,
       String? protoName}) {
-    assert(_isGroupOrMessage(fieldType) || _isEnum(fieldType));
+    assert(isGroupOrMessageFieldType(fieldType) || isEnumFieldType(fieldType));
     addRepeated<T>(tagNumber, name, fieldType, _checkNotNull, subBuilder,
         valueOf, enumValues,
         defaultEnumValue: defaultEnumValue, protoName: protoName);
diff --git a/protobuf/lib/src/protobuf/extension_field_set.dart b/protobuf/lib/src/protobuf/extension_field_set.dart
index d936251..ec6eff9 100644
--- a/protobuf/lib/src/protobuf/extension_field_set.dart
+++ b/protobuf/lib/src/protobuf/extension_field_set.dart
@@ -7,7 +7,7 @@
 class ExtensionFieldSet {
   final FieldSet _parent;
   final Map<int, Extension> _info = <int, Extension>{};
-  final Map<int, dynamic> _values = <int, dynamic>{};
+  final Map<int, dynamic> values = <int, dynamic>{};
   bool _isReadOnly = false;
 
   ExtensionFieldSet(this._parent);
@@ -29,7 +29,7 @@
   }
 
   bool _hasField(int tagNumber) {
-    var value = _values[tagNumber];
+    var value = values[tagNumber];
     if (value == null) return false;
     if (value is List) return value.isNotEmpty;
     return true;
@@ -42,16 +42,16 @@
   List<T?> _ensureRepeatedField<T>(Extension<T?> fi) {
     assert(!_isReadOnly);
     assert(fi.isRepeated);
-    assert(fi.extendee == '' || fi.extendee == _parent._messageName);
+    assert(fi.extendee == '' || fi.extendee == _parent.messageName);
 
-    var list = _values[fi.tagNumber];
+    var list = values[fi.tagNumber];
     if (list != null) return list as List<T>;
 
     return _addInfoAndCreateList(fi) as List<T?>;
   }
 
   List<T?> _getList<T>(Extension<T?> fi) {
-    var value = _values[fi.tagNumber];
+    var value = values[fi.tagNumber];
     if (value != null) return value as List<T>;
     _checkNotInUnknown(fi);
     if (_isReadOnly) return List<T>.unmodifiable(const []);
@@ -66,7 +66,7 @@
     return newList;
   }
 
-  dynamic getFieldOrNull(Extension extension) => _values[extension.tagNumber];
+  dynamic getFieldOrNull(Extension extension) => values[extension.tagNumber];
 
   void _clearFieldAndInfo(Extension fi) {
     _clearField(fi);
@@ -77,7 +77,7 @@
     _ensureWritable();
     _validateInfo(fi);
     if (_parent._hasObservers) _parent._eventPlugin!.beforeClearField(fi);
-    _values.remove(fi.tagNumber);
+    values.remove(fi.tagNumber);
   }
 
   /// Sets a value for a non-repeated extension that has already been added.
@@ -93,7 +93,7 @@
           fi, value, 'repeating field (use get + .add())'));
     }
     _ensureWritable();
-    _parent._validateField(fi, value);
+    _parent.validateField(fi, value);
     _setFieldUnchecked(fi, value);
   }
 
@@ -107,24 +107,24 @@
     }
     _ensureWritable();
     _validateInfo(fi);
-    _parent._validateField(fi, value);
+    _parent.validateField(fi, value);
     _addInfoUnchecked(fi);
     _setFieldUnchecked(fi, value);
   }
 
   void _ensureWritable() {
-    if (_isReadOnly) frozenMessageModificationHandler(_parent._messageName);
+    if (_isReadOnly) frozenMessageModificationHandler(_parent.messageName);
   }
 
   void _validateInfo(Extension fi) {
-    if (fi.extendee != _parent._messageName) {
+    if (fi.extendee != _parent.messageName) {
       throw ArgumentError(
-          'Extension $fi not legal for message ${_parent._messageName}');
+          'Extension $fi not legal for message ${_parent.messageName}');
     }
   }
 
   void _addInfoUnchecked(Extension fi) {
-    assert(fi.extendee == _parent._messageName);
+    assert(fi.extendee == _parent.messageName);
     _info[fi.tagNumber] = fi;
   }
 
@@ -132,20 +132,20 @@
     if (_parent._hasObservers) {
       _parent._eventPlugin!.beforeSetField(fi, value);
     }
-    _values[fi.tagNumber] = value;
+    values[fi.tagNumber] = value;
   }
 
   // Bulk operations
 
-  Iterable<int> get tagNumbers => _values.keys;
+  Iterable<int> get tagNumbers => values.keys;
   Iterable<Extension> get _infos => _info.values;
 
-  bool get _hasValues => _values.isNotEmpty;
+  bool get _hasValues => values.isNotEmpty;
 
   bool _equalValues(ExtensionFieldSet? other) =>
-      other != null && _areMapsEqual(_values, other._values);
+      other != null && _areMapsEqual(values, other.values);
 
-  void _clearValues() => _values.clear();
+  void _clearValues() => values.clear();
 
   /// Makes a shallow copy of all values from [original] to this.
   ///
@@ -172,16 +172,16 @@
     _isReadOnly = true;
     for (var field in _info.values) {
       if (field.isRepeated) {
-        final entries = _values[field.tagNumber];
+        final entries = values[field.tagNumber];
         if (entries == null) continue;
         if (field.isGroupOrMessage) {
           for (var subMessage in entries as List<GeneratedMessage>) {
             subMessage.freeze();
           }
         }
-        _values[field.tagNumber] = entries.toFrozenPbList();
+        values[field.tagNumber] = entries.toFrozenPbList();
       } else if (field.isGroupOrMessage) {
-        final entry = _values[field.tagNumber];
+        final entry = values[field.tagNumber];
         if (entry != null) {
           (entry as GeneratedMessage).freeze();
         }
diff --git a/protobuf/lib/src/protobuf/field_info.dart b/protobuf/lib/src/protobuf/field_info.dart
index 53ada9a..aa76a0c 100644
--- a/protobuf/lib/src/protobuf/field_info.dart
+++ b/protobuf/lib/src/protobuf/field_info.dart
@@ -59,10 +59,10 @@
         check = null,
         protoName = protoName ?? _unCamelCase(name),
         assert(type != 0),
-        assert(!_isGroupOrMessage(type) ||
+        assert(!isGroupOrMessageFieldType(type) ||
             subBuilder != null ||
-            _isMapField(type)),
-        assert(!_isEnum(type) || valueOf != null);
+            isMapFieldType(type)),
+        assert(!isEnumFieldType(type) || valueOf != null);
 
   // Represents a field that has been removed by a program transformation.
   FieldInfo.dummy(this.index)
@@ -84,9 +84,9 @@
         protoName = protoName ?? _unCamelCase(name) {
     ArgumentError.checkNotNull(name, 'name');
     ArgumentError.checkNotNull(tagNumber, 'tagNumber');
-    assert(_isRepeated(type));
+    assert(isRepeatedFieldType(type));
     assert(check != null);
-    assert(!_isEnum(type) || valueOf != null);
+    assert(!isEnumFieldType(type) || valueOf != null);
   }
 
   static MakeDefaultFunc? findMakeDefault(int type, dynamic defaultOrMaker) {
@@ -99,11 +99,11 @@
   /// that has been removed by a program transformation.
   bool get _isDummy => tagNumber == 0;
 
-  bool get isRequired => _isRequired(type);
-  bool get isRepeated => _isRepeated(type);
-  bool get isGroupOrMessage => _isGroupOrMessage(type);
-  bool get isEnum => _isEnum(type);
-  bool get isMapField => _isMapField(type);
+  bool get isRequired => isRequiredFieldType(type);
+  bool get isRepeated => isRepeatedFieldType(type);
+  bool get isGroupOrMessage => isGroupOrMessageFieldType(type);
+  bool get isEnum => isEnumFieldType(type);
+  bool get isMapField => isMapFieldType(type);
 
   /// Returns a read-only default value for a field.
   /// (Unlike getField, doesn't create a repeated field.)
@@ -118,7 +118,7 @@
   /// That is, it doesn't contain any required fields that aren't initialized.
   bool _hasRequiredValues(value) {
     if (value == null) return !isRequired; // missing is okay if optional
-    if (!_isGroupOrMessage(type)) return true; // primitive and present
+    if (!isGroupOrMessageFieldType(type)) return true; // primitive and present
 
     if (!isRepeated) {
       // A required message: recurse.
@@ -141,7 +141,7 @@
   void _appendInvalidFields(List<String> problems, value, String prefix) {
     if (value == null) {
       if (isRequired) problems.add('$prefix$name');
-    } else if (!_isGroupOrMessage(type)) {
+    } else if (!isGroupOrMessageFieldType(type)) {
       // primitive and present
     } else if (!isRepeated) {
       // Required message/group: recurse.
@@ -182,7 +182,7 @@
 
   /// Convenience method to thread this FieldInfo's reified type parameter to
   /// _FieldSet._ensureRepeatedField.
-  List<T?> _ensureRepeatedField(BuilderInfo meta, FieldSet fs) {
+  List<T?> ensureRepeatedField(BuilderInfo meta, FieldSet fs) {
     return fs.ensureRepeatedField<T>(meta, this);
   }
 
@@ -226,14 +226,14 @@
             protoName: protoName) {
     ArgumentError.checkNotNull(name, 'name');
     ArgumentError.checkNotNull(tagNumber, 'tagNumber');
-    assert(_isMapField(type));
-    assert(!_isEnum(type) || valueOf != null);
+    assert(isMapFieldType(type));
+    assert(!isEnumFieldType(type) || valueOf != null);
   }
 
   FieldInfo get valueFieldInfo =>
       mapEntryBuilderInfo.fieldInfo[PbMap.valueFieldNumber]!;
 
-  Map<K, V> _ensureMapField(BuilderInfo meta, FieldSet fs) {
+  Map<K, V> ensureMapField(BuilderInfo meta, FieldSet fs) {
     return fs.ensureMapField<K, V>(meta, this);
   }
 
diff --git a/protobuf/lib/src/protobuf/field_set.dart b/protobuf/lib/src/protobuf/field_set.dart
index 284b26a..9dff575 100644
--- a/protobuf/lib/src/protobuf/field_set.dart
+++ b/protobuf/lib/src/protobuf/field_set.dart
@@ -117,7 +117,7 @@
 
   // Metadata about multiple fields
 
-  String get _messageName => meta.qualifiedMessageName;
+  String get messageName => meta.qualifiedMessageName;
   bool get _hasRequiredFields => meta.hasRequiredFields;
 
   /// The FieldInfo for each non-extension field.
@@ -159,7 +159,7 @@
   FieldInfo _ensureInfo(int tagNumber) {
     var fi = _getFieldInfoOrNull(tagNumber);
     if (fi != null) return fi;
-    throw ArgumentError('tag $tagNumber not defined in $_messageName');
+    throw ArgumentError('tag $tagNumber not defined in $messageName');
   }
 
   /// Returns the FieldInfo for a regular or extension field.
@@ -204,7 +204,7 @@
   }
 
   void _ensureWritable() {
-    if (_isReadOnly) frozenMessageModificationHandler(_messageName);
+    if (_isReadOnly) frozenMessageModificationHandler(messageName);
   }
 
   // Single-field operations
@@ -227,7 +227,7 @@
         return extensions!._getFieldOrDefault(fi);
       }
     }
-    throw ArgumentError('tag $tagNumber not defined in $_messageName');
+    throw ArgumentError('tag $tagNumber not defined in $messageName');
   }
 
   dynamic _getDefault(FieldInfo fi) {
@@ -331,7 +331,7 @@
     var fi = nonExtensionInfo(info, tagNumber);
     if (fi == null) {
       if (!hasExtensions) {
-        throw ArgumentError('tag $tagNumber not defined in $_messageName');
+        throw ArgumentError('tag $tagNumber not defined in $messageName');
       }
       extensions!._setField(tagNumber, value);
       return;
@@ -341,7 +341,7 @@
       throw ArgumentError(_setFieldFailedMessage(
           fi, value, 'repeating field (use get + .add())'));
     }
-    _validateField(fi, value);
+    validateField(fi, value);
     _setNonExtensionFieldUnchecked(info, fi, value);
   }
 
@@ -561,7 +561,7 @@
   }
 
   bool _$check(int index, var newValue) {
-    _validateField(_nonExtensionInfoByIndex(index), newValue);
+    validateField(_nonExtensionInfoByIndex(index), newValue);
     return true; // Allows use in an assertion.
   }
 
@@ -661,11 +661,11 @@
       }
 
       hash = _HashUtils._combine(hash, fi.tagNumber);
-      if (_isBytes(fi.type)) {
+      if (isBytesFieldType(fi.type)) {
         // Bytes are represented as a List<int> (Usually with byte-data).
         // We special case that to match our equality semantics.
         hash = _HashUtils._combine(hash, _HashUtils._hashObjects(value));
-      } else if (!_isEnum(fi.type)) {
+      } else if (!isEnumFieldType(fi.type)) {
         hash = _HashUtils._combine(hash, value.hashCode);
       } else if (fi.isRepeated) {
         hash = _HashUtils._hashObjects(value.map((enm) => enm.value));
@@ -747,7 +747,7 @@
       extensions!._info.keys.toList()
         ..sort()
         ..forEach((int tagNumber) => writeFieldValue(
-            extensions!._values[tagNumber],
+            extensions!.values[tagNumber],
             '[${extensions!._info[tagNumber]!.name}]'));
     }
     if (hasUnknownFields) {
@@ -798,12 +798,12 @@
       fi = otherFi;
     }
 
-    var mustClone = _isGroupOrMessage(otherFi.type);
+    var mustClone = isGroupOrMessageFieldType(otherFi.type);
 
     if (fi!.isMapField) {
       var f = fi as MapFieldInfo<dynamic, dynamic>;
-      mustClone = _isGroupOrMessage(f.valueFieldType!);
-      var map = f._ensureMapField(info, this) as PbMap<dynamic, dynamic>;
+      mustClone = isGroupOrMessageFieldType(f.valueFieldType!);
+      var map = f.ensureMapField(info, this) as PbMap<dynamic, dynamic>;
       if (mustClone) {
         for (MapEntry entry in fieldValue.entries) {
           map[entry.key] = (entry.value as GeneratedMessage).deepCopy();
@@ -818,14 +818,14 @@
       if (mustClone) {
         // fieldValue must be a PbListBase of GeneratedMessage.
         PbListBase<GeneratedMessage> pbList = fieldValue;
-        var repeatedFields = fi._ensureRepeatedField(info, this);
+        var repeatedFields = fi.ensureRepeatedField(info, this);
         for (var i = 0; i < pbList.length; ++i) {
           repeatedFields.add(pbList[i].deepCopy());
         }
       } else {
         // fieldValue must be at least a PbListBase.
         PbListBase pbList = fieldValue;
-        fi._ensureRepeatedField(meta, this).addAll(pbList);
+        fi.ensureRepeatedField(meta, this).addAll(pbList);
       }
       return;
     }
@@ -846,7 +846,7 @@
       _ensureExtensions()
           ._setFieldAndInfo(fi as Extension<dynamic>, fieldValue);
     } else {
-      _validateField(fi, fieldValue);
+      validateField(fi, fieldValue);
       _setNonExtensionFieldUnchecked(info, fi, fieldValue);
     }
   }
@@ -854,7 +854,7 @@
   // Error-checking
 
   /// Checks the value for a field that's about to be set.
-  void _validateField(FieldInfo fi, var newValue) {
+  void validateField(FieldInfo fi, var newValue) {
     _ensureWritable();
     var message = _getFieldError(fi.type, newValue);
     if (message != null) {
@@ -863,7 +863,7 @@
   }
 
   String _setFieldFailedMessage(FieldInfo fi, var value, String detail) {
-    return 'Illegal to set field ${fi.name} (${fi.tagNumber}) of $_messageName'
+    return 'Illegal to set field ${fi.name} (${fi.tagNumber}) of $messageName'
         ' to value ($value): $detail';
   }
 
diff --git a/protobuf/lib/src/protobuf/field_type.dart b/protobuf/lib/src/protobuf/field_type.dart
index bd02ad1..d7317b3 100644
--- a/protobuf/lib/src/protobuf/field_type.dart
+++ b/protobuf/lib/src/protobuf/field_type.dart
@@ -5,20 +5,22 @@
 // ignore_for_file: constant_identifier_names,non_constant_identifier_names
 part of protobuf;
 
-bool _isRepeated(int fieldType) => (fieldType & PbFieldType.REPEATED_BIT) != 0;
+bool isRepeatedFieldType(int fieldType) =>
+    (fieldType & PbFieldType.REPEATED_BIT) != 0;
 
-bool _isRequired(int fieldType) => (fieldType & PbFieldType.REQUIRED_BIT) != 0;
+bool isRequiredFieldType(int fieldType) =>
+    (fieldType & PbFieldType.REQUIRED_BIT) != 0;
 
-bool _isEnum(int fieldType) =>
+bool isEnumFieldType(int fieldType) =>
     PbFieldType.baseType(fieldType) == PbFieldType.ENUM_BIT;
 
-bool _isBytes(int fieldType) =>
+bool isBytesFieldType(int fieldType) =>
     PbFieldType.baseType(fieldType) == PbFieldType.BYTES_BIT;
 
-bool _isGroupOrMessage(int fieldType) =>
+bool isGroupOrMessageFieldType(int fieldType) =>
     (fieldType & (PbFieldType.GROUP_BIT | PbFieldType.MESSAGE_BIT)) != 0;
 
-bool _isMapField(int fieldType) => (fieldType & PbFieldType.MAP_BIT) != 0;
+bool isMapFieldType(int fieldType) => (fieldType & PbFieldType.MAP_BIT) != 0;
 
 /// Defines constants and functions for dealing with fieldType bits.
 class PbFieldType {
diff --git a/protobuf/lib/src/protobuf/generated_message.dart b/protobuf/lib/src/protobuf/generated_message.dart
index b3c922f..a831416 100644
--- a/protobuf/lib/src/protobuf/generated_message.dart
+++ b/protobuf/lib/src/protobuf/generated_message.dart
@@ -390,7 +390,7 @@
   /// Sets the value of a non-repeated extension field to [value].
   void setExtension(Extension extension, Object value) {
     ArgumentError.checkNotNull(value, 'value');
-    if (_isRepeated(extension.type)) {
+    if (isRepeatedFieldType(extension.type)) {
       throw ArgumentError(fieldSet._setFieldFailedMessage(
           extension, value, 'repeating field (use get + .add())'));
     }
diff --git a/protobuf/lib/src/protobuf/pb_map.dart b/protobuf/lib/src/protobuf/pb_map.dart
index d8a2e79..254ea26 100644
--- a/protobuf/lib/src/protobuf/pb_map.dart
+++ b/protobuf/lib/src/protobuf/pb_map.dart
@@ -116,7 +116,7 @@
 
   PbMap freeze() {
     _isReadonly = true;
-    if (_isGroupOrMessage(valueFieldType!)) {
+    if (isGroupOrMessageFieldType(valueFieldType!)) {
       for (var subMessage in values as Iterable<GeneratedMessage>) {
         subMessage.freeze();
       }
diff --git a/protobuf/lib/src/protobuf/proto3_json.dart b/protobuf/lib/src/protobuf/proto3_json.dart
index 1f7feb7..b8edff8 100644
--- a/protobuf/lib/src/protobuf/proto3_json.dart
+++ b/protobuf/lib/src/protobuf/proto3_json.dart
@@ -8,7 +8,7 @@
   String? convertToMapKey(dynamic key, int keyType) {
     var baseType = PbFieldType.baseType(keyType);
 
-    assert(!_isRepeated(keyType));
+    assert(!isRepeatedFieldType(keyType));
 
     switch (baseType) {
       case PbFieldType.BOOL_BIT:
@@ -35,10 +35,10 @@
   Object? valueToProto3Json(dynamic fieldValue, int? fieldType) {
     if (fieldValue == null) return null;
 
-    if (_isGroupOrMessage(fieldType!)) {
+    if (isGroupOrMessageFieldType(fieldType!)) {
       return _writeToProto3Json(
           (fieldValue as GeneratedMessage).fieldSet, typeRegistry);
-    } else if (_isEnum(fieldType)) {
+    } else if (isEnumFieldType(fieldType)) {
       return (fieldValue as ProtobufEnum).name;
     } else {
       var baseType = PbFieldType.baseType(fieldType);
@@ -357,7 +357,7 @@
             }
           }
 
-          if (_isMapField(fieldInfo.type)) {
+          if (isMapFieldType(fieldInfo.type)) {
             if (value is Map) {
               final mapFieldInfo = fieldInfo as MapFieldInfo<dynamic, dynamic>;
               final Map fieldValues = fieldSet.ensureMapField(info, fieldInfo);
@@ -374,7 +374,7 @@
             } else {
               throw context.parseException('Expected a map', value);
             }
-          } else if (_isRepeated(fieldInfo.type)) {
+          } else if (isRepeatedFieldType(fieldInfo.type)) {
             if (value == null) {
               // `null` is accepted as the empty list [].
               fieldSet.ensureRepeatedField(info, fieldInfo);
@@ -389,7 +389,7 @@
             } else {
               throw context.parseException('Expected a list', value);
             }
-          } else if (_isGroupOrMessage(fieldInfo.type)) {
+          } else if (isGroupOrMessageFieldType(fieldInfo.type)) {
             // TODO(sigurdm) consider a cleaner separation between parsing and
             // merging.
             var parsedSubMessage =
diff --git a/protobuf_serializers/lib/protobuf_serializers.dart b/protobuf_serializers/lib/protobuf_serializers.dart
index f8c9a0c..d55d745 100644
--- a/protobuf_serializers/lib/protobuf_serializers.dart
+++ b/protobuf_serializers/lib/protobuf_serializers.dart
@@ -1,5 +1,6 @@
 library protobuf_serializers;
 
+import 'dart:convert' show base64Decode, base64Encode;
 import 'dart:math' as math;
 import 'dart:typed_data' show TypedData, Uint8List, ByteData, Endian;
 
@@ -7,289 +8,7 @@
 
 import 'package:protobuf/protobuf.dart';
 
+part 'src/coded_buffer.dart';
 part 'src/coded_buffer_reader.dart';
 part 'src/coded_buffer_writer.dart';
-
-void writeToCodedBufferWriter(FieldSet fs, CodedBufferWriter out) {
-  // Sorting by tag number isn't required, but it sometimes enables performance
-  // optimizations for the receiver. See:
-  // https://developers.google.com/protocol-buffers/docs/encoding?hl=en#order
-
-  for (var fi in fs.infosSortedByTag) {
-    var value = fs.values[fi.index!];
-    if (value == null) continue;
-    out.writeField(fi.tagNumber, fi.type, value);
-  }
-
-  if (fs.hasExtensions) {
-    for (var tagNumber in _sorted(fs.extensions!.tagNumbers)) {
-      var fi = fs.extensions!.getInfoOrNull(tagNumber)!;
-      out.writeField(tagNumber, fi.type, fs.extensions!.getFieldOrNull(fi));
-    }
-  }
-
-  if (fs.hasUnknownFields) {
-    final fields = fs.unknownFields!.fields;
-    for (var key in fields.keys) {
-      final UnknownFieldSetField value = fields[key]!;
-
-      out.writeField(key, PbFieldType.REPEATED_UINT64, value.varints);
-      out.writeField(key, PbFieldType.REPEATED_FIXED32, value.fixed32s);
-      out.writeField(key, PbFieldType.REPEATED_FIXED64, value.fixed64s);
-      out.writeField(key, PbFieldType.REPEATED_BYTES, value.lengthDelimited);
-      out.writeField(key, PbFieldType.REPEATED_GROUP, value.groups);
-    }
-  }
-}
-
-void mergeFromCodedBufferReader(BuilderInfo meta, FieldSet fs,
-    CodedBufferReader input, ExtensionRegistry registry) {
-  ArgumentError.checkNotNull(registry);
-  while (true) {
-    var tag = input.readTag();
-    if (tag == 0) return;
-    var wireType = tag & 0x7;
-    var tagNumber = tag >> 3;
-
-    var fi = fs.nonExtensionInfo(meta, tagNumber);
-    fi ??= registry.getExtension(meta.qualifiedMessageName, tagNumber);
-
-    if (fi == null || !_wireTypeMatches(fi.type, wireType)) {
-      if (!input._mergeField(tag, fs.ensureUnknownFields())) {
-        return;
-      }
-      continue;
-    }
-
-    // Ignore required/optional packed/unpacked.
-    var fieldType = fi.type;
-    fieldType &= ~(PbFieldType.PACKED_BIT | PbFieldType.REQUIRED_BIT);
-    switch (fieldType) {
-      case PbFieldType.OPTIONAL_BOOL:
-        fs.setFieldUnchecked(meta, fi, input.readBool());
-        break;
-      case PbFieldType.OPTIONAL_BYTES:
-        fs.setFieldUnchecked(meta, fi, input.readBytes());
-        break;
-      case PbFieldType.OPTIONAL_STRING:
-        fs.setFieldUnchecked(meta, fi, input.readString());
-        break;
-      case PbFieldType.OPTIONAL_FLOAT:
-        fs.setFieldUnchecked(meta, fi, input.readFloat());
-        break;
-      case PbFieldType.OPTIONAL_DOUBLE:
-        fs.setFieldUnchecked(meta, fi, input.readDouble());
-        break;
-      case PbFieldType.OPTIONAL_ENUM:
-        var rawValue = input.readEnum();
-        var value = meta.decodeEnum(tagNumber, registry, rawValue);
-        if (value == null) {
-          var unknown = fs.ensureUnknownFields();
-          unknown.mergeVarintField(tagNumber, Int64(rawValue));
-        } else {
-          fs.setFieldUnchecked(meta, fi, value);
-        }
-        break;
-      case PbFieldType.OPTIONAL_GROUP:
-        var subMessage = meta.makeEmptyMessage(tagNumber, registry);
-        var oldValue = fs.getFieldOrNull(fi);
-        if (oldValue != null) {
-          subMessage.mergeFromMessage(oldValue);
-        }
-        input.readGroup(tagNumber, subMessage, registry);
-        fs.setFieldUnchecked(meta, fi, subMessage);
-        break;
-      case PbFieldType.OPTIONAL_INT32:
-        fs.setFieldUnchecked(meta, fi, input.readInt32());
-        break;
-      case PbFieldType.OPTIONAL_INT64:
-        fs.setFieldUnchecked(meta, fi, input.readInt64());
-        break;
-      case PbFieldType.OPTIONAL_SINT32:
-        fs.setFieldUnchecked(meta, fi, input.readSint32());
-        break;
-      case PbFieldType.OPTIONAL_SINT64:
-        fs.setFieldUnchecked(meta, fi, input.readSint64());
-        break;
-      case PbFieldType.OPTIONAL_UINT32:
-        fs.setFieldUnchecked(meta, fi, input.readUint32());
-        break;
-      case PbFieldType.OPTIONAL_UINT64:
-        fs.setFieldUnchecked(meta, fi, input.readUint64());
-        break;
-      case PbFieldType.OPTIONAL_FIXED32:
-        fs.setFieldUnchecked(meta, fi, input.readFixed32());
-        break;
-      case PbFieldType.OPTIONAL_FIXED64:
-        fs.setFieldUnchecked(meta, fi, input.readFixed64());
-        break;
-      case PbFieldType.OPTIONAL_SFIXED32:
-        fs.setFieldUnchecked(meta, fi, input.readSfixed32());
-        break;
-      case PbFieldType.OPTIONAL_SFIXED64:
-        fs.setFieldUnchecked(meta, fi, input.readSfixed64());
-        break;
-      case PbFieldType.OPTIONAL_MESSAGE:
-        var subMessage = meta.makeEmptyMessage(tagNumber, registry);
-        var oldValue = fs.getFieldOrNull(fi);
-        if (oldValue != null) {
-          subMessage.mergeFromMessage(oldValue);
-        }
-        input.readMessage(subMessage, registry);
-        fs.setFieldUnchecked(meta, fi, subMessage);
-        break;
-      case PbFieldType.REPEATED_BOOL:
-        _readPackable(meta, fs, input, wireType, fi, input.readBool);
-        break;
-      case PbFieldType.REPEATED_BYTES:
-        fs.ensureRepeatedField(meta, fi).add(input.readBytes());
-        break;
-      case PbFieldType.REPEATED_STRING:
-        fs.ensureRepeatedField(meta, fi).add(input.readString());
-        break;
-      case PbFieldType.REPEATED_FLOAT:
-        _readPackable(meta, fs, input, wireType, fi, input.readFloat);
-        break;
-      case PbFieldType.REPEATED_DOUBLE:
-        _readPackable(meta, fs, input, wireType, fi, input.readDouble);
-        break;
-      case PbFieldType.REPEATED_ENUM:
-        _readPackableToListEnum(
-            meta, fs, input, wireType, fi, tagNumber, registry);
-        break;
-      case PbFieldType.REPEATED_GROUP:
-        var subMessage = meta.makeEmptyMessage(tagNumber, registry);
-        input.readGroup(tagNumber, subMessage, registry);
-        fs.ensureRepeatedField(meta, fi).add(subMessage);
-        break;
-      case PbFieldType.REPEATED_INT32:
-        _readPackable(meta, fs, input, wireType, fi, input.readInt32);
-        break;
-      case PbFieldType.REPEATED_INT64:
-        _readPackable(meta, fs, input, wireType, fi, input.readInt64);
-        break;
-      case PbFieldType.REPEATED_SINT32:
-        _readPackable(meta, fs, input, wireType, fi, input.readSint32);
-        break;
-      case PbFieldType.REPEATED_SINT64:
-        _readPackable(meta, fs, input, wireType, fi, input.readSint64);
-        break;
-      case PbFieldType.REPEATED_UINT32:
-        _readPackable(meta, fs, input, wireType, fi, input.readUint32);
-        break;
-      case PbFieldType.REPEATED_UINT64:
-        _readPackable(meta, fs, input, wireType, fi, input.readUint64);
-        break;
-      case PbFieldType.REPEATED_FIXED32:
-        _readPackable(meta, fs, input, wireType, fi, input.readFixed32);
-        break;
-      case PbFieldType.REPEATED_FIXED64:
-        _readPackable(meta, fs, input, wireType, fi, input.readFixed64);
-        break;
-      case PbFieldType.REPEATED_SFIXED32:
-        _readPackable(meta, fs, input, wireType, fi, input.readSfixed32);
-        break;
-      case PbFieldType.REPEATED_SFIXED64:
-        _readPackable(meta, fs, input, wireType, fi, input.readSfixed64);
-        break;
-      case PbFieldType.REPEATED_MESSAGE:
-        var subMessage = meta.makeEmptyMessage(tagNumber, registry);
-        input.readMessage(subMessage, registry);
-        fs.ensureRepeatedField(meta, fi).add(subMessage);
-        break;
-      case PbFieldType.MAP:
-        final mapFieldInfo = fi as MapFieldInfo;
-        final mapEntryMeta = mapFieldInfo.mapEntryBuilderInfo;
-        input._mergeMapEntry(
-            mapEntryMeta, fs.ensureMapField(meta, mapFieldInfo), registry);
-        break;
-      default:
-        throw 'Unknown field type $fieldType';
-    }
-  }
-}
-
-void _readPackable(BuilderInfo meta, FieldSet fs, CodedBufferReader input,
-    int wireType, FieldInfo fi, Function readFunc) {
-  void readToList(List list) => list.add(readFunc());
-  _readPackableToList(meta, fs, input, wireType, fi, readToList);
-}
-
-void _readPackableToListEnum(
-    BuilderInfo meta,
-    FieldSet fs,
-    CodedBufferReader input,
-    int wireType,
-    FieldInfo fi,
-    int tagNumber,
-    ExtensionRegistry registry) {
-  void readToList(List list) {
-    var rawValue = input.readEnum();
-    var value = meta.decodeEnum(tagNumber, registry, rawValue);
-    if (value == null) {
-      var unknown = fs.ensureUnknownFields();
-      unknown.mergeVarintField(tagNumber, Int64(rawValue));
-    } else {
-      list.add(value);
-    }
-  }
-
-  _readPackableToList(meta, fs, input, wireType, fi, readToList);
-}
-
-void _readPackableToList(BuilderInfo meta, FieldSet fs, CodedBufferReader input,
-    int wireType, FieldInfo fi, Function readToList) {
-  var list = fs.ensureRepeatedField(meta, fi);
-
-  if (wireType == WIRETYPE_LENGTH_DELIMITED) {
-    // Packed.
-    input._withLimit(input.readInt32(), () {
-      while (!input.isAtEnd()) {
-        readToList(list);
-      }
-    });
-  } else {
-    // Not packed.
-    readToList(list);
-  }
-}
-
-List<T> _sorted<T>(Iterable<T> list) => List.from(list)..sort();
-
-int getTagFieldNumber(int tag) => tag >> TAG_TYPE_BITS;
-
-int getTagWireType(int tag) => tag & TAG_TYPE_MASK;
-
-/// Returns true if the wireType can be merged into the given fieldType.
-bool _wireTypeMatches(int fieldType, int wireType) {
-  switch (PbFieldType.baseType(fieldType)) {
-    case PbFieldType.BOOL_BIT:
-    case PbFieldType.ENUM_BIT:
-    case PbFieldType.INT32_BIT:
-    case PbFieldType.INT64_BIT:
-    case PbFieldType.SINT32_BIT:
-    case PbFieldType.SINT64_BIT:
-    case PbFieldType.UINT32_BIT:
-    case PbFieldType.UINT64_BIT:
-      return wireType == WIRETYPE_VARINT ||
-          wireType == WIRETYPE_LENGTH_DELIMITED;
-    case PbFieldType.FLOAT_BIT:
-    case PbFieldType.FIXED32_BIT:
-    case PbFieldType.SFIXED32_BIT:
-      return wireType == WIRETYPE_FIXED32 ||
-          wireType == WIRETYPE_LENGTH_DELIMITED;
-    case PbFieldType.DOUBLE_BIT:
-    case PbFieldType.FIXED64_BIT:
-    case PbFieldType.SFIXED64_BIT:
-      return wireType == WIRETYPE_FIXED64 ||
-          wireType == WIRETYPE_LENGTH_DELIMITED;
-    case PbFieldType.BYTES_BIT:
-    case PbFieldType.STRING_BIT:
-    case PbFieldType.MESSAGE_BIT:
-      return wireType == WIRETYPE_LENGTH_DELIMITED;
-    case PbFieldType.GROUP_BIT:
-      return wireType == WIRETYPE_START_GROUP;
-    default:
-      return false;
-  }
-}
+part 'src/json.dart';
diff --git a/protobuf_serializers/lib/src/coded_buffer.dart b/protobuf_serializers/lib/src/coded_buffer.dart
new file mode 100644
index 0000000..486c62e
--- /dev/null
+++ b/protobuf_serializers/lib/src/coded_buffer.dart
@@ -0,0 +1,289 @@
+// Copyright (c) 2011, 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.
+
+part of protobuf_serializers;
+
+void writeToCodedBufferWriter(FieldSet fs, CodedBufferWriter out) {
+  // Sorting by tag number isn't required, but it sometimes enables performance
+  // optimizations for the receiver. See:
+  // https://developers.google.com/protocol-buffers/docs/encoding?hl=en#order
+
+  for (var fi in fs.infosSortedByTag) {
+    var value = fs.values[fi.index!];
+    if (value == null) continue;
+    out.writeField(fi.tagNumber, fi.type, value);
+  }
+
+  if (fs.hasExtensions) {
+    for (var tagNumber in _sorted(fs.extensions!.tagNumbers)) {
+      var fi = fs.extensions!.getInfoOrNull(tagNumber)!;
+      out.writeField(tagNumber, fi.type, fs.extensions!.getFieldOrNull(fi));
+    }
+  }
+
+  if (fs.hasUnknownFields) {
+    final fields = fs.unknownFields!.fields;
+    for (var key in fields.keys) {
+      final UnknownFieldSetField value = fields[key]!;
+
+      out.writeField(key, PbFieldType.REPEATED_UINT64, value.varints);
+      out.writeField(key, PbFieldType.REPEATED_FIXED32, value.fixed32s);
+      out.writeField(key, PbFieldType.REPEATED_FIXED64, value.fixed64s);
+      out.writeField(key, PbFieldType.REPEATED_BYTES, value.lengthDelimited);
+      out.writeField(key, PbFieldType.REPEATED_GROUP, value.groups);
+    }
+  }
+}
+
+void mergeFromCodedBufferReader(BuilderInfo meta, FieldSet fs,
+    CodedBufferReader input, ExtensionRegistry registry) {
+  ArgumentError.checkNotNull(registry);
+  while (true) {
+    var tag = input.readTag();
+    if (tag == 0) return;
+    var wireType = tag & 0x7;
+    var tagNumber = tag >> 3;
+
+    var fi = fs.nonExtensionInfo(meta, tagNumber);
+    fi ??= registry.getExtension(meta.qualifiedMessageName, tagNumber);
+
+    if (fi == null || !_wireTypeMatches(fi.type, wireType)) {
+      if (!input._mergeField(tag, fs.ensureUnknownFields())) {
+        return;
+      }
+      continue;
+    }
+
+    // Ignore required/optional packed/unpacked.
+    var fieldType = fi.type;
+    fieldType &= ~(PbFieldType.PACKED_BIT | PbFieldType.REQUIRED_BIT);
+    switch (fieldType) {
+      case PbFieldType.OPTIONAL_BOOL:
+        fs.setFieldUnchecked(meta, fi, input.readBool());
+        break;
+      case PbFieldType.OPTIONAL_BYTES:
+        fs.setFieldUnchecked(meta, fi, input.readBytes());
+        break;
+      case PbFieldType.OPTIONAL_STRING:
+        fs.setFieldUnchecked(meta, fi, input.readString());
+        break;
+      case PbFieldType.OPTIONAL_FLOAT:
+        fs.setFieldUnchecked(meta, fi, input.readFloat());
+        break;
+      case PbFieldType.OPTIONAL_DOUBLE:
+        fs.setFieldUnchecked(meta, fi, input.readDouble());
+        break;
+      case PbFieldType.OPTIONAL_ENUM:
+        var rawValue = input.readEnum();
+        var value = meta.decodeEnum(tagNumber, registry, rawValue);
+        if (value == null) {
+          var unknown = fs.ensureUnknownFields();
+          unknown.mergeVarintField(tagNumber, Int64(rawValue));
+        } else {
+          fs.setFieldUnchecked(meta, fi, value);
+        }
+        break;
+      case PbFieldType.OPTIONAL_GROUP:
+        var subMessage = meta.makeEmptyMessage(tagNumber, registry);
+        var oldValue = fs.getFieldOrNull(fi);
+        if (oldValue != null) {
+          subMessage.mergeFromMessage(oldValue);
+        }
+        input.readGroup(tagNumber, subMessage, registry);
+        fs.setFieldUnchecked(meta, fi, subMessage);
+        break;
+      case PbFieldType.OPTIONAL_INT32:
+        fs.setFieldUnchecked(meta, fi, input.readInt32());
+        break;
+      case PbFieldType.OPTIONAL_INT64:
+        fs.setFieldUnchecked(meta, fi, input.readInt64());
+        break;
+      case PbFieldType.OPTIONAL_SINT32:
+        fs.setFieldUnchecked(meta, fi, input.readSint32());
+        break;
+      case PbFieldType.OPTIONAL_SINT64:
+        fs.setFieldUnchecked(meta, fi, input.readSint64());
+        break;
+      case PbFieldType.OPTIONAL_UINT32:
+        fs.setFieldUnchecked(meta, fi, input.readUint32());
+        break;
+      case PbFieldType.OPTIONAL_UINT64:
+        fs.setFieldUnchecked(meta, fi, input.readUint64());
+        break;
+      case PbFieldType.OPTIONAL_FIXED32:
+        fs.setFieldUnchecked(meta, fi, input.readFixed32());
+        break;
+      case PbFieldType.OPTIONAL_FIXED64:
+        fs.setFieldUnchecked(meta, fi, input.readFixed64());
+        break;
+      case PbFieldType.OPTIONAL_SFIXED32:
+        fs.setFieldUnchecked(meta, fi, input.readSfixed32());
+        break;
+      case PbFieldType.OPTIONAL_SFIXED64:
+        fs.setFieldUnchecked(meta, fi, input.readSfixed64());
+        break;
+      case PbFieldType.OPTIONAL_MESSAGE:
+        var subMessage = meta.makeEmptyMessage(tagNumber, registry);
+        var oldValue = fs.getFieldOrNull(fi);
+        if (oldValue != null) {
+          subMessage.mergeFromMessage(oldValue);
+        }
+        input.readMessage(subMessage, registry);
+        fs.setFieldUnchecked(meta, fi, subMessage);
+        break;
+      case PbFieldType.REPEATED_BOOL:
+        _readPackable(meta, fs, input, wireType, fi, input.readBool);
+        break;
+      case PbFieldType.REPEATED_BYTES:
+        fs.ensureRepeatedField(meta, fi).add(input.readBytes());
+        break;
+      case PbFieldType.REPEATED_STRING:
+        fs.ensureRepeatedField(meta, fi).add(input.readString());
+        break;
+      case PbFieldType.REPEATED_FLOAT:
+        _readPackable(meta, fs, input, wireType, fi, input.readFloat);
+        break;
+      case PbFieldType.REPEATED_DOUBLE:
+        _readPackable(meta, fs, input, wireType, fi, input.readDouble);
+        break;
+      case PbFieldType.REPEATED_ENUM:
+        _readPackableToListEnum(
+            meta, fs, input, wireType, fi, tagNumber, registry);
+        break;
+      case PbFieldType.REPEATED_GROUP:
+        var subMessage = meta.makeEmptyMessage(tagNumber, registry);
+        input.readGroup(tagNumber, subMessage, registry);
+        fs.ensureRepeatedField(meta, fi).add(subMessage);
+        break;
+      case PbFieldType.REPEATED_INT32:
+        _readPackable(meta, fs, input, wireType, fi, input.readInt32);
+        break;
+      case PbFieldType.REPEATED_INT64:
+        _readPackable(meta, fs, input, wireType, fi, input.readInt64);
+        break;
+      case PbFieldType.REPEATED_SINT32:
+        _readPackable(meta, fs, input, wireType, fi, input.readSint32);
+        break;
+      case PbFieldType.REPEATED_SINT64:
+        _readPackable(meta, fs, input, wireType, fi, input.readSint64);
+        break;
+      case PbFieldType.REPEATED_UINT32:
+        _readPackable(meta, fs, input, wireType, fi, input.readUint32);
+        break;
+      case PbFieldType.REPEATED_UINT64:
+        _readPackable(meta, fs, input, wireType, fi, input.readUint64);
+        break;
+      case PbFieldType.REPEATED_FIXED32:
+        _readPackable(meta, fs, input, wireType, fi, input.readFixed32);
+        break;
+      case PbFieldType.REPEATED_FIXED64:
+        _readPackable(meta, fs, input, wireType, fi, input.readFixed64);
+        break;
+      case PbFieldType.REPEATED_SFIXED32:
+        _readPackable(meta, fs, input, wireType, fi, input.readSfixed32);
+        break;
+      case PbFieldType.REPEATED_SFIXED64:
+        _readPackable(meta, fs, input, wireType, fi, input.readSfixed64);
+        break;
+      case PbFieldType.REPEATED_MESSAGE:
+        var subMessage = meta.makeEmptyMessage(tagNumber, registry);
+        input.readMessage(subMessage, registry);
+        fs.ensureRepeatedField(meta, fi).add(subMessage);
+        break;
+      case PbFieldType.MAP:
+        final mapFieldInfo = fi as MapFieldInfo;
+        final mapEntryMeta = mapFieldInfo.mapEntryBuilderInfo;
+        input._mergeMapEntry(
+            mapEntryMeta, fs.ensureMapField(meta, mapFieldInfo), registry);
+        break;
+      default:
+        throw 'Unknown field type $fieldType';
+    }
+  }
+}
+
+void _readPackable(BuilderInfo meta, FieldSet fs, CodedBufferReader input,
+    int wireType, FieldInfo fi, Function readFunc) {
+  void readToList(List list) => list.add(readFunc());
+  _readPackableToList(meta, fs, input, wireType, fi, readToList);
+}
+
+void _readPackableToListEnum(
+    BuilderInfo meta,
+    FieldSet fs,
+    CodedBufferReader input,
+    int wireType,
+    FieldInfo fi,
+    int tagNumber,
+    ExtensionRegistry registry) {
+  void readToList(List list) {
+    var rawValue = input.readEnum();
+    var value = meta.decodeEnum(tagNumber, registry, rawValue);
+    if (value == null) {
+      var unknown = fs.ensureUnknownFields();
+      unknown.mergeVarintField(tagNumber, Int64(rawValue));
+    } else {
+      list.add(value);
+    }
+  }
+
+  _readPackableToList(meta, fs, input, wireType, fi, readToList);
+}
+
+void _readPackableToList(BuilderInfo meta, FieldSet fs, CodedBufferReader input,
+    int wireType, FieldInfo fi, Function readToList) {
+  var list = fs.ensureRepeatedField(meta, fi);
+
+  if (wireType == WIRETYPE_LENGTH_DELIMITED) {
+    // Packed.
+    input._withLimit(input.readInt32(), () {
+      while (!input.isAtEnd()) {
+        readToList(list);
+      }
+    });
+  } else {
+    // Not packed.
+    readToList(list);
+  }
+}
+
+List<T> _sorted<T>(Iterable<T> list) => List.from(list)..sort();
+
+int getTagFieldNumber(int tag) => tag >> TAG_TYPE_BITS;
+
+int getTagWireType(int tag) => tag & TAG_TYPE_MASK;
+
+/// Returns true if the wireType can be merged into the given fieldType.
+bool _wireTypeMatches(int fieldType, int wireType) {
+  switch (PbFieldType.baseType(fieldType)) {
+    case PbFieldType.BOOL_BIT:
+    case PbFieldType.ENUM_BIT:
+    case PbFieldType.INT32_BIT:
+    case PbFieldType.INT64_BIT:
+    case PbFieldType.SINT32_BIT:
+    case PbFieldType.SINT64_BIT:
+    case PbFieldType.UINT32_BIT:
+    case PbFieldType.UINT64_BIT:
+      return wireType == WIRETYPE_VARINT ||
+          wireType == WIRETYPE_LENGTH_DELIMITED;
+    case PbFieldType.FLOAT_BIT:
+    case PbFieldType.FIXED32_BIT:
+    case PbFieldType.SFIXED32_BIT:
+      return wireType == WIRETYPE_FIXED32 ||
+          wireType == WIRETYPE_LENGTH_DELIMITED;
+    case PbFieldType.DOUBLE_BIT:
+    case PbFieldType.FIXED64_BIT:
+    case PbFieldType.SFIXED64_BIT:
+      return wireType == WIRETYPE_FIXED64 ||
+          wireType == WIRETYPE_LENGTH_DELIMITED;
+    case PbFieldType.BYTES_BIT:
+    case PbFieldType.STRING_BIT:
+    case PbFieldType.MESSAGE_BIT:
+      return wireType == WIRETYPE_LENGTH_DELIMITED;
+    case PbFieldType.GROUP_BIT:
+      return wireType == WIRETYPE_START_GROUP;
+    default:
+      return false;
+  }
+}
diff --git a/protobuf/lib/src/protobuf/json.dart b/protobuf_serializers/lib/src/json.dart
similarity index 93%
rename from protobuf/lib/src/protobuf/json.dart
rename to protobuf_serializers/lib/src/json.dart
index d48a1c8..ea409d9 100644
--- a/protobuf/lib/src/protobuf/json.dart
+++ b/protobuf_serializers/lib/src/json.dart
@@ -1,14 +1,14 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// Copyright (c) 2011, 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.
 
-part of protobuf;
+part of protobuf_serializers;
 
-Map<String, dynamic> _writeToJsonMap(FieldSet fs) {
+Map<String, dynamic> writeToJsonMap(FieldSet fs) {
   dynamic convertToMap(dynamic fieldValue, int fieldType) {
     var baseType = PbFieldType.baseType(fieldType);
 
-    if (_isRepeated(fieldType)) {
+    if (isRepeatedFieldType(fieldType)) {
       return List.from(fieldValue.map((e) => convertToMap(e, baseType)));
     }
 
@@ -56,7 +56,7 @@
     if (value == null || (value is List && value.isEmpty)) {
       continue; // It's missing, repeated, or an empty byte array.
     }
-    if (_isMapField(fi.type)) {
+    if (isMapFieldType(fi.type)) {
       result['${fi.tagNumber}'] =
           _writeMap(value, fi as MapFieldInfo<dynamic, dynamic>);
       continue;
@@ -65,7 +65,7 @@
   }
   if (fs.hasExtensions) {
     for (var tagNumber in _sorted(fs.extensions!.tagNumbers)) {
-      var value = fs.extensions!._values[tagNumber];
+      var value = fs.extensions!.values[tagNumber];
       if (value is List && value.isEmpty) {
         continue; // It's repeated or an empty byte array.
       }
@@ -78,7 +78,7 @@
 
 // Merge fields from a previously decoded JSON object.
 // (Called recursively on nested messages.)
-void _mergeFromJsonMap(
+void mergeFromJsonMap(
     FieldSet fs, Map<String, dynamic> json, ExtensionRegistry? registry) {
   final keys = json.keys;
   final info = fs.meta;
@@ -86,7 +86,7 @@
     var fi = info.byTagAsString[key];
     if (fi == null) {
       if (registry == null) continue; // Unknown tag; skip
-      fi = registry.getExtension(fs._messageName, int.parse(key));
+      fi = registry.getExtension(fs.messageName, int.parse(key));
       if (fi == null) continue; // Unknown tag; skip
     }
     if (fi.isMapField) {
@@ -102,7 +102,7 @@
 
 void _appendJsonList(BuilderInfo meta, FieldSet fs, List jsonList, FieldInfo fi,
     ExtensionRegistry? registry) {
-  final repeated = fi._ensureRepeatedField(meta, fs);
+  final repeated = fi.ensureRepeatedField(meta, fs);
   // Micro optimization. Using "for in" generates the following and iterator
   // alloc:
   //   for (t1 = J.get$iterator$ax(json), t2 = fi.tagNumber, t3 = fi.type,
@@ -122,7 +122,7 @@
 void _appendJsonMap(BuilderInfo meta, FieldSet fs, List jsonList,
     MapFieldInfo fi, ExtensionRegistry? registry) {
   final entryMeta = fi.mapEntryBuilderInfo;
-  final map = fi._ensureMapField(meta, fs) as PbMap<dynamic, dynamic>;
+  final map = fi.ensureMapField(meta, fs) as PbMap<dynamic, dynamic>;
   for (var jsonEntryDynamic in jsonList) {
     var jsonEntry = jsonEntryDynamic as Map<String, dynamic>;
     final entryFieldSet = FieldSet(null, entryMeta, null);
@@ -157,7 +157,7 @@
   // Therefore we run _validateField for debug builds only to validate
   // correctness of conversion.
   assert(() {
-    fs._validateField(fi, value);
+    fs.validateField(fi, value);
     return true;
   }());
   fs.setFieldUnchecked(meta, fi, value);
@@ -261,7 +261,7 @@
       if (value is Map) {
         final messageValue = value as Map<String, dynamic>;
         var subMessage = meta.makeEmptyMessage(tagNumber, registry);
-        _mergeFromJsonMap(subMessage.fieldSet, messageValue, registry);
+        mergeFromJsonMap(subMessage.fieldSet, messageValue, registry);
         return subMessage;
       }
       expectedType = 'nested message or group';