[Proto] Support polymorphic serialization for derived classes#63
[Proto] Support polymorphic serialization for derived classes#63kengwang wants to merge 7 commits into
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.
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 (
ProtoPolymorphicAttributeandProtoDerivedTypeAttribute) 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); |
There was a problem hiding this comment.
[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); |
|
|
||
| // get creator and fields, oh my reflection! | ||
| var polyObjectInfo = polyConverter.GetType() | ||
| .GetField("ObjectInfo",BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(polyConverter)!; |
There was a problem hiding this comment.
[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)!; |
| } | ||
| } | ||
|
|
||
| private void EmitFieldsInfo(SourceWriter source,Dictionary<int, ProtoFieldInfo> fields ) |
There was a problem hiding this comment.
[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 ) |
|
|
||
| } | ||
|
|
||
| private void PopulateFieldInfo(INamedTypeSymbol classSymbol, Dictionary<int, ProtoFieldInfo> fields, string identifier = "" ,CancellationToken token = default) |
There was a problem hiding this comment.
[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) |
Motivation
Since the
PbElemfield 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
Details
To-do
Lagrange.Proto.Generator