Add direct ISourceNode.ToPoco() implementation#3472
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a direct ISourceNode -> POCO/PocoNode conversion path by extending NewPocoBuilder to traverse ISourceNode trees (in addition to ITypedElement) and updates tests/benchmarks to validate and measure the new approach.
Changes:
- Refactors
NewPocoBuilderinto shared + typed-element + source-node partials, and addsBuildFrom(ISourceNode, ...). - Updates
ISourceNodeconversion extensions to use the new direct builder path and addsISourceNode.ToPocoNode(...). - Adds unit tests for direct
ISourceNodeconversion behavior and expands benchmarks to compare direct vs bridged deserialization.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/Hl7.Fhir.Support.Poco.Tests/Model/SourceNodeToPocoTests.cs | Adds coverage for direct ISourceNode to POCO/PocoNode conversion, overflow behavior, and annotation preservation. |
| src/Hl7.Fhir.Shims.STU3AndUp/Serialization/JsonSerializationOptionsExtensions.cs | Cleanup of nullable directive/footer formatting. |
| src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/VersionedConversionExtensions.cs | Adds ISourceNode-scoped extension members for ToPoco() / ToPocoNode(). |
| src/Hl7.Fhir.Base/Serialization/engine/ElementModelSerializationEngine.cs | Switches to explicit LegacyPocoBuilder invocation to deserialize ISourceNode into a resource. |
| src/Hl7.Fhir.Base/ElementModel/SourceNodeExtensions.Conversions.cs | Implements direct ISourceNode.ToPoco(...) via NewPocoBuilder and adds ISourceNode.ToPocoNode(...). |
| src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.cs | Splits core builder into a partial class and adds BuildFrom(ISourceNode, ...) entry point. |
| src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.TypedElement.cs | Moves ITypedElement traversal/deserialization logic into a dedicated partial. |
| src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.SourceNode.cs | Adds direct ISourceNode traversal/deserialization logic including choice/type handling. |
| src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.Shared.cs | Extracts shared helpers (instance creation, list assignment, primitive normalization, enum validation). |
| src/Benchmarks/DeserializationBenchmark.cs | Adds direct-source-node benchmark scenarios and restructures setup/state. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: cknaap <2102470+cknaap@users.noreply.github.com> Agent-Logs-Url: https://github.com/FirelyTeam/firely-net-sdk/sessions/b997651a-e112-4381-89b3-179c44154d8a
Fix XmlReader field name collision in DeserializationBenchmark
…com/FirelyTeam/firely-net-sdk into feature/3471-sourcenode-to-poconode
|
@copilot : The build pipeline failed with a test failure. Can you try to fix that? The failure is: |
…pe abstract properties Co-authored-by: cknaap <2102470+cknaap@users.noreply.github.com> Agent-Logs-Url: https://github.com/FirelyTeam/firely-net-sdk/sessions/57c622ff-0ce8-407e-ac68-ad41df1a1948
Fixed in 4affcb4. The root cause was in The fix is to use |
| { | ||
| try | ||
| { | ||
| return (Resource)deserializer().ToPoco(inspector, null, pocoSettings); |
There was a problem hiding this comment.
I think the previous one would do the checks for the type returned and skip building if PocoNode?
There was a problem hiding this comment.
The readFromElement had way more comments in the original version documenting why certain choices are important for compatibility, would be a shame to lose them.
Description
This pull request introduces a direct conversion from
ISourceNodetoPoco. For this, it refactors the implementation of theNewPocoBuilderfor deserializing FHIR resources fromISourceNode(new) andITypedElement(existing) trees, and updates the deserialization benchmarks to use and compare this new approach. The changes improve the flexibility, maintainability, and correctness of POCO construction from various node types, and add more comprehensive benchmarks for deserialization strategies.Core NewPocoBuilder Implementation:
NewPocoBuilder, split intoNewPocoBuilder.Shared.cs,NewPocoBuilder.SourceNode.cs, andNewPocoBuilder.TypedElement.cs, providing robust logic for constructing POCOs from bothISourceNodeandITypedElementtrees, including dynamic type resolution, property assignment, and primitive normalization.classMappingForElement(NewPocoBuilder.SourceNode.cs) wherepropertyMapping.ImplementingTypewas used to look up the class mapping. For properties with an abstractImplementingTypebut a single concreteFhirType(e.g.Signature.whoin R4, typed asDataTypebut always holding aReference), the code incorrectly fell through toDynamicDataType. The fix usespropertyMapping.GetInstantiableType()instead, which resolves to the single concrete type when only one is allowed.Refactoring and API Improvements:
NewPocoBuilderfrom a single file to a partial class, encapsulating logic and making the constructor public for broader usability. Also removed unused usings and cleaned up the main file.Benchmark Enhancements:
DeserializationBenchmark.csto add new benchmark methods for the new POCO builder, comparing direct and bridge deserialization for both JSON and XML. Removed theXmlReaderclass field (which caused a name collision withSystem.Xml.XmlReader) and replaced it with a localusing vardeclaration insideXmlDictionaryDeserializer(), also ensuring proper disposal.General Code Quality:
These changes collectively modernize and enhance the deserialization pipeline, making it easier to extend and reason about, and providing a strong foundation for future improvements.
Related issues
Testing
Added unit tests and benchmark tests. The fix for
Signature.whoround-trip deserialization is covered by the existingRoundtripSignature.WorksWithTypedElementSerializerstest (16,233 R4 serialization tests pass).FirelyTeam Checklist
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.