-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Background and Motivation
The existing JsonValue class has a factory method like:
public static JsonValue? Create<T>(T? value, JsonNodeOptions? options = null);When called with a primitive such as an Int32 or string this method is safe for trimming. However, this method also supports any serializable CLR type including custom data types, anonymous types and even POCOs and collections, so it is unsafe for trimming since it needs to call the serializer.
Separating the safe vs. unsafe creation is necessary to avoid having to add unsafe trimming attributes to this method.
See this issue for more information: #52002
Proposed API
namespace System.Text.Json.Nodes
{
public partial class JsonValue
{
// Here is the safe version that supports JsonTypeInfo with source-generated types:
+ public static JsonValue? Create<T>(T? value, JsonTypeInfo<T> jsonTypeInfo, JsonNodeOptions? options = null)
// Here is the unsafe version with a name that makes that clear:
- public static JsonValue? Create<T>(T? value, JsonNodeOptions? options = null)
+ public static JsonValue? CreateFromSerializableValue<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)]T>(T? value, JsonNodeOptions? options = null)
}
// To maintain compatibility with the removed `Create<T>`, here are safe overloads for each supported primitive value:
+ public static JsonValue Create(bool value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(byte value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(char value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(System.DateTime value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(System.DateTimeOffset value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(decimal value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(double value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(System.Guid value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(short value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(int value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(long value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(bool? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(byte? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(char? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(System.DateTimeOffset? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(System.DateTime? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(decimal? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(double? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(System.Guid? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(short? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(int? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(long? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ [System.CLSCompliantAttribute(false)]
+ public static JsonValue? Create(sbyte? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(float? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(System.Text.Json.JsonElement? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ [System.CLSCompliantAttribute(false)]
+ public static JsonValue? Create(ushort? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ [System.CLSCompliantAttribute(false)]
+ public static JsonValue? Create(uint? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ [System.CLSCompliantAttribute(false)]
+ public static JsonValue? Create(ulong? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ [System.CLSCompliantAttribute(false)]
+ public static JsonValue Create(sbyte value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue Create(float value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(string? value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ public static JsonValue? Create(System.Text.Json.JsonElement value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ [System.CLSCompliantAttribute(false)]
+ public static JsonValue Create(ushort value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ [System.CLSCompliantAttribute(false)]
+ public static JsonValue Create(uint value, JsonNodeOptions? options = default(JsonNodeOptions?));
+ [System.CLSCompliantAttribute(false)]
+ public static JsonValue Create(ulong value, JsonNodeOptions? options = default(JsonNodeOptions?));
}In addition, operators are added to support JsonElement which is the only supported type that did not have operators yet:
namespace System.Text.Json.Nodes
{
public partial class JsonNode
{
+ public static implicit operator JsonNode? (System.Text.Json.JsonElement value);
+ public static implicit operator JsonNode? (System.Text.Json.JsonElement? value);
+ public static explicit operator System.Text.Json.JsonElement (JsonNode value);
+ public static explicit operator System.Text.Json.JsonElement? (JsonNode? value);
}
}Note that JsonElement is handled specially: an internal value of JsonValueKind.Null is treated as a null JsonValue. This is required since JsonValue is based on JsonElement after a Parse() and treats null JSON values as a null CLR value.
Alternative Designs
Only supporting primitive types and not custom data types, anonymous types, etc would prevent the need for CreateFromSerializableValue() but we would still likely want the various new Create() overloads for each supported value type to make the expected types known at compile-time (instead of throwing at run-time). If we did not support custom data types, etc then users would need to:
- Use the serializer and create POCOs (not always feasible)
- Create helper methods to generate JSON from a custom data type, etc.
These may include invoking the non-trimmable serializer APIs and thus doesn't really help from a safe trimability perspective.