Skip to content

Commit 3b44e3e

Browse files
CopilotScarletKuro
andcommitted
Add JsonDateTimeFormatAttribute to eliminate SYSLIB1223 warnings with source generators
Co-authored-by: ScarletKuro <19953225+ScarletKuro@users.noreply.github.com>
1 parent 977082b commit 3b44e3e

10 files changed

Lines changed: 348 additions & 54 deletions

AGENT.md

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This document provides guidance for AI agents and developers working on the **Sc
88

99
### Key Features
1010

11-
- Custom date/time format attributes (`JsonDateTimeConverterAttribute`)
11+
- Custom date/time format attributes (`JsonDateTimeConverterAttribute` for reflection, `JsonDateTimeFormatAttribute` for source generators)
1212
- Source generator-compatible format converters (`JsonDateTimeFormatConverter<T>`)
1313
- .NET 9+ contract customization resolver (`DateTimeConverterResolver`)
1414
- Support for `DateTime`, `DateTimeOffset`, and nullable variants
@@ -35,17 +35,20 @@ Scarlet.System.Text.Json.DateTimeConverter/
3535
│ │ ├── DateTimeConverterFactoryHelper.cs
3636
│ │ ├── DateTimeConverterResolver.cs # .NET 9+ contract customization
3737
│ │ ├── IJsonDateTimeFormat.cs
38-
│ │ ├── JsonDateTimeConverterAttribute.cs
38+
│ │ ├── JsonDateTimeConverterAttribute.cs # For reflection-based serialization
39+
│ │ ├── JsonDateTimeFormatAttribute.cs # For source generators (no warnings)
3940
│ │ ├── JsonDateTimeFormatConverter.cs
4041
│ │ └── Scarlet.System.Text.Json.DateTimeConverter.csproj
4142
│ └── Scarlet.System.Text.Json.DateTimeConverter.Tests/
4243
│ ├── JsonDateTimeConverterAttributeTests.cs
4344
│ ├── JsonDateTimeFormatConverterTests.cs
4445
│ ├── Model/ # Test models
4546
│ │ ├── ReflectionBasedModel.cs
46-
│ │ ├── SourceGeneratorModel.cs
47-
│ │ ├── SourceGeneratorWithResolverModel.cs
48-
│ │ └── TestModelSourceGeneratorJsonSerializerContext.cs
47+
│ │ ├── SourceGeneratorWithConverterModel.cs
48+
│ │ ├── SourceGeneratorWithResolverAttributeModel.cs # Uses JsonDateTimeConverter (has warnings)
49+
│ │ ├── SourceGeneratorWithResolverFormatModel.cs # Uses JsonDateTimeFormat (no warnings)
50+
│ │ ├── ConverterModelJsonSerializerContext.cs
51+
│ │ └── ResolverModelJsonSerializerContext.cs
4952
│ └── Scarlet.System.Text.Json.DateTimeConverter.Tests.csproj
5053
└── .github/
5154
└── workflows/ # CI/CD workflows
@@ -201,7 +204,16 @@ public DateTime Date { get; set; }
201204
```
202205
- Derives from `JsonConverterAttribute`
203206
- Works with reflection-based serialization
204-
- .NET 9+: Works with source generators via `DateTimeConverterResolver`
207+
- .NET 9+: Works with source generators via `DateTimeConverterResolver` but produces SYSLIB1223 warnings
208+
209+
**`JsonDateTimeFormatAttribute` (.NET 9+)**
210+
```csharp
211+
[JsonDateTimeFormat("yyyy-MM-dd")]
212+
public DateTime Date { get; set; }
213+
```
214+
- Derives from `Attribute` (not `JsonConverterAttribute`)
215+
- Designed for use with .NET 9+ source generators and `DateTimeConverterResolver`
216+
- **No SYSLIB1223 warnings** (recommended over `JsonDateTimeConverterAttribute` for source generators)
205217

206218
**`JsonDateTimeFormatConverter<T>`**
207219
```csharp
@@ -221,6 +233,7 @@ var options = new JsonSerializerOptions
221233
```
222234
- Implements `IJsonTypeInfoResolver` and extends `JsonSerializerContext`
223235
- Uses `JsonPropertyInfo.AttributeProvider` (populated by source generators in .NET 9+) to read attributes
236+
- Supports both `JsonDateTimeFormatAttribute` (no warnings) and `JsonDateTimeConverterAttribute` (backward compatibility)
224237
- Enables attribute syntax with source generators
225238

226239
#### 3. Factory Helper
@@ -248,11 +261,12 @@ public class DateTimeConverterResolver : JsonSerializerContext, IJsonTypeInfoRes
248261

249262
### Testing Strategy
250263

251-
Tests verify three distinct usage patterns:
264+
Tests verify four distinct usage patterns:
252265

253266
1. **Reflection-based** - Uses `JsonDateTimeConverterAttribute` with default `JsonSerializer`
254267
2. **Source generator with converter** - Uses `JsonDateTimeFormatConverter<T>` with `JsonSerializerContext`
255-
3. **Source generator with resolver** - Uses `JsonDateTimeConverterAttribute` + `DateTimeConverterResolver` (.NET 9+)
268+
3. **Source generator with resolver (new attribute)** - Uses `JsonDateTimeFormatAttribute` + `DateTimeConverterResolver` (.NET 9+, no warnings)
269+
4. **Source generator with resolver (old attribute)** - Uses `JsonDateTimeConverterAttribute` + `DateTimeConverterResolver` (.NET 9+, with SYSLIB1223 warnings for backward compatibility)
256270

257271
Each pattern is tested with:
258272
- Individual types (`DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?`)

README.md

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ A flexible and powerful library for customizing `DateTime` and `DateTimeOffset`
2424

2525
## Overview
2626

27-
This package provides three ways to specify custom date formats for `DateTime`, `DateTimeOffset`, and their nullable counterparts when serializing and deserializing JSON using `System.Text.Json`:
27+
This package provides four ways to specify custom date formats for `DateTime`, `DateTimeOffset`, and their nullable counterparts when serializing and deserializing JSON using `System.Text.Json`:
2828

29-
1. **`JsonDateTimeConverterAttribute`** - Simple attribute-based approach (reflection only, or .NET 9+ with resolver)
30-
2. **`JsonDateTimeFormatConverter<T>`** - Type-safe converter for source generators
31-
3. **`DateTimeConverterResolver`** - Contract customization for .NET 9+ source generators
29+
1. **`JsonDateTimeConverterAttribute`** - Simple attribute-based approach (reflection only, or .NET 9+ with resolver but produces warnings)
30+
2. **`JsonDateTimeFormatAttribute`** - Clean attribute for source generators with .NET 9+ resolver (no warnings)
31+
3. **`JsonDateTimeFormatConverter<T>`** - Type-safe converter for source generators (all .NET versions)
32+
4. **`DateTimeConverterResolver`** - Contract customization for .NET 9+ source generators
3233

3334
## Installation
3435

@@ -61,6 +62,26 @@ var json = JsonSerializer.Serialize(new MyModel { Date = DateTime.Now });
6162
// Output: {"Date":"2026-01-15"}
6263
```
6364

65+
**Best for source generators** (.NET 9+, no warnings):
66+
67+
```csharp
68+
public class MyModel
69+
{
70+
[JsonDateTimeFormat("yyyy-MM-dd")]
71+
public DateTime Date { get; set; }
72+
}
73+
74+
[JsonSerializable(typeof(MyModel))]
75+
public partial class MyJsonContext : JsonSerializerContext { }
76+
77+
var options = new JsonSerializerOptions
78+
{
79+
TypeInfoResolver = new DateTimeConverterResolver(MyJsonContext.Default)
80+
};
81+
var json = JsonSerializer.Serialize(new MyModel { Date = DateTime.Now }, options);
82+
// Output: {"Date":"2026-01-15"}
83+
```
84+
6485
## Usage Scenarios
6586

6687
### Reflection-Based Serialization (.NET 6+)
@@ -181,24 +202,28 @@ var deserializedOrder = (Order?)JsonSerializer.Deserialize(json, typeof(Order),
181202

182203
---
183204

184-
### Source Generator with Attribute and Resolver (.NET 9+)
205+
### Source Generator with Resolver (.NET 9+)
185206

186207
**.NET 9+** populates `JsonPropertyInfo.AttributeProvider` in source generators, enabling attribute-based syntax with `DateTimeConverterResolver`.
187208

209+
#### Option A: JsonDateTimeFormatAttribute (Recommended - No Warnings)
210+
211+
Use `JsonDateTimeFormatAttribute` for the cleanest experience without SYSLIB1223 warnings:
212+
188213
```csharp
189214
using Scarlet.System.Text.Json.DateTimeConverter;
190215
using System.Text.Json;
191216
using System.Text.Json.Serialization;
192217

193218
public class Order
194219
{
195-
[JsonDateTimeConverter("yyyy-MM-dd")]
220+
[JsonDateTimeFormat("yyyy-MM-dd")]
196221
public DateTime OrderDate { get; set; }
197222

198-
[JsonDateTimeConverter("yyyy-MM-ddTHH:mm:ss")]
223+
[JsonDateTimeFormat("yyyy-MM-ddTHH:mm:ss")]
199224
public DateTime? ProcessedDate { get; set; }
200225

201-
[JsonDateTimeConverter("yyyy-MM-ddTHH:mm:ss.fffZ")]
226+
[JsonDateTimeFormat("yyyy-MM-ddTHH:mm:ss.fffZ")]
202227
public DateTimeOffset ShippedAt { get; set; }
203228
}
204229

@@ -225,12 +250,35 @@ var deserializedOrder = (Order?)JsonSerializer.Deserialize(json, typeof(Order),
225250
**✅ Pros:**
226251
- Clean attribute syntax with source generators
227252
- AOT-friendly
253+
- **No SYSLIB1223 warnings**
228254
- Best of both worlds: readability + performance
229255

230256
**❌ Cons:**
231257
- **Requires .NET 9+**
232258
- Slightly more setup (need to wrap context with resolver)
233259

260+
#### Option B: JsonDateTimeConverterAttribute (Backward Compatible - Has Warnings)
261+
262+
You can also use `JsonDateTimeConverterAttribute` (for backward compatibility), but it will produce SYSLIB1223 warnings:
263+
264+
```csharp
265+
public class Order
266+
{
267+
[JsonDateTimeConverter("yyyy-MM-dd")] // ⚠️ Produces SYSLIB1223 warning
268+
public DateTime OrderDate { get; set; }
269+
}
270+
```
271+
272+
The resolver still works, but the source generator will emit warnings because `JsonDateTimeConverterAttribute` derives from `JsonConverterAttribute`.
273+
274+
**✅ Pros:**
275+
- Works with existing code using `JsonDateTimeConverterAttribute`
276+
- Backward compatible
277+
278+
**❌ Cons:**
279+
- Produces SYSLIB1223 warnings during build
280+
- May confuse users about the warnings
281+
234282
---
235283

236284
## Available Components
@@ -244,7 +292,20 @@ A `JsonConverterAttribute`-derived attribute for specifying date formats directl
244292
public DateTime Date { get; set; }
245293
```
246294

247-
**When to use:** Reflection-based serialization, or .NET 9+ with `DateTimeConverterResolver`.
295+
**When to use:** Reflection-based serialization. Can also be used with .NET 9+ `DateTimeConverterResolver` but produces SYSLIB1223 warnings.
296+
297+
---
298+
299+
### `JsonDateTimeFormatAttribute` (.NET 9+)
300+
301+
A simple `Attribute`-derived attribute for specifying date formats with source generators (no warnings).
302+
303+
```csharp
304+
[JsonDateTimeFormat("yyyy-MM-dd")]
305+
public DateTime Date { get; set; }
306+
```
307+
308+
**When to use:** .NET 9+ source generators with `DateTimeConverterResolver` (recommended, no warnings).
248309

249310
---
250311

@@ -268,14 +329,14 @@ public DateTime Date { get; set; }
268329

269330
### `DateTimeConverterResolver` (.NET 9+)
270331

271-
A `JsonSerializerContext` and `IJsonTypeInfoResolver` that enables `JsonDateTimeConverterAttribute` to work with source generators by using contract customization.
332+
A `JsonSerializerContext` and `IJsonTypeInfoResolver` that enables attribute-based date formatting with source generators by using contract customization.
272333

273334
```csharp
274335
var resolver = new DateTimeConverterResolver(MyJsonContext.Default);
275336
var options = new JsonSerializerOptions { TypeInfoResolver = resolver };
276337
```
277338

278-
**When to use:** .NET 9+ source generators with attribute syntax.
339+
**When to use:** .NET 9+ source generators with `JsonDateTimeFormatAttribute` or `JsonDateTimeConverterAttribute`.
279340

280341
---
281342

@@ -285,10 +346,11 @@ var options = new JsonSerializerOptions { TypeInfoResolver = resolver };
285346
|----------|---------------------|
286347
| Reflection-based, any .NET version | `JsonDateTimeConverterAttribute` |
287348
| Source generator, .NET 6-8 | `JsonDateTimeFormatConverter<T>` |
288-
| Source generator, .NET 9+ | `JsonDateTimeConverterAttribute` + `DateTimeConverterResolver` |
349+
| Source generator, .NET 9+ (no warnings) | `JsonDateTimeFormatAttribute` + `DateTimeConverterResolver` |
350+
| Source generator, .NET 9+ (backward compat) | `JsonDateTimeConverterAttribute` + `DateTimeConverterResolver` (⚠️ warnings) |
289351
| Need reusable formats across many properties | `JsonDateTimeFormatConverter<T>` (define format class once) |
290352
| Prototyping/simple projects | `JsonDateTimeConverterAttribute` (simplest) |
291-
| AOT compilation | `JsonDateTimeFormatConverter<T>` or .NET 9+ resolver |
353+
| AOT compilation | `JsonDateTimeFormatConverter<T>` or .NET 9+ resolver with attributes |
292354

293355
---
294356

@@ -300,7 +362,29 @@ var options = new JsonSerializerOptions { TypeInfoResolver = resolver };
300362

301363
> "Attributes deriving from JsonConverterAttribute are not supported by the source generator."
302364
303-
**Solution:** Use `JsonDateTimeFormatConverter<T>` instead, or upgrade to .NET 9+.
365+
**Solution:** Use `JsonDateTimeFormatConverter<T>` instead, or upgrade to .NET 9+ and use `JsonDateTimeFormatAttribute` with `DateTimeConverterResolver` (no warnings).
366+
367+
---
368+
369+
### SYSLIB1223 Warning with JsonDateTimeConverterAttribute (.NET 9+ Source Generators)
370+
371+
When using `JsonDateTimeConverterAttribute` with source generators in .NET 9+, you'll get SYSLIB1223 warnings:
372+
373+
> "Attributes deriving from JsonConverterAttribute are not supported by the source generator."
374+
375+
This happens because `JsonDateTimeConverterAttribute` derives from `JsonConverterAttribute`. The code still works with `DateTimeConverterResolver`, but the warnings may be confusing.
376+
377+
**Solution:** Use `JsonDateTimeFormatAttribute` instead, which derives only from `Attribute` and produces no warnings:
378+
379+
```csharp
380+
// ❌ Produces warnings (but still works)
381+
[JsonDateTimeConverter("yyyy-MM-dd")]
382+
public DateTime Date { get; set; }
383+
384+
// ✅ No warnings
385+
[JsonDateTimeFormat("yyyy-MM-dd")]
386+
public DateTime Date { get; set; }
387+
```
304388

305389
---
306390

0 commit comments

Comments
 (0)