Skip to content

Modernize dependency injection and JSON serialization#368

Draft
niemyjski wants to merge 9 commits into
mainfrom
feature/modern-di-stj
Draft

Modernize dependency injection and JSON serialization#368
niemyjski wants to merge 9 commits into
mainfrom
feature/modern-di-stj

Conversation

@niemyjski

Copy link
Copy Markdown
Member

Summary

  • replace the vendored Newtonsoft.Json fork with System.Text.Json
  • replace TinyIoC with Microsoft.Extensions.DependencyInjection while preserving the existing dependency resolver API
  • add source-generated serialization metadata and trimming annotations required for NativeAOT
  • preserve legacy public API compatibility shims and existing Exceptionless payload behavior
  • add regression coverage for exclusions, depth limits, cycles, raw data, public fields, MessagePack storage, hosting integration, packed-package AOT, and stack frame identity

Why

The client depended on a vendored legacy serializer and TinyIoC. Neither the original combination nor changing only the serializer or DI container produced a working NativeAOT client. The hardened combination of System.Text.Json, Microsoft DI, source-generated metadata, and trimming-safe activation now passes NativeAOT smoke tests.

Impact

  • Existing public API compatibility is preserved according to ApiCompat for netstandard2.0 consumers.
  • Custom payload serialization now follows System.Text.Json semantics. Consumers relying on Newtonsoft-specific attributes or DataContract behavior should migrate payloads to System.Text.Json attributes or register source-generated contexts.
  • Microsoft.Extensions.DependencyInjection becomes a transitive dependency. System.Text.Json 10 is used on net462, netstandard2.0, and net8.0.
  • The DI container remains internal to each Exceptionless client; registrations from an application's root provider are not imported automatically.
  • The package gains modern target assets and NativeAOT support. The package is larger overall because it contains more TFMs, while the individual netstandard2.0 assembly is substantially smaller after removing the vendored Newtonsoft source.
  • This should ship as a major version because serializer behavior, dependency graph, target assets, and stack trace implementation change even though the API compatibility check is clean.

Validation

  • Release build: 0 warnings, 0 errors
  • Exceptionless.Tests: 324 passed, 17 skipped
  • Exceptionless.MessagePack.Tests: 11 passed, 1 skipped
  • focused serializer regressions: 42 passed
  • packed-package NativeAOT smoke: net8.0 and net10.0 passed
  • Generic Host NativeAOT smoke: net8.0 and net10.0 passed
  • ApiCompat: clean for previous netstandard2.0 API against current netstandard2.0 and net8.0
  • Windows cross-build and forced net462/net472 compilation: 0 warnings, 0 errors
  • Windows-shaped package contains net462, netstandard2.0, net8.0, and net10.0
  • workflow YAML parsing and git diff --check: passed

Remaining release gates

  • run Linux, macOS, and Windows CI from this PR
  • validate net462/net472 runtime loading and binding on a real Windows runner
  • consider requiring the platform build checks in branch protection; currently only license/cla is required

niemyjski and others added 9 commits July 12, 2026 13:31
- Remove vendored Newtonsoft.Json (entire src/Exceptionless/Newtonsoft.Json/ directory)
- Remove update-json.ps1 script
- Add System.Text.Json 10.0.0 NuGet package reference
- Rewrite DefaultJsonSerializer using System.Text.Json with:
  - SnakeCaseNamingPolicy matching legacy Newtonsoft behavior (e.g. OSName -> o_s_name)
  - Per-type snake_case applied only to Exceptionless.Models namespace
  - DataDictionaryConverter for storing complex values as JSON strings
  - SettingsDictionaryConverter for ObservableDictionary-based type
  - ObjectToInferredTypesConverter for proper type inference
  - PostDataConverter to convert object/array PostData to indented strings
  - Custom WriteValue with depth limiting and property exclusion support
- Update all model classes to remove [JsonObject] attributes
- Add [JsonPropertyName] for EnvironmentInfo OS properties
- Update DefaultSubmissionClient to use JsonDocument instead of JObject
- All 300 tests pass

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sion bug

- Replace custom SnakeCaseNamingPolicy with built-in JsonNamingPolicy.SnakeCaseLower
  (aligns with server approach in exceptionless/Exceptionless#2135)
- Delete unused SnakeCaseNamingPolicy.cs
- Fix DataDictionaryConverter.Write: use WriteRawValue for JSON strings that
  were previously objects (fixes double-escaping on storage roundtrip)
- Fix exclusion logic: add TypeInfoResolver = new DefaultJsonTypeInfoResolver()
  so GetTypeInfo() works and WriteValue can filter properties by name
- Update all test assertions to expect snake_case property names

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WriteValue for IDictionary entries wrote the property name before checking
whether the value could actually be serialized at the current depth. When a
complex value exceeded maxDepth, WriteValue returned without writing anything,
leaving the JSON writer in an invalid state. The error was silently swallowed
by continueOnSerializationError, causing a fallback to full serialization
(effectively ignoring the depth limit entirely).

Fix: check depth before writing the property name. Skip complex dictionary
entries that would exceed maxDepth, consistent with the object property path.

Added regression test that fails before the fix (depth limit violated) and
passes after.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- preserve literal JSON-looking strings in DataDictionary as strings
- restore raw JSON emission only for values produced from structured data
- preserve raw JSON markers through MessagePack storage roundtrips
- coerce primitive SettingsDictionary JSON values to strings like main
- keep dictionary depth-limit regression coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant