|
| 1 | +# AGENT.md |
| 2 | + |
| 3 | +This document provides guidance for AI agents and developers working on the **Scarlet.System.Text.Json.DateTimeConverter** package. |
| 4 | + |
| 5 | +## Package Overview |
| 6 | + |
| 7 | +**Scarlet.System.Text.Json.DateTimeConverter** is a .NET library that provides flexible custom date/time formatting for `System.Text.Json` serialization. It supports both reflection-based and source generator approaches, with special .NET 9+ features for enhanced source generator compatibility. |
| 8 | + |
| 9 | +### Key Features |
| 10 | + |
| 11 | +- Custom date/time format attributes (`JsonDateTimeConverterAttribute`) |
| 12 | +- Source generator-compatible format converters (`JsonDateTimeFormatConverter<T>`) |
| 13 | +- .NET 9+ contract customization resolver (`DateTimeConverterResolver`) |
| 14 | +- Support for `DateTime`, `DateTimeOffset`, and nullable variants |
| 15 | +- Multi-target framework support (.NET 6, 7, 8, 9, 10) |
| 16 | + |
| 17 | +## Repository Structure |
| 18 | + |
| 19 | +``` |
| 20 | +Scarlet.System.Text.Json.DateTimeConverter/ |
| 21 | +├── README.md # User-facing documentation |
| 22 | +├── LICENSE # MIT License |
| 23 | +├── version.json # Nerdbank.GitVersioning configuration |
| 24 | +├── icon.png # Package icon |
| 25 | +├── build/ # NUKE build system |
| 26 | +│ ├── Build.cs # Main build configuration |
| 27 | +│ └── _build.csproj # Build project |
| 28 | +├── src/ |
| 29 | +│ ├── Scarlet.System.Text.Json.DateTimeConverter/ |
| 30 | +│ │ ├── Converters/ # Internal converter implementations |
| 31 | +│ │ │ ├── DateTimeConverter.cs |
| 32 | +│ │ │ ├── DateTimeNullableConverter.cs |
| 33 | +│ │ │ ├── DateTimeOffsetConverter.cs |
| 34 | +│ │ │ └── DateTimeOffsetNullableConverter.cs |
| 35 | +│ │ ├── DateTimeConverterFactoryHelper.cs |
| 36 | +│ │ ├── DateTimeConverterResolver.cs # .NET 9+ contract customization |
| 37 | +│ │ ├── IJsonDateTimeFormat.cs |
| 38 | +│ │ ├── JsonDateTimeConverterAttribute.cs |
| 39 | +│ │ ├── JsonDateTimeFormatConverter.cs |
| 40 | +│ │ └── Scarlet.System.Text.Json.DateTimeConverter.csproj |
| 41 | +│ └── Scarlet.System.Text.Json.DateTimeConverter.Tests/ |
| 42 | +│ ├── JsonDateTimeConverterAttributeTests.cs |
| 43 | +│ ├── JsonDateTimeFormatConverterTests.cs |
| 44 | +│ ├── Model/ # Test models |
| 45 | +│ │ ├── ReflectionBasedModel.cs |
| 46 | +│ │ ├── SourceGeneratorModel.cs |
| 47 | +│ │ ├── SourceGeneratorWithResolverModel.cs |
| 48 | +│ │ └── TestModelSourceGeneratorJsonSerializerContext.cs |
| 49 | +│ └── Scarlet.System.Text.Json.DateTimeConverter.Tests.csproj |
| 50 | +└── .github/ |
| 51 | + └── workflows/ # CI/CD workflows |
| 52 | + ├── continuous.yml # Build and test on push |
| 53 | + └── release.yml # Publish on tag |
| 54 | +``` |
| 55 | + |
| 56 | +## Building the Project |
| 57 | + |
| 58 | +### Prerequisites |
| 59 | + |
| 60 | +- .NET SDK 10.0 or later (for development) |
| 61 | +- Git (for version control) |
| 62 | + |
| 63 | +### Build Commands |
| 64 | + |
| 65 | +The project uses [NUKE](https://nuke.build/) for build automation. |
| 66 | + |
| 67 | +#### Quick Build |
| 68 | + |
| 69 | +```bash |
| 70 | +# Linux/macOS |
| 71 | +./build.sh Compile |
| 72 | + |
| 73 | +# Windows |
| 74 | +build.cmd Compile |
| 75 | +``` |
| 76 | + |
| 77 | +#### Clean Build |
| 78 | + |
| 79 | +```bash |
| 80 | +# Clean + Restore + Build |
| 81 | +./build.sh Clean Compile |
| 82 | + |
| 83 | +# Or use standard dotnet commands |
| 84 | +dotnet clean |
| 85 | +dotnet build src/Scarlet.System.Text.Json.DateTimeConverter.sln --configuration Release |
| 86 | +``` |
| 87 | + |
| 88 | +#### Full CI Build |
| 89 | + |
| 90 | +```bash |
| 91 | +./build.sh Clean Restore VerifyFormat Compile Test Pack |
| 92 | +``` |
| 93 | + |
| 94 | +### Build Targets |
| 95 | + |
| 96 | +| Target | Description | |
| 97 | +|--------|-------------| |
| 98 | +| `Clean` | Cleans build artifacts | |
| 99 | +| `Restore` | Restores NuGet packages | |
| 100 | +| `VerifyFormat` | Verifies code formatting (whitespace and style) | |
| 101 | +| `Compile` | Compiles the solution | |
| 102 | +| `Test` | Runs all tests | |
| 103 | +| `Pack` | Creates NuGet packages | |
| 104 | +| `Push` | Pushes packages to NuGet.org (requires tag + secrets) | |
| 105 | +| `PushGithubNuget` | Pushes packages to GitHub Packages (requires tag + secrets) | |
| 106 | + |
| 107 | +## Running Tests |
| 108 | + |
| 109 | +### Run All Tests |
| 110 | + |
| 111 | +```bash |
| 112 | +# Using NUKE |
| 113 | +./build.sh Test |
| 114 | + |
| 115 | +# Using dotnet CLI |
| 116 | +dotnet test src/Scarlet.System.Text.Json.DateTimeConverter.Tests/Scarlet.System.Text.Json.DateTimeConverter.Tests.csproj --configuration Release |
| 117 | +``` |
| 118 | + |
| 119 | +### Run Specific Test |
| 120 | + |
| 121 | +```bash |
| 122 | +dotnet test --filter "FullyQualifiedName~ReflectionBased_DateTime_WithAttribute" |
| 123 | +``` |
| 124 | + |
| 125 | +### Test Project Structure |
| 126 | + |
| 127 | +Tests are organized into two main test classes: |
| 128 | + |
| 129 | +1. **`JsonDateTimeConverterAttributeTests.cs`** |
| 130 | + - Tests for `JsonDateTimeConverterAttribute` with reflection-based serialization |
| 131 | + - Tests individual primitive types and complete models |
| 132 | + |
| 133 | +2. **`JsonDateTimeFormatConverterTests.cs`** |
| 134 | + - Tests for `JsonDateTimeFormatConverter<T>` with both reflection and source generators |
| 135 | + - Tests for `DateTimeConverterResolver` (.NET 9+) |
| 136 | + - Includes tests with null values |
| 137 | + |
| 138 | +### Test Naming Convention |
| 139 | + |
| 140 | +Tests follow the pattern: `[ApproachType]_[Scenario]_[OptionalDetails]` |
| 141 | + |
| 142 | +Examples: |
| 143 | +- `ReflectionBased_DateTime_WithAttribute` |
| 144 | +- `SourceGenerator_CompleteModel_WithFormatConverter` |
| 145 | +- `SourceGenerator_WithResolver_WithAttribute_UsingOptions` |
| 146 | + |
| 147 | +## Development Workflow |
| 148 | + |
| 149 | +### 1. Make Changes |
| 150 | + |
| 151 | +Edit source files in `src/Scarlet.System.Text.Json.DateTimeConverter/` |
| 152 | + |
| 153 | +### 2. Format Code |
| 154 | + |
| 155 | +The project enforces code formatting. Use: |
| 156 | + |
| 157 | +```bash |
| 158 | +# Check format |
| 159 | +dotnet format whitespace src/Scarlet.System.Text.Json.DateTimeConverter.sln --verify-no-changes |
| 160 | +dotnet format style src/Scarlet.System.Text.Json.DateTimeConverter.sln --verify-no-changes |
| 161 | + |
| 162 | +# Fix format |
| 163 | +dotnet format whitespace src/Scarlet.System.Text.Json.DateTimeConverter.sln |
| 164 | +dotnet format style src/Scarlet.System.Text.Json.DateTimeConverter.sln |
| 165 | +``` |
| 166 | + |
| 167 | +### 3. Build and Test |
| 168 | + |
| 169 | +```bash |
| 170 | +./build.sh Compile Test |
| 171 | +``` |
| 172 | + |
| 173 | +### 4. Create PR |
| 174 | + |
| 175 | +The project uses a standard GitHub workflow: |
| 176 | +1. Fork or create a branch |
| 177 | +2. Make changes |
| 178 | +3. Ensure tests pass |
| 179 | +4. Create pull request |
| 180 | + |
| 181 | +## Architecture |
| 182 | + |
| 183 | +### Core Components |
| 184 | + |
| 185 | +#### 1. Converters (Internal) |
| 186 | + |
| 187 | +Four internal converter classes handle actual JSON serialization/deserialization: |
| 188 | +- `DateTimeConverter` - for `DateTime` |
| 189 | +- `DateTimeNullableConverter` - for `DateTime?` |
| 190 | +- `DateTimeOffsetConverter` - for `DateTimeOffset` |
| 191 | +- `DateTimeOffsetNullableConverter` - for `DateTimeOffset?` |
| 192 | + |
| 193 | +All use `CultureInfo.InvariantCulture` for consistent formatting. |
| 194 | + |
| 195 | +#### 2. Public API |
| 196 | + |
| 197 | +**`JsonDateTimeConverterAttribute`** |
| 198 | +```csharp |
| 199 | +[JsonDateTimeConverter("yyyy-MM-dd")] |
| 200 | +public DateTime Date { get; set; } |
| 201 | +``` |
| 202 | +- Derives from `JsonConverterAttribute` |
| 203 | +- Works with reflection-based serialization |
| 204 | +- .NET 9+: Works with source generators via `DateTimeConverterResolver` |
| 205 | + |
| 206 | +**`JsonDateTimeFormatConverter<T>`** |
| 207 | +```csharp |
| 208 | +[JsonConverter(typeof(JsonDateTimeFormatConverter<MyFormat>))] |
| 209 | +public DateTime Date { get; set; } |
| 210 | +``` |
| 211 | +- `JsonConverterFactory` implementation |
| 212 | +- Compatible with source generators (all .NET versions) |
| 213 | +- Requires `IJsonDateTimeFormat` implementation |
| 214 | + |
| 215 | +**`DateTimeConverterResolver` (.NET 9+)** |
| 216 | +```csharp |
| 217 | +var options = new JsonSerializerOptions |
| 218 | +{ |
| 219 | + TypeInfoResolver = new DateTimeConverterResolver(MyJsonContext.Default) |
| 220 | +}; |
| 221 | +``` |
| 222 | +- Implements `IJsonTypeInfoResolver` and extends `JsonSerializerContext` |
| 223 | +- Uses `JsonPropertyInfo.AttributeProvider` to read attributes |
| 224 | +- Enables attribute syntax with source generators |
| 225 | + |
| 226 | +#### 3. Factory Helper |
| 227 | + |
| 228 | +`DateTimeConverterFactoryHelper` centralizes converter instantiation based on target type. |
| 229 | + |
| 230 | +### Multi-Targeting |
| 231 | + |
| 232 | +The library targets multiple frameworks to maximize compatibility: |
| 233 | + |
| 234 | +```xml |
| 235 | +<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks> |
| 236 | +``` |
| 237 | + |
| 238 | +`DateTimeConverterResolver` is conditionally compiled for .NET 9+ only: |
| 239 | + |
| 240 | +```csharp |
| 241 | +#if NET9_0_OR_GREATER |
| 242 | +public class DateTimeConverterResolver : JsonSerializerContext, IJsonTypeInfoResolver |
| 243 | +{ |
| 244 | + // Implementation |
| 245 | +} |
| 246 | +#endif |
| 247 | +``` |
| 248 | + |
| 249 | +### Testing Strategy |
| 250 | + |
| 251 | +Tests verify three distinct usage patterns: |
| 252 | + |
| 253 | +1. **Reflection-based** - Uses `JsonDateTimeConverterAttribute` with default `JsonSerializer` |
| 254 | +2. **Source generator with converter** - Uses `JsonDateTimeFormatConverter<T>` with `JsonSerializerContext` |
| 255 | +3. **Source generator with resolver** - Uses `JsonDateTimeConverterAttribute` + `DateTimeConverterResolver` (.NET 9+) |
| 256 | + |
| 257 | +Each pattern is tested with: |
| 258 | +- Individual types (`DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?`) |
| 259 | +- Complete models |
| 260 | +- Null value handling |
| 261 | + |
| 262 | +## Common Tasks |
| 263 | + |
| 264 | +### Adding a New Converter |
| 265 | + |
| 266 | +1. Create converter in `Converters/` directory |
| 267 | +2. Implement `JsonConverter<T>` with `Read` and `Write` methods |
| 268 | +3. Register in `DateTimeConverterFactoryHelper.CreateConverter` |
| 269 | +4. Add tests in both test files |
| 270 | +5. Update documentation |
| 271 | + |
| 272 | +### Updating Supported Frameworks |
| 273 | + |
| 274 | +1. Update `<TargetFrameworks>` in `.csproj` |
| 275 | +2. Test all scenarios on new framework |
| 276 | +3. Update `README.md` prerequisites |
| 277 | +4. Update CI/CD workflows if needed |
| 278 | + |
| 279 | +### Releasing a New Version |
| 280 | + |
| 281 | +Versioning is handled by [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning): |
| 282 | + |
| 283 | +1. Update `version.json` if needed |
| 284 | +2. Create a git tag: `git tag 1.2.0` |
| 285 | +3. Push tag: `git push origin 1.2.0` |
| 286 | +4. GitHub Actions will automatically build and publish |
| 287 | + |
| 288 | +## Troubleshooting |
| 289 | + |
| 290 | +### Build Issues |
| 291 | + |
| 292 | +**Problem:** "Shallow clone lacks the objects required to calculate version height" |
| 293 | + |
| 294 | +**Solution:** |
| 295 | +```bash |
| 296 | +git fetch --unshallow |
| 297 | +``` |
| 298 | + |
| 299 | +**Problem:** Format verification fails |
| 300 | + |
| 301 | +**Solution:** |
| 302 | +```bash |
| 303 | +dotnet format whitespace src/Scarlet.System.Text.Json.DateTimeConverter.sln |
| 304 | +dotnet format style src/Scarlet.System.Text.Json.DateTimeConverter.sln |
| 305 | +``` |
| 306 | + |
| 307 | +### Test Issues |
| 308 | + |
| 309 | +**Problem:** SYSLIB1223 warning in tests |
| 310 | + |
| 311 | +**Solution:** This is expected for `SourceGeneratorWithResolverModel` as it demonstrates the problem that `DateTimeConverterResolver` solves. |
| 312 | + |
| 313 | +**Problem:** Tests fail after renaming models |
| 314 | + |
| 315 | +**Solution:** Ensure all references are updated in both test files and `TestModelSourceGeneratorJsonSerializerContext.cs`. |
| 316 | + |
| 317 | +## CI/CD |
| 318 | + |
| 319 | +### Continuous Integration |
| 320 | + |
| 321 | +On every push: |
| 322 | +- Restore packages |
| 323 | +- Verify code format |
| 324 | +- Compile all target frameworks |
| 325 | +- Run tests |
| 326 | +- Create NuGet packages (artifacts) |
| 327 | + |
| 328 | +### Release |
| 329 | + |
| 330 | +On tag push (e.g., `1.2.0`): |
| 331 | +- All CI steps |
| 332 | +- Publish to NuGet.org |
| 333 | +- Publish to GitHub Packages |
| 334 | + |
| 335 | +### Secrets Required |
| 336 | + |
| 337 | +- `NUGET_KEY` - NuGet.org API key |
| 338 | +- `GITHUB_TOKEN` - Automatically provided by GitHub Actions |
| 339 | + |
| 340 | +## Code Style |
| 341 | + |
| 342 | +- Use C# 12+ features where appropriate |
| 343 | +- Nullable reference types enabled |
| 344 | +- `ImplicitUsings` enabled |
| 345 | +- Follow .editorconfig rules |
| 346 | +- Use XML documentation for public APIs |
| 347 | +- Keep internal converters simple and focused |
| 348 | + |
| 349 | +## Performance Considerations |
| 350 | + |
| 351 | +- Converters use `CultureInfo.InvariantCulture` for consistent, culture-independent formatting |
| 352 | +- No reflection in hot path (converters are created once and reused) |
| 353 | +- Source generator support ensures zero reflection overhead when using AOT |
| 354 | + |
| 355 | +## Additional Resources |
| 356 | + |
| 357 | +- [System.Text.Json Documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview) |
| 358 | +- [Custom Converters](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to) |
| 359 | +- [Source Generation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation) |
| 360 | +- [Contract Customization (.NET 9+)](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/custom-contracts) |
| 361 | +- [NUKE Build](https://nuke.build/) |
| 362 | +- [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) |
0 commit comments