fieldToJson function

Object? fieldToJson(
  1. Object? value,
  2. String type, {
  3. String? typeName,
  4. Map<int, String>? enumValues,
  5. List<Map<String, Object?>>? messageDescriptor,
  6. Map<String, String>? features,
  7. AnyTypeResolver? resolver,
})

Converts a single protobuf field value to its Proto3 JSON representation.

Type-specific conversions:

  • TYPE_INT64, TYPE_UINT64, TYPE_SINT64, TYPE_SFIXED64, TYPE_FIXED64: encoded as JSON string (too large for JS number).
  • TYPE_BYTES: encoded as base64 string.
  • TYPE_FLOAT, TYPE_DOUBLE: NaN/Infinity/-Infinity as string literals.
  • TYPE_ENUM: encoded as string name using the enumValues map. Falls back to the integer if no name mapping is available.
  • TYPE_MESSAGE: recursively marshaled using messageDescriptor.
  • All other scalar types pass through unchanged.

enumValues is an optional Map<int, String> mapping enum ordinals to their Proto3 names. messageDescriptor is the nested field descriptor list for TYPE_MESSAGE fields.

Implementation

Object? fieldToJson(
  Object? value,
  String type, {
  String? typeName,
  Map<int, String>? enumValues,
  List<Map<String, Object?>>? messageDescriptor,
  Map<String, String>? features,
  AnyTypeResolver? resolver,
}) {
  if (value == null) return null;

  switch (type) {
    // Signed 64-bit integers: encode as a string per the Proto3 JSON spec.
    case 'TYPE_INT64':
    case 'TYPE_SINT64':
    case 'TYPE_SFIXED64':
      return value.toString();

    // Unsigned 64-bit: a Dart int holding a value >= 2^63 is stored as a
    // negative bit pattern, so render it as the unsigned decimal (string).
    case 'TYPE_UINT64':
    case 'TYPE_FIXED64':
      if (value is int && value < 0) {
        return (BigInt.from(value) + (BigInt.one << 64)).toString();
      }
      return value.toString();

    // bytes: base64 encode.
    case 'TYPE_BYTES':
      if (value is List<int>) {
        return base64.encode(value);
      }
      return value;

    // float / double: handle special IEEE 754 values.
    case 'TYPE_FLOAT':
    case 'TYPE_DOUBLE':
      if (value is double) {
        if (value.isNaN) return 'NaN';
        if (value.isInfinite) {
          return value.isNegative ? '-Infinity' : 'Infinity';
        }
      }
      return value;

    // enum: encode as string name.
    case 'TYPE_ENUM':
      if (enumValues != null && value is int) {
        return enumValues[value] ?? value;
      }
      return value;

    // message: recursive marshal (or a well-known-type JSON mapping).
    case 'TYPE_MESSAGE':
      if (typeName == 'google.protobuf.Any' &&
          (resolver ?? anyTypeResolver) != null) {
        return _anyToJson(value, features, resolver);
      }
      if (typeName != null && isWellKnownJsonType(typeName)) {
        return wktToJson(typeName, value, features, resolver);
      }
      if (messageDescriptor != null && value is Map<String, Object?>) {
        return _marshalToMap(value, messageDescriptor, resolver);
      }
      return value;

    // string: pass through, optionally verifying UTF-8 (utf8_validation=VERIFY).
    case 'TYPE_STRING':
      if (value is String) {
        _verifyUtf8IfRequired(value, features);
      }
      return value;

    // All other scalar types (int32, uint32, sint32, bool,
    // fixed32, sfixed32) pass through directly.
    default:
      return value;
  }
}