BrighterCommand / BrighterCommand/Brighter
Newtonsoft.Json compatibility broken on MessageHeader and Observability.Baggage
- Dominant language
- C#
- Stars
- 2.5k
- Forks
- 296
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 21
Description
### Describe the bug
Several Brighter types cannot be (de)serialised by `Newtonsoft.Json`. This breaks the otherwise-supported combination of Brighter + a Newtonsoft-based persistence stack (e.g. an app using the Cosmos SDK's `CosmosJsonNetSerializer` for the inbox/outbox).
Two distinct symptoms with the same root cause — Brighter only ships System.Text.Json infrastructure for shapes that need a custom converter:
**1. `MessageHeader` properties carry `[Newtonsoft.Json.JsonConverter]` attributes pointing at STJ converters**
`MessageHeader.Topic`, `MessageHeader.ReplyTo`, `MessageHeader.TraceParent`, and `MessageHeader.TraceState` are decorated with `[Newtonsoft.Json.JsonConverter(typeof(X))]` where the referenced converter type `X` inherits from `System.Text.Json.Serialization.JsonConverter`, not `Newtonsoft.Json.JsonConverter`. Newtonsoft tries to instantiate it as one of its own converters and throws `InvalidCastException`.
**2. `Paramore.Brighter.Observability.Baggage` has no Newtonsoft converter**
`Baggage` implements `IEnumerable>` but exposes `Add(string, string)` only — there is no `Add(KeyValuePair)`. Newtonsoft sees the `IEnumerable>` implementation, infers a list-shaped target, and fails to populate it because no matching `Add` overload exists. The same shape of problem affects `System.Net.Mime.ContentType` (its `Parameters` collection isn't Newtonsoft-friendly) wherever it appears on a Newtonsoft-traversed type.
### To Reproduce
**Symptom 1:**
```csharp
var json = JsonConvert.SerializeObject(new MessageHeader(
messageId: Id.Random(),
topic: new RoutingKey("test"),
messageType: MessageType.MT_EVENT));
// Throws InvalidCastException: Unable to cast object of type
// 'Paramore.Brighter.JsonConverters.' to type 'Newtonsoft.Json.JsonConverter'.
var roundtripped = JsonConvert.DeserializeObject(json);
```
Equivalent failure occurs whenever Newtonsoft walks the type — e.g. Cosmos SDK reading a `MessageHeader` out of an outbox/inbox document with `CosmosJsonNetSerializer`.
**Symptom 2:**
```csharp
var baggage = new Baggage();
baggage.Add("k", "v");
var json = JsonConvert.SerializeObject(baggage);
var roundtripped = JsonConvert.DeserializeObject(json);
// Fails — no matching Add overload for KeyValuePair
```
### Exceptions
Symptom 1:
```
System.InvalidCastException: Unable to cast object of type
'Paramore.Brighter.JsonConverters.' to type 'Newtonsoft.Json.JsonConverter'.
at Newtonsoft.Json.Serialization.JsonTypeReflector.CreateJsonConverterInstance(...)
at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateProperty(...)
```
### Suggested fix
For the `MessageHeader` attributes — either:
1. Drop the `[Newtonsoft.Json.JsonConverter(...)]` attributes (STJ-only annotation is fine; Newtonsoft consumers can configure their own converters globally), **or**
2. Ship a real `Newtonsoft.Json.JsonConverter` sibling for each affected property and reference it from the attribute.
For `Baggage` (and any sibling types with the same problem) — either:
1. Ship a `Newtonsoft.Json.JsonConverter` sibling alongside the STJ converter, matching the same wire shape (`[{"Key":"...","Value":"..."}]`), **or**
2. Expose a public mutable dictionary-shaped API on `Baggage` that general-purpose serialisers can populate without a custom converter.
Workaround currently in use downstream: a Newtonsoft `IContractResolver` that detects the misattributed properties and ignores them, plus hand-rolled `Newtonsoft.Json.JsonConverter` and `Newtonsoft.Json.JsonConverter` siblings.
### Further technical details
- Brighter version: **10.4.1**
- `dotnet --info`:
```
.NET SDK 10.0.108 (commit 94ea82652c, MSBuild 18.0.11+94ea82652)
Host 10.0.8 (x64)
RID: win-x64
```
- OS: Windows 11 (build 10.0.26200)
Contributor guide
Assessment
This issue has not been assessed yet.