fieldFromJson function

Object? fieldFromJson(
  1. Object? jsonValue,
  2. String type, {
  3. Map<int, String>? enumValues,
  4. List<Map<String, Object?>>? messageDescriptor,
  5. Map<String, String>? features,
})

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

Type-specific conversions:

  • TYPE_INT64, TYPE_UINT64, TYPE_SINT64, TYPE_SFIXED64, TYPE_FIXED64: string -> int.
  • TYPE_BYTES: base64 string -> List<int>.
  • TYPE_FLOAT, TYPE_DOUBLE: "NaN"/"Infinity"/"-Infinity" -> double.
  • TYPE_ENUM: string name -> ordinal using the reverse enumValues map.
  • TYPE_MESSAGE: recursively unmarshaled using messageDescriptor.
  • TYPE_INT32, TYPE_UINT32, etc.: numeric coercion from JSON number.
  • TYPE_BOOL: passed through.
  • TYPE_STRING: passed through.

enumValues maps ordinal -> name; this function reverses it to look up by name. messageDescriptor is the nested field descriptor list for TYPE_MESSAGE fields.

Implementation

Object? fieldFromJson(
  Object? jsonValue,
  String type, {
  Map<int, String>? enumValues,
  List<Map<String, Object?>>? messageDescriptor,
  Map<String, String>? features,
}) {
  if (jsonValue == null) return null;

  switch (type) {
    // 64-bit integers: JSON string -> int.
    case 'TYPE_INT64':
    case 'TYPE_UINT64':
    case 'TYPE_SINT64':
    case 'TYPE_SFIXED64':
    case 'TYPE_FIXED64':
      if (jsonValue is String) {
        return int.parse(jsonValue);
      }
      if (jsonValue is num) {
        return jsonValue.toInt();
      }
      return jsonValue;

    // bytes: base64 string -> List<int>.
    case 'TYPE_BYTES':
      if (jsonValue is String) {
        return base64.decode(jsonValue);
      }
      return jsonValue;

    // float / double: handle special string literals.
    case 'TYPE_FLOAT':
    case 'TYPE_DOUBLE':
      if (jsonValue is String) {
        switch (jsonValue) {
          case 'NaN':
            return double.nan;
          case 'Infinity':
            return double.infinity;
          case '-Infinity':
            return double.negativeInfinity;
          default:
            return double.parse(jsonValue);
        }
      }
      if (jsonValue is int) {
        return jsonValue.toDouble();
      }
      return jsonValue;

    // enum: string name -> ordinal.
    case 'TYPE_ENUM':
      if (jsonValue is String && enumValues != null) {
        // Reverse lookup: find the ordinal for this name.
        for (final entry in enumValues.entries) {
          if (entry.value == jsonValue) {
            return entry.key;
          }
        }
        // If the string is a numeric literal, parse it.
        final numeric = int.tryParse(jsonValue);
        if (numeric != null) return numeric;
        // Unknown enum name. Proto3 JSON (json_format = ALLOW, the editions
        // default) requires a known name or a numeric literal — reject. Under
        // LEGACY_BEST_EFFORT (proto2) or when features are absent, tolerate it
        // by passing the raw string through (legacy best-effort behavior).
        if (features != null && jsonFormatIsAllow(features)) {
          throw FormatException(
            'Unknown enum value "$jsonValue" (json_format=ALLOW requires a '
            'known name or numeric literal)',
          );
        }
        return jsonValue;
      }
      if (jsonValue is num) {
        return jsonValue.toInt();
      }
      return jsonValue;

    // message: recursive unmarshal.
    case 'TYPE_MESSAGE':
      if (messageDescriptor != null && jsonValue is Map) {
        final stringMap = <String, Object?>{};
        for (final entry in jsonValue.entries) {
          stringMap[entry.key.toString()] = entry.value;
        }
        return _unmarshalFromMap(stringMap, messageDescriptor);
      }
      return jsonValue;

    // 32-bit integers and fixed-width 32-bit: coerce from JSON number.
    case 'TYPE_INT32':
    case 'TYPE_UINT32':
    case 'TYPE_SINT32':
    case 'TYPE_FIXED32':
    case 'TYPE_SFIXED32':
      if (jsonValue is String) {
        return int.parse(jsonValue);
      }
      if (jsonValue is num) {
        return jsonValue.toInt();
      }
      return jsonValue;

    case 'TYPE_BOOL':
      if (jsonValue is bool) return jsonValue;
      if (jsonValue is String) return jsonValue == 'true';
      return jsonValue;

    case 'TYPE_STRING':
      final s = jsonValue is String ? jsonValue : jsonValue.toString();
      _verifyUtf8IfRequired(s, features);
      return s;

    default:
      return jsonValue;
  }
}