| File: System\Text\Json\Nodes\JsonValue.cs | Web Access |
| Project: src\runtime\src\libraries\System.Text.Json\src\System.Text.Json.csproj (System.Text.Json) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Nodes { /// <summary> /// Represents a mutable JSON value. /// </summary> public abstract partial class JsonValue : JsonNode { internal const string CreateUnreferencedCodeMessage = "Creating JsonValue instances with non-primitive types is not compatible with trimming. It can result in non-primitive types being serialized, which may have their members trimmed."; internal const string CreateDynamicCodeMessage = "Creating JsonValue instances with non-primitive types requires generating code at runtime."; private protected JsonValue(JsonNodeOptions? options) : base(options) { } /// <summary> /// Tries to obtain the current JSON value and returns a value that indicates whether the operation succeeded. /// </summary> /// <remarks> /// {T} can be the type or base type of the underlying value. /// If the underlying value is a <see cref="JsonElement"/> then {T} can also be the type of any primitive /// value supported by current <see cref="JsonElement"/>. /// Specifying the <see cref="object"/> type for {T} will always succeed and return the underlying value as <see cref="object"/>.<br /> /// The underlying value of a <see cref="JsonValue"/> after deserialization is an instance of <see cref="JsonElement"/>, /// otherwise it's the value specified when the <see cref="JsonValue"/> was created. /// </remarks> /// <seealso cref="JsonNode.GetValue{T}"></seealso> /// <typeparam name="T">The type of value to obtain.</typeparam> /// <param name="value">When this method returns, contains the parsed value.</param> /// <returns><see langword="true"/> if the value can be successfully obtained; otherwise, <see langword="false"/>.</returns> public abstract bool TryGetValue<T>([NotNullWhen(true)] out T? value); /// <summary> /// Initializes a new instance of the <see cref="JsonValue"/> class that contains the specified value. /// </summary> /// <returns> /// The new instance of the <see cref="JsonValue"/> class that contains the specified value. /// </returns> /// <typeparam name="T">The type of value to create.</typeparam> /// <param name="value">The value to create.</param> /// <param name="options">Options to control the behavior.</param> /// <returns>The new instance of the <see cref="JsonValue"/> class that contains the specified value.</returns> [RequiresUnreferencedCode(CreateUnreferencedCodeMessage + " Use the overload that takes a JsonTypeInfo, or make sure all of the required types are preserved.")] [RequiresDynamicCode(CreateDynamicCodeMessage)] public static JsonValue? Create<T>(T? value, JsonNodeOptions? options = null) { if (value is null) { return null; } if (value is JsonNode) { ThrowHelper.ThrowArgumentException_NodeValueNotAllowed(nameof(value)); } if (value is JsonElement element) { return CreateFromElement(ref element, options); } var jsonTypeInfo = JsonSerializerOptions.Default.GetTypeInfo<T>(); return CreateFromTypeInfo(value, jsonTypeInfo, options); } /// <summary> /// Initializes a new instance of the <see cref="JsonValue"/> class that contains the specified value. /// </summary> /// <returns> /// The new instance of the <see cref="JsonValue"/> class that contains the specified value. /// </returns> /// <typeparam name="T">The type of value to create.</typeparam> /// <param name="value">The value to create.</param> /// <param name="jsonTypeInfo">The <see cref="JsonTypeInfo"/> that will be used to serialize the value.</param> /// <param name="options">Options to control the behavior.</param> /// <returns>The new instance of the <see cref="JsonValue"/> class that contains the specified value.</returns> public static JsonValue? Create<T>(T? value, JsonTypeInfo<T> jsonTypeInfo, JsonNodeOptions? options = null) { ArgumentNullException.ThrowIfNull(jsonTypeInfo); if (value is null) { return null; } if (value is JsonNode) { ThrowHelper.ThrowArgumentException_NodeValueNotAllowed(nameof(value)); } jsonTypeInfo.EnsureConfigured(); if (value is JsonElement element && jsonTypeInfo.EffectiveConverter.IsInternalConverter) { return CreateFromElement(ref element, options); } return CreateFromTypeInfo(value, jsonTypeInfo, options); } internal override bool DeepEqualsCore(JsonNode otherNode) { if (GetValueKind() != otherNode.GetValueKind()) { return false; } // Fall back to slow path that converts the nodes to JsonElement. JsonElement thisElement = ToJsonElement(this, out JsonDocument? thisDocument); JsonElement otherElement = ToJsonElement(otherNode, out JsonDocument? otherDocument); try { return JsonElement.DeepEquals(thisElement, otherElement); } finally { thisDocument?.Dispose(); otherDocument?.Dispose(); } static JsonElement ToJsonElement(JsonNode node, out JsonDocument? backingDocument) { if (node.UnderlyingElement is { } element) { backingDocument = null; return element; } Utf8JsonWriter writer = Utf8JsonWriterCache.RentWriterAndBuffer( options: default, JsonSerializerOptions.BufferSizeDefault, out PooledByteBufferWriter output); try { node.WriteTo(writer); writer.Flush(); Utf8JsonReader reader = new(output.WrittenSpan); backingDocument = JsonDocument.ParseValue(ref reader); return backingDocument.RootElement; } finally { Utf8JsonWriterCache.ReturnWriterAndBuffer(writer, output); } } } internal sealed override void GetPath(ref ValueStringBuilder path, JsonNode? child) { Debug.Assert(child == null); Parent?.GetPath(ref path, this); } internal static JsonValue CreateFromTypeInfo<T>(T value, JsonTypeInfo<T> jsonTypeInfo, JsonNodeOptions? options = null) { Debug.Assert(jsonTypeInfo.IsConfigured); Debug.Assert(value != null); if (JsonValue<T>.TypeIsSupportedPrimitive && jsonTypeInfo is { EffectiveConverter.IsInternalConverter: true } && (jsonTypeInfo.EffectiveNumberHandling & JsonNumberHandling.WriteAsString) is 0) { // If the type is using the built-in converter for a known primitive, // switch to the more efficient JsonValuePrimitive<T> implementation. return new JsonValuePrimitive<T>(value, jsonTypeInfo.EffectiveConverter, options); } return new JsonValueCustomized<T>(value, jsonTypeInfo, options); } internal static JsonValue? CreateFromElement(ref readonly JsonElement element, JsonNodeOptions? options = null) { switch (element.ValueKind) { case JsonValueKind.Null: return null; case JsonValueKind.Object or JsonValueKind.Array: // Force usage of JsonArray and JsonObject instead of supporting those in an JsonValue. ThrowHelper.ThrowInvalidOperationException_NodeElementCannotBeObjectOrArray(); return null; default: return new JsonValueOfElement(element, options); } } } }