Как заставить protobuf-net и protostuff взаимно поддерживать унаследованные классы в.Net и Java?

Я делаю связь между программой на основе.Net в системе Windows и устройствами Android. На стороне.Net я использую замечательную программу Marcu Gravell protobuf-net, а на стороне Android я использую замечательную программу David Yu для создания прототипов.

Моя процедура (до сих пор) заключается в использовании классов.Net в качестве определяющих классов. Я использую метод protobuf-net Serializer.GetProto() для генерации файла.proto и программу protostuff-compiler protostuff для генерации Java-классов, более или менее соответствующих классам.Net.

Кажется, это работает довольно хорошо, за исключением того, что я столкнулся с проблемой наследования. Да, я знаю, наследование не должно работать с буферами протокола. Но и protobuf-net, и protostuff реализовали поддержку унаследованных классов, каждый по-своему.

Поэтому у меня вопрос: есть ли у кого-нибудь предложение о простом способе получения унаследованных классов C# для сопоставления с унаследованными классами Java и наоборот?

Вот пример того, с чем я работаю. Это классы C#:

   public /* abstract */ class ProgramInfoBase
   {
      private string _programName;

      private string _programVersion;

      [ProtoMember(1)]
      public string ProgramName
      {
         get { return _programName; }
         set { _programName = value; }
      }

      [ProtoMember(2)]
      public string ProgramVersion
      {
         get { return _programVersion; }
         set { _programVersion = value; }
      }
   }


   public class ProgramInfoAndroid : ProgramInfoBase
   {
      private string _androidDeviceName;


      [ProtoMember(1)]
      public string AndroidDeviceName
      {
         get { return _androidDeviceName; }
         set { _androidDeviceName = value; }
      }
   }


   public class ProgramInfoWindows : ProgramInfoBase
   {
      private string _windowsMachineName;

      [ProtoMember(1)]
      public string WindowsMachineName
      {
         get { return _windowsMachineName; }
         set { _windowsMachineName = value; }
      }
   }

А вот один из моих файлов.proto:

package Merlinia.MessagingDefinitions;

option java_package = "com.Merlinia.MMessaging_Test.protostuff";

message ProgramInfoAndroid {
   optional string AndroidDeviceName = 1;
}
message ProgramInfoBase {
   optional string ProgramName = 1;
   optional string ProgramVersion = 2;
   // the following represent sub-types; at most 1 should have a value
   optional ProgramInfoAndroid ProgramInfoAndroid = 1001;
   optional ProgramInfoWindows ProgramInfoWindows = 1002;
}
message ProgramInfoWindows {
   optional string WindowsMachineName = 1;
}

Выполнение этого через программу protostuff-compiler protostuff создает три отдельных Java-класса, что и следовало ожидать. Но я хотел бы, чтобы он генерировал соответствующее наследование классов C# для классов Java и для сериализации и десериализации между protobuf-net и protostuff для поддержки унаследованных классов на обоих концах.

РЕДАКТИРОВАТЬ:

Теперь я передумал. Пожалуйста, посмотрите следующий вопрос: Как получить protobuf-net, чтобы сгладить и разжать унаследованные классы в.Net?

1 ответ

Во-первых, обратите внимание, что полиморфизм не определен в спецификации protobuf; любые реализации на заказ. Было бы неплохо, если бы они были одинаковыми.

По сути, похоже, что они принимают принципиально иные парадигмы; Protobuf-net обрабатывает подтипы как вложенные объекты, начиная с базового типа и ниже, согласно опубликованному вами.proto (который, как я полагаю, пришел от GetProto, из-за знакомого комментария). Этот выбор был сделан по многим причинам, в том числе:

  • данные могут быть смоделированы и проанализированы реализациями, которые не имеют никакого понятия наследования
  • в данных нет зависимостей типа / имени / мета / и т. д. (чего-то, чего протобуф избегает везде, так что хорошо бы это сохранить)
  • что также помогает обеспечить полную мобильность между платформами
  • данные выживают рефакторинга типа
  • если приложения для чтения / записи находятся на разных уровнях, клиент все еще может читать данные, о которых он знает, даже если автор добавляет новый подкласс, о котором он не знает
  • работает с произвольными полями, слиянием и т. д. (если два слияния не находятся в разных ветвях наследования)

прототип, однако, делает вещи по-другому; Взглянув на репозиторий, он записывает имя типа в поле 127 (и ограничивает поля данных 126) и использует это имя для выполнения разрешения типа. Я предполагаю (не проверено), что это означает, что в терминах.proto схема, таким образом:

message ProgramInfoBase {
   optional string ProgramName = 1;
   optional string ProgramVersion = 2;
   required string MagicTypeName = 127;
}
message ProgramInfoWindows {
   optional string ProgramName = 1;
   optional string ProgramVersion = 2;
   optional string WindowsMachineName = 3;
   required string MagicTypeName = 127;
}
message ProgramInfoAndroid {
   optional string ProgramName = 1;
   optional string ProgramVersion = 2;
   optional string AndroidDeviceName = 3;
   required string MagicTypeName = 127;
}

Итак, на данный момент у вас есть несколько вариантов:

  • переделывать данные в обеих реализациях таким образом, чтобы вообще не использовать наследование - поэтому у вас есть плоская модель DTO; Вы, конечно, можете решить, что после десериализации вывести его обратно в иерархическую модель предметной области.
  • выберите одну модель (предположительно ту, в которой у вас уже есть данные), и продолжайте использовать наследование в этой реализации, и смоделируйте реализацию как DTO с другой стороны.

Например, если вы сохраняете код Java как полиморфный, для кода.NET потребуется что-то похожее на приведенное выше, с именем магического типа, но это может привести к путанице из-за конфликтов - например, если поле 1 было int Foo в одном подтипе, и string Bar в другом подтипе: плохие вещи; вам также нужно жестко кодировать / распознавать имена типов pojo. Не пытаясь взорвать свою собственную трубу, это именно тот тип вопросов, конфликтов и зависимостей имен, которые я упорно трудился, чтобы обойти в Protobuf-сети реализации

Если вы используете protobuf-net как полиморфный, то вы, вероятно, могли бы просто начать с.proto, который вы разместили, и просто проверить (после десериализации на стороне Java), является ли ProgramInfoAndroid или ProgramInfoWindows ненулевым; затем в зависимости от того, какое значение не равно NULL, сопоставьте его с одним из 3 различных типов доменов.

Другие вопросы по тегам