Как заставить 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 различных типов доменов.