System.Text.Json.JsonSerializer.Serialize
is a set of overloads that serialize a C# object into json.
The non-generic overloads all share three parameters - object? value
which is the object to serialize; System.Text.Json.JsonSerializerOptions? options
, which allows configuring the serializer with respect to all kinds of choices, and Type inputType
, which is what this question is about.
inputType
is merely described as "The type of the value to convert." However, what does that actually mean and do? Is there a meaningful difference between typeof(object)
in this context and value.GetType()
?
I peeked into the code, but it quickly became clearly this isn't a simple matter; the type helps resolve a JsonTypeInfo, but e.g. typeof(object) is special-cased there.
I did a few quick-and dirty benchmarks:
using System.Security.Cryptography;
using System.Text.Json;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run<JsonBench>();
sealed record Bla(string Foo, int Bar);
public class JsonBench
{
readonly Bla value = new Bla("a", 2);
[Benchmark]
public string WithGenerics() => JsonSerializer.Serialize(value);
[Benchmark]
public string WithGetType() => JsonSerializer.Serialize(value, value.GetType());
[Benchmark]
public string WithObjectType() => JsonSerializer.Serialize(value, typeof(object));
readonly Type cachedObject = typeof(object), cachedBla = typeof(Bla);
[Benchmark]
public string WithCachedGetType() => JsonSerializer.Serialize(value, cachedBla);
[Benchmark]
public string WithCachedObjectType() => JsonSerializer.Serialize(value, cachedObject);
}
...and for small objects there appears to be very slight (on the order of 10ns) overhead from using typeof(object)
. Is that it? Are there corner cases where this is more? If using value.GetType()
is reliably faster... why does this choice even exist all?
In short: I'm not sure I understand the purpose of this Type inputType
parameter.
Can anybody clarify what this is actually for?
CodePudding user response:
As stated in the docs, the inputType
argument of JsonSerializer.Serialize(Object value, Type type, JsonSerializerOptions? options = default)
specifies:
The type of the
value
to convert.
Specifically, inputType
defines the type to use when determining the serialization contract for the incoming value:
typeof(object)
- The actual, concrete type of the incoming value is used.value.GetType()
- Once again the actual, concrete type of the incoming value is used.Some other
System.Type
thatvalue
inherits from or implements -- this type will be used instead of the actual, concrete type.
E.g., consider the following type hierarchy:
public class Base
{
public string BaseProperty { get; set; }
}
public class Derived : Base
{
public string DerivedProperty { get; set; }
}
Then if you serialize an instance of Derived
using typeof(Base)
:
var value = new Derived { BaseProperty = "hello", DerivedProperty = "there" };
var json = JsonSerializer.Serialize(value, typeof(Base));
Then the serializer will only serialize the properties of the specified base type Base
:
{
"BaseProperty" : "hello"
}
Demo here. You will get the same result if you do JsonSerializer.Serialize<Base>(value)
.
Since your sealed record Bla(string Foo, int Bar);
does not have any derived or base types other than typeof(object)
, your benchmarks can't surface the third case.[1]
This is discussed in How to serialize properties of derived classes with System.Text.Json:
In versions prior to .NET 7,
System.Text.Json
doesn't support the serialization of polymorphic type hierarchies. For example, if a property's type is an interface or an abstract class, only the properties defined on the interface or abstract class are serialized, even if the runtime type has additional properties. The exceptions to this behavior are explained in this section....
To serialize the properties of the derived type... use one of the following approaches:
Call an overload of Serialize that lets you specify the type at run time.
Declare the object to be serialized as
object
.
Now, in .NET 7 polymorphic type hierarchies are supported, however if you don't mark your types with JsonDerivedTypeAttribute
or use some other mechanism to inform the serializer of the expected derived types, the behavior falls back to the .NET 6 behavior.
Related questions:
- Why does System.Text Json Serialiser not serialise this generic property but Json.NET does?.
- Is polymorphic deserialization possible in System.Text.Json?.
[1] Eamon Nerbonne comments:
incidentally, that sealed record Bla does have base types, and those have the expected useless behavior when used here -
JsonSerializer(new Bla("",1), typeof(IEquatable<Bla>)) == "{}"
.