Cocoar.FluentJson
0.2.0-alpha.7
dotnet add package Cocoar.FluentJson --version 0.2.0-alpha.7
NuGet\Install-Package Cocoar.FluentJson -Version 0.2.0-alpha.7
<PackageReference Include="Cocoar.FluentJson" Version="0.2.0-alpha.7" />
<PackageVersion Include="Cocoar.FluentJson" Version="0.2.0-alpha.7" />
<PackageReference Include="Cocoar.FluentJson" />
paket add Cocoar.FluentJson --version 0.2.0-alpha.7
#r "nuget: Cocoar.FluentJson, 0.2.0-alpha.7"
#:package Cocoar.FluentJson@0.2.0-alpha.7
#addin nuget:?package=Cocoar.FluentJson&version=0.2.0-alpha.7&prerelease
#tool nuget:?package=Cocoar.FluentJson&version=0.2.0-alpha.7&prerelease
Cocoar.FluentJson
Cocoar.FluentJson is a C# source generator + runtime library that automatically generates JsonConverter<T> implementations for System.Text.Json based on fluent configuration rules. It lets you define custom JSON serialization and deserialization behavior without manually writing converter code.
Contributions are welcome! Feel free to open issues or pull requests for improvements or new features.
Features
- Source Generation: Automatically generates
JsonConverter<T>classes at compile-time - Fluent Configuration: Define JSON rules using a clean, fluent API
- Property Control: Rename, ignore, or apply custom logic to individual properties
- Custom Converters: Use separate read and write converters for properties
- Inline Logic: Define custom serialization logic directly in configuration
- Compile-time Validation: Catch configuration errors during build
Installation
Install the NuGet package:
dotnet add package Cocoar.FluentJson
Note: The single NuGet package contains:
- Runtime assembly (
Cocoar.FluentJson.dll) fornet8.0&net9.0- Shared contracts & source generator delivered via analyzer (
analyzers/dotnet/cs) No extra setup beyond installing the package.
How It Works
- Create a partial class that implements
IFluentJsonConverter<T> - Define fluent rules in the
CreateFluentRulesmethod - The source generator automatically creates a complete
JsonConverter<T>implementation - Register the converter with
JsonSerializerOptions
Getting Started
1. Define Your Model
Create your data model that needs custom JSON serialization:
public class NeededServiceConfig
{
public int LocalPort { get; set; }
public bool Direct { get; set; }
public string Destination { get; set; }
public string ServiceName { get; set; }
}
2. Implement IFluentJsonConverter<T>
Create a partial class that implements the interface:
public partial class NeededConfigConverter : IFluentJsonConverter<NeededServiceConfig>
{
public void CreateFluentRules(IFluentConverterRulesBuilder<NeededServiceConfig> rules)
{
rules
.ForProperty(x => x.Destination, x => x.Read((ref Utf8JsonReader r) =>
r.TokenType == JsonTokenType.Number ? $"localhost:{r.GetInt32()}" : r.GetString()!))
.ForProperty(x => x.Direct, x => x.Read((ref Utf8JsonReader reader) =>
reader.TokenType == JsonTokenType.String
? bool.Parse(reader.GetString()!)
: reader.GetBoolean()))
.Ignore(x => x.ServiceName);
}
}
3. Register and Use the Converter
var options = new JsonSerializerOptions
{
Converters = { new NeededConfigConverter() }
};
var json = """{"LocalPort": 1434, "Destination": 1433, "Direct": "true"}""";
var config = JsonSerializer.Deserialize<NeededServiceConfig>(json, options);
// config.Destination = "localhost:1433"
// config.Direct = true
// config.ServiceName = null (ignored)
API Reference
IFluentConverterRulesBuilder<T>
The fluent builder provides methods to configure JSON serialization rules:
ForProperty(propertyExpression, configure)
Configure rules for a specific property using the property configurator.
Ignore(propertyExpression)
Skip a property during both serialization and deserialization.
IPropertyConfigurator<T, TProperty>
Available configuration methods for individual properties:
Rename(string newName)
Change the JSON property name.
Ignore()
Skip this property (alternative to Ignore() on the builder).
UseReadConverter<TConverter>()
Use a custom JsonConverter<TProperty> only for reading (deserialization).
UseWriteConverter<TConverter>()
Use a custom JsonConverter<TProperty> only for writing (serialization).
Read(ReadDelegate<TProperty> inlineRead)
Define inline deserialization logic using a delegate.
Write(Action<Utf8JsonWriter, TProperty> inlineWrite)
Define inline serialization logic using an action.
Example Configurations
rules
.ForProperty(x => x.Id, x => x.Rename("identifier"))
.ForProperty(x => x.Value, x => x.UseReadConverter<CustomStringConverter>())
.ForProperty(x => x.Data, x => x.Read((ref Utf8JsonReader reader) =>
reader.TokenType == JsonTokenType.Number
? reader.GetInt32().ToString()
: reader.GetString()))
.Ignore(x => x.InternalProperty);
Generated Code
The source generator creates a partial implementation of your converter class that extends JsonConverter<T>. For example:
// Generated code (conceptual - actual implementation may vary)
public partial class NeededConfigConverter : JsonConverter<NeededServiceConfig>
{
public override NeededServiceConfig Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// Generated switch statement handling each property
// with custom logic, converters, and default behavior
}
public override void Write(Utf8JsonWriter writer, NeededServiceConfig value, JsonSerializerOptions options)
{
// Generated serialization logic respecting
// renames, ignores, and custom converters
}
// Generated helper methods for inline logic
private string Read_Destination(ref Utf8JsonReader reader) { /* custom logic */ }
}
Known Limitations
⚠️ Current limitations that may be addressed in future versions:
Complex Lambda Expressions: Inline
Read()andWrite()methods support basic lambda expressions, but complex multi-statement blocks may not parse correctly.Write Method Parameter Parsing: The inline
Write()lambda parameter detection may have edge cases with complex parameter patterns.Converter Validation: While the source generator validates that converters are not abstract, additional validation for proper inheritance could be improved.
Error Reporting: Diagnostic error messages could be more detailed and provide better guidance for fixing configuration issues.
Method Chaining Limitations: Very deep method chaining in configuration might not be fully supported by the syntax parser.
Generic Type Constraints: Custom converters with complex generic constraints may not be properly validated at compile time.
Technical Details
- Runtime Target Frameworks:
net8.0,net9.0 - Generator & Shared Contracts:
netstandard2.0(broad IDE / compiler compatibility) - Key Dependencies:
- Microsoft.CodeAnalysis.CSharp 4.12.0 (analyzer/generator only)
- System.Text.Json 9.0.0
- Package Layout:
lib/net8.0|net9.0: runtime + shared DLLanalyzers/dotnet/cs: generator + shared DLL (for compile-time)
- Debugging: Attach to a build with generator debug launch profile (see
Properties/launchSettings.json).
Why Cocoar.FluentJson?
Cocoar.FluentJson saves time and effort by:
- Centralizing JSON rules.
- Reducing boilerplate code.
- Providing type-safe compile-time validation.
It’s especially useful in projects with complex models and custom serialization requirements.
Contributing
Contributions are welcome! Feel free to open issues or pull requests for improvements or new features.
License
This project is licensed under the MIT License.
Links
- NuGet: Cocoar.FluentJson
- GitHub: Cocoar.FluentJson
- Documentation: Getting Started
- Docs: Options | Converter | Advanced | Limitations | Roadmap | Changelog
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.2.0-alpha.7 | 161 | 9/24/2025 |
| 0.2.0-alpha.6 | 151 | 9/24/2025 |
Initial beta release providing fluent JsonSerializerOptions builder and source generator for custom converters.