-
Notifications
You must be signed in to change notification settings - Fork 22
[Proto] Support polymorphic serialization for derived classes #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…ved classes Inspired by STJ's JsonDerivedType & JsonPolymorphic
Allow [Base,Derived]Serialize × [Base,Derived]Deserialize (4 cases in total)
Currently, it doesn't support multi-level inheritance. still improving. Try to go to the root type first and then (un)wrap the protobuf type discriminator step by step, going down to the current type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements polymorphic serialization support for protobuf-style serialization in the Lagrange.Proto library. The implementation allows base classes to define derived type mappings using attributes, enabling automatic serialization/deserialization of the correct derived type based on a discriminator field.
- Added polymorphic attribute system (
ProtoPolymorphicAttribute
andProtoDerivedTypeAttribute
) for defining type hierarchies - Implemented runtime polymorphic type resolution during serialization and deserialization
- Extended the source generator to emit polymorphic-aware serialization code
Reviewed Changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
ThrowHelper.cs | Added error handling methods for polymorphic serialization scenarios |
ProtoSerializer.Serialize.cs | Enhanced serialization logic to handle polymorphic types with discriminator fields |
ProtoSerializer.Helpers.cs | Added helper methods for dynamic converter creation and reflection-based type info |
ProtoSerializer.Deserialize.cs | Extended deserialization to support polymorphic type reconstruction |
ProtoTypeResolver.Dynamic.cs | Added polymorphic metadata population and multi-level inheritance support |
ProtoPolymorphicInfo.cs | New metadata classes for managing polymorphic type information |
ProtoObjectInfo.cs | Added polymorphic info property to object metadata |
ProtoPolymorphicAttribute.cs | New attribute for configuring polymorphic behavior |
ProtoDerivedTypeAttribute.cs | New attribute for registering derived types with discriminators |
ProtoPolymorphismTest.cs | Comprehensive test suite covering various polymorphic scenarios |
Program.cs | Added example classes demonstrating polymorphic usage |
ProtoSourceGenerator.Parser.cs | Enhanced parser to handle polymorphic attributes and inheritance |
ProtoSourceGenerator.Emitter.TypeInfo.cs | Extended code generation for polymorphic type descriptors |
ProtoSourceGenerator.Emitter.Serialize.cs | Added polymorphic-aware serialization code generation |
PolymorphicTypeInfo.cs | New entity classes for representing polymorphic metadata |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
ProtoTypeResolver.Register(converter = new ProtoObjectConverter<T>()); | ||
// has polymorphic type | ||
var index = polymorphicInfo.PolymorphicIndicateIndex; | ||
var fieldInfo = fields.FirstOrDefault(t=>t.Value.Field == index); |
Copilot
AI
Oct 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Missing space around the lambda arrow operator. Should be t => t.Value.Field == index
for consistency with coding standards.
var fieldInfo = fields.FirstOrDefault(t=>t.Value.Field == index); | |
var fieldInfo = fields.FirstOrDefault(t => t.Value.Field == index); |
Copilot uses AI. Check for mistakes.
|
||
// get creator and fields, oh my reflection! | ||
var polyObjectInfo = polyConverter.GetType() | ||
.GetField("ObjectInfo",BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(polyConverter)!; |
Copilot
AI
Oct 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Missing space after the comma in BindingFlags parameter. Should be BindingFlags.NonPublic | BindingFlags.Instance
with proper spacing.
.GetField("ObjectInfo",BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(polyConverter)!; | |
.GetField("ObjectInfo", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(polyConverter)!; |
Copilot uses AI. Check for mistakes.
} | ||
} | ||
|
||
private void EmitFieldsInfo(SourceWriter source,Dictionary<int, ProtoFieldInfo> fields ) |
Copilot
AI
Oct 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Missing space after comma in parameter list. Should be SourceWriter source, Dictionary<int, ProtoFieldInfo> fields
.
private void EmitFieldsInfo(SourceWriter source,Dictionary<int, ProtoFieldInfo> fields ) | |
private void EmitFieldsInfo(SourceWriter source, Dictionary<int, ProtoFieldInfo> fields ) |
Copilot uses AI. Check for mistakes.
|
||
} | ||
|
||
private void PopulateFieldInfo(INamedTypeSymbol classSymbol, Dictionary<int, ProtoFieldInfo> fields, string identifier = "" ,CancellationToken token = default) |
Copilot
AI
Oct 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Extra space before comma in parameter list. Should be string identifier = \"\", CancellationToken token = default)
.
private void PopulateFieldInfo(INamedTypeSymbol classSymbol, Dictionary<int, ProtoFieldInfo> fields, string identifier = "" ,CancellationToken token = default) | |
private void PopulateFieldInfo(INamedTypeSymbol classSymbol, Dictionary<int, ProtoFieldInfo> fields, string identifier = "", CancellationToken token = default) |
Copilot uses AI. Check for mistakes.
Motivation
Since the
PbElem
field ofCommonElem(53)
contains actual data, the ServiceType field is used to distinguish the type of data contained therein.The TLVs also have this design model style.
It is hoped that the data structure can reflect this model design, making it easier to convert during serialization and deserialization.
Current
(Not Implemented CommonElem in V2)
In
Lagrange.Core
, directly read to a byte array and re-serialize.Reference
Reference STJ's JsonDerivedTypeAttribute and JsonPolymorphicAttribute structures to achieve polymorphism.
Example
See Lagrange.Proto.Test/ProtoPolymorphismTest.cs
To-do
Lagrange.Proto.Generator