google/protobuf.dart

Generated dart code for JSON wire format is too proprietary!

chipweinberger opened this issue · 2 comments

I hope this does not fall on deaf ears. Thanks for working on Dart support for protobufs!

When using JSON as the wire-format, why does the generated Dart code use so much protobuf specific code like GeneratedMessage, PbList, ExtensionRegistry, etc? JSON deserialization is already part of the Dart standard library, doesn't it seem kinda unnecessary to generate so much non standard code?

I understand JSON is probably not the focus of this protobuf library, but there are good use cases! For example, trying to remove the protobuf runtime dependency in Flutter Blue Plus. See: chipweinberger/flutter_blue_plus#236

It would be awesome if this was improved.

syntax = "proto3";
message User {
  int64 id = 1;
  string name = 2;
  string email = 3;
}
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: user.proto

import 'dart:core' show int, bool, double, String, List, override;
import 'package:protobuf/protobuf.dart';

class User extends GeneratedMessage {
  static final BuilderInfo _i = BuilderInfo('User')
    ..aInt64(1, 'id')
    ..aOS(2, 'name')
    ..aOS(3, 'email')
    ..hasRequiredFields = false;

  User() : super();
  User.fromBuffer(List<int> i,
      [ExtensionRegistry r = ExtensionRegistry.EMPTY])
      : super.fromBuffer(i, r);
  User.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY])
      : super.fromJson(i, r);
  User clone() => User()..mergeFromMessage(this);
  BuilderInfo get info_ => _i;
  static User create() => User();
  static PbList<User> createRepeated() => PbList<User>();
  static User getDefault() {
    if (_defaultInstance == null) _defaultInstance = User._();
    return _defaultInstance;
  }

  static User _defaultInstance;
  static void $checkItem(User v) {
    if (v is! User) checkItemFailed(v, 'User');
  }

  int get id => $_getI64(0);
  set id(int v) {
    $_setInt64(0, v);
  }

  bool hasId() => $_has(0);
  void clearId() => clearField(1);

  String get name => $_getS(1, '');
  set name(String v) {
    $_setString(1, v);
  }

  bool hasName() => $_has(1);
  void clearName() => clearField(2);

  String get email => $_getS(2, '');
  set email(String v) {
    $_setString(2, v);
  }

  bool hasEmail() => $_has(2);
  void clearEmail() => clearField(3);
}
osa1 commented

Reading your links, it seems like this is about getting rid of the runtime dependency to the protobuf library and not about JSON wire format, right?

Generated class for a message is not wire-format specific, it support the binary wire format, a custom JSON format, and the proto3 JSON format. Without a runtime dependency parsers for each of these formats would have to be generated, which would certianly be too much duplicate code generated when you have more than a few messages. Also, every message supports unknown fields and extensions, so the data structures for those are also shared with a shared runtime dependency. PbList and PbMap types are needed to allow freezing messages, which are also shared.

So generating stand-alone code with no deps other than the SDK would not be possible for this particular protobuf library.

Generated class for a message is not wire-format specific, it support the binary wire format, a custom JSON format, and the proto3 JSON format.

This is more complexity than we personally need. But of course I understand ours is just 1 use case.

The parser imo should just be the json parser, that already ships with dart.

And then a small class per message to read the parsed json and validate. No dependencies needed.

Handling unknown fields seems pretty simple, just provide access to the parsed json from dart:convert. I'm not familiar with freezing.

That said, since filing this issue I've learned about Pigeon, which seems more tailored for the Flutter Blue Plus use case.