SystemTextJson.FluentApi
1.0.1
dotnet add package SystemTextJson.FluentApi --version 1.0.1
NuGet\Install-Package SystemTextJson.FluentApi -Version 1.0.1
<PackageReference Include="SystemTextJson.FluentApi" Version="1.0.1" />
paket add SystemTextJson.FluentApi --version 1.0.1
#r "nuget: SystemTextJson.FluentApi, 1.0.1"
// Install SystemTextJson.FluentApi as a Cake Addin #addin nuget:?package=SystemTextJson.FluentApi&version=1.0.1 // Install SystemTextJson.FluentApi as a Cake Tool #tool nuget:?package=SystemTextJson.FluentApi&version=1.0.1
WARNING: this package is under active development so the api may change at any time. Normally you do not need to use this package and just copy paste required functionality like NRT or polymorphism support.
SystemTextJson.FluentApi
SystemTextJson.FluentApi is a fluent configuration library for System.Text.Json that allows developers to configure serialization uses strongly typed fluent interface and lambda expression.
Documentation
All api usually repeats attributes from System.Text.Json.Serialization and set corresponding property in JsonPropertyInfo or JsonTypeInfo. Configuration based on IJsonTypeInfoResolver so developers can configure reflection based TypeInfoResolver and source generator JsonSerializerContext.
Quick start
To use FluentApi need to configure JsonSerializerOptions instance via JsonModelBuilder
and pass it to serializer.
var options = new JsonSerializerOptions() { WriteIndented = true };
options.ConfigureDefaultTypeResolver(p =>
p.Entity<Person>()
.Property(p => p.LastName).HasName("surname")
.Property(p => p.FirstName).IsIgnored()
.VirtualProperty("FullName", p => $"{p.FirstName} {p.LastName}")
.Property(p => p.Age).HasHumberHandling(JsonNumberHandling.WriteAsString));
var person = new Person() { FirstName = "First name", LastName = "Last name", Age = 12 };
var json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json);
This example produce this JSON
{
"surname": "Last name",
"Age": "12",
"FullName": "First name Last name"
}
Polymorphism serialization
STJ has build in support polymorphic serialization, but user have to annotate base class with JsonDerivedTypeAttribute with all derived types. In fluent API you can configure each derived type manually or find all derived types in runtime.
builder.Entity<Root>()
.HasDerivedType<Derived1>(nameof(Derived1))
.HasDerivedType<Derived2>(nameof(Derived2))
.HasDerivedType<Root>(nameof(Root))
// or
builder.Entity<Root>().HasDerivedTypesFromAssembly(Assembly.GetExecutingAssembly(), t => t.Name)
var testObject = new Root[]
{
new Derived1() { Derived1Property = "derived" },
new Derived2() { Derived2Property = "derived2" },
new Root(){ RootProperty = "root"}
};
public class Root
{
public string? RootProperty { get; set; }
}
public class Derived1 : Root
{
public string? Derived1Property { get; set; }
}
public class Derived2 : Root
{
public string? Derived2Property { get; set; }
}
Serialization of testObject
collection will produce:
[
{
"$type":"Derived1",
"Derived1Property":"derived",
"RootProperty":null
},
{
"$type":"Derived2",
"Derived2Property":"derived2",
"RootProperty":null
},
{
"$type":"Root",
"RootProperty":"root"
}
]
With $type
discriminator serializer are able to deserialize this collection. Another approach to serialization is use actual type from object instance, instead of property type. To achieve this behavior serializer can threat specific property as object
using PropertyBuilder.SerializeAsObject
.
builder.Entity<AsObjectTestClass>().Property(p => p.Data).SerializeAsObject();
var testObject = new AsObjectTestClass { Data = new Derived() { Property = "Prop" } };
public class AsObjectTestClass
{
public Root? Data { get; set; }
}
public class Root { }
public class Derived : Root
{
public string? Property { get; set; }
}
Serialization of testObject
will produce:
{
"Data":{
"Property":"Prop"
}
}
But in this case only serialization is available because JSON does not contain type discriminator and JsonException
will be thrown on deserialization.
Nullable reference type support
STJ has build in support of required
properties, but it just check, that value exists in JSON on deserialization and does not prevent setting null
to none nullable properties. Fluent Api can configure JsonSerializerOptions
to respect NRT annotations on fields and properties. Internally it uses JsonPropertyInfo.Set property and reduces deserialization performance.
builder.RespectNullableReferenceType();
JsonSerializer.Deserialize<TestClass>("""{"Property": null}""", _options); // this throws JsonException
JsonAsserts.AssertObject(new TestClass(), "{}", _options); // BUT this is not because Property is not requred.
public class TestClass
{
public string Property { get; set; }
}
Virtual properties
Fluent Api can define virtual properties, that does not match to any real property in object.
builder.Entity<Person>()
.Property(p => p.LastName).IsIgnored()
.Property(p => p.FirstName).IsIgnored()
.VirtualProperty("FullName", p => $"{p.FirstName} {p.LastName}")
var testObject = new Person() { FirstName = "First name", LastName = "Last name" };
class Person
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
}
Serialization of testObject
will produce:
{
"FullName": "First name Last name"
}
Change tracking
Fluent Api can track changes during serialization and deserialization. If some entity implement IHaveChnagedProperties
interface with not null ChangedProperties
property, it will be used to track changes. To populate property/field names that set deserialization use TrackChangedProperties()
method. To serialize properties only from ChangedProperties
use SerializeOnlyChangedProperties()
. This method will override JsonIgnoreCondition
.
builder.TrackChangedProperties().SerializeOnlyChangedProperties();
var testObject = new TrackTestClass()
{
StringProperty = "str",
IntProperty = 1,
ChangedProperties = { nameof(TrackTestClass.IntProperty) }
};
public class TrackTestClass : IHaveChangedProperties
{
public string? StringProperty { get; set; }
public int IntProperty { get; set; }
public ISet<string> ChangedProperties { get; } = new HashSet<string>();
}
Serialization of testObject
will produce:
{
"IntProperty": 1
}
And deserialization of this JSON will populate "IntProperty"
value to ChangedProperties
.
ValueTuple serialization
Fluent Api has ValueTupleJsonConverter
to serialize and deserialize ValueTuple
as array.
var options = new JsonSerializerOptions() { Converters = { new ValueTupleJsonConverter() } };
JsonSerializer.Serialize((1,"str"),options);
This code output:
[1,"str"]
Inline arrays support
Fluent Api has InlineArrayJsonConverter
for .NET 8 and above to serialize and deserialize InlineArray
structs as arrays.
var array = new InlineArray();
array[0] = null;
array[1] = 1;
array[2] = -1;
var options = new JsonSerializerOptions() { Converters = { new InlineArrayJsonConverter() } };
JsonSerializer.Serialize(array,options);
[InlineArray(3)]
private struct InlineArray
{
public int? Value;
}
Output: "[null,1,-1]"
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. 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 is compatible. 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. |
.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 is compatible. 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. |
-
.NETFramework 4.6.2
- System.Text.Json (>= 8.0.0)
-
.NETStandard 2.0
- System.Text.Json (>= 8.0.0)
-
net6.0
- System.Text.Json (>= 8.0.0)
-
net7.0
- System.Text.Json (>= 8.0.0)
-
net8.0
- System.Text.Json (>= 8.0.0)
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 |
---|---|---|
1.0.1 | 138 | 1/25/2024 |
1.0.0 | 183 | 11/17/2023 |
1.0.0-preview.1.0.4 | 70 | 11/8/2023 |
1.0.0-preview.1.0.3 | 70 | 11/5/2023 |
1.0.0-preview.1.0.2 | 67 | 11/5/2023 |
1.0.0-preview.1.0.1 | 70 | 11/4/2023 |