System.Text.Json’s has been greatly improved in .NET 7 through the addition of new performance-oriented features and the resolution of critical reliability and consistency issues. Contract customization provides you more control over how types are serialized or deserialized, polymorphic serialization for user-defined type hierarchies, necessary member support, and many other features are all introduced with the release of .NET 7.
Contract Customization
By creating a JSON contract for a given .NET type, System.Text.Json can decide how that type should be serialized and deserialized. The contract is derived either at runtime using reflection or at compile time using the source generator from the type’s shape, which includes the constructors, properties, and fields that are available as well as whether it implements IEnumerable or IDictionary. Users have historically had some limited control over the derived contract through the use of System.Text.Json attribute annotations, provided they have the ability to change the type declaration.
Previously utilized as an opaque token only in source generator APIs, JsonTypeInfo<T> now serves as a representation of the contract metadata for a specific type T. Most aspects of the JsonTypeInfo contract metadata have been made visible and editable for users as with .NET 7. By employing implementations of the IJsonTypeInfoResolver interface, contract customization enables users to create their own JSON contract resolution logic:
public interface IJsonTypeInfoResolver { JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options); }
For the specified Type and JsonSerializerOptions combination, a contract resolver returns a JsonTypeInfo object that has been configured. If the resolver does not support metadata for the designated input type, it may return null.
The DefaultJsonTypeInfoResolver class, which implements IJsonTypeInfoResolver, now exposes contract resolution done by the reflection-based default serializer. By using this class, users can add their own specific modifications to the standard reflection-based resolution or mix it with other resolvers (such as source-generated resolvers).
The JsonSerializerContext class, which is used to generate source code, has been implementing IJsonTypeInfoResolver since .NET 7. See How to use source generation in System for additional information on the source generator. Text.Json.
The new TypeInfoResolver field allows a custom resolver to be added to a JsonSerializerOptions instance:
// configure to use reflection contracts var reflectionOptions = new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }; // configure to use source generated contracts var sourceGenOptions = new JsonSerializerOptions { TypeInfoResolver = EntryContext.Default }; [JsonSerializable(typeof(MyPoco))] public partial class EntryContext : JsonSerializerContext { }
Type Hierarchies in .NET
The System.Text.Json. class, which is used to generate source code, has been implementing JsonDerivedTypeAttribute since.NET 7. See How to use source generation in System for additional information on the source generator. Text.Json.
The new TypeInfoResolver field allows a custom resolver to be added to a JsonSerializerOptions instance:
[JsonDerivedType(typeof(DerivedClass))] public class Temp { public int i { get; set; } } public class DerivedClass : Temp { public int j { get; set; } }
The aforementioned code example makes the Base class’s polymorphic serialization possible.
Base val = new DerivedClass(); JsonSerializer.Serialize(value);
DerivedClass is the run-time type listed above.
Required Members in .NET
The addition of support for needed members came with C# 11. This enables class creators to select which fields, properties, and reflection serialization should be filled in during instantiation. Here is a brief C# code example:
using System.Text.Json; JsonSerializer.Deserialize("""{"StudentCourse": 7}"""); public class Student { public required string StudentName { get; set; } public int NumberOfSubjects { get; set; } }
A JSON exception will be raised as a result of this since the necessary parameter (StudentName) was removed.
JsonSerializerOptions.Default
Developers can now pass and access a read-only instance of JsonSerializerOptions by making use of the JsonSerializerOptions. static default property Here is some code that exemplifies this:
public class Convert : JsonConverter { private readonly static JsonConverter s_default = (JsonConverter)JsonSerializerOptions.Default.GetConverter(typeof(int)); public override void Write(Utf8JsonWriter w, int val, JsonSerializerOptions o) { return w.WriteStringValue(val.ToString()); } public override int Read(ref Utf8JsonReader r, Type type, JsonSerializerOptions o) { return s_default.Read(ref r, t, o); } }
Performance upgrades
The quickest System is released with.NET 7. not yet Text.Json. Both the internal implementation and the user-facing APIs have undergone a number of performance-focused upgrades. for a thorough analysis of System. Please refer to the appropriate section of Stephen Toub’s article, Performance Improvements in.NET 7 for more information on the Text.Json performance improvements.
Closing
This year, we have concentrated on improving System.Text.Json’s extensibility, consistency, and reliability while also committing to yearly performance improvements.