File: Operations\Operation.cs
Web Access
Project: src\aspnetcore\src\Features\JsonPatch.SystemTextJson\src\Microsoft.AspNetCore.JsonPatch.SystemTextJson.csproj (Microsoft.AspNetCore.JsonPatch.SystemTextJson)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.JsonPatch.SystemTextJson.Adapters;
using Microsoft.AspNetCore.JsonPatch.SystemTextJson;
using Microsoft.AspNetCore.Shared;
 
namespace Microsoft.AspNetCore.JsonPatch.SystemTextJson.Operations;
 
public class Operation : OperationBase
{
    internal struct ValueHolder : IEquatable<ValueHolder>
    {
        // While ShouldSerializeValue appears unread. Be careful if you remove it.
        // It decides whether or not the value property is serialized.
        // It's used by STJ for deciding the JsonIgnore (WhenWritingDefault)
        public bool ShouldSerializeValue { get; set; }
 
        public object Value { get; set; }
 
        public ValueHolder(bool shouldSerializeValue, object value)
        {
            ShouldSerializeValue = shouldSerializeValue;
            Value = value;
        }
 
        public bool Equals(ValueHolder other)
            => ShouldSerializeValue == other.ShouldSerializeValue && Equals(Value, other.Value);
 
        public override bool Equals([NotNullWhen(true)] object obj)
            => obj is ValueHolder other && Equals(other);
 
        public override int GetHashCode()
            => ShouldSerializeValue ? Value?.GetHashCode() ?? 0 : 0;
    }
 
    private sealed class ValueHolderConverter : JsonConverter<ValueHolder>
    {
        public override ValueHolder Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var value = JsonSerializer.Deserialize<object>(ref reader, options);
            return new ValueHolder(true, value);
        }
 
        public override void Write(Utf8JsonWriter writer, ValueHolder value, JsonSerializerOptions options)
            => JsonSerializer.Serialize(writer, value.Value, options);
    }
 
    [JsonIgnore]
    public object value { get; set; }
 
    [JsonPropertyName(nameof(value))]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    [JsonConverter(typeof(ValueHolderConverter))]
    [JsonInclude]
    internal ValueHolder Holder
    {
        get => OperationType is OperationType.Add or OperationType.Replace or OperationType.Test
              ? new ValueHolder(true, value)
              : default;
 
        set => this.value = value.Value;
    }
 
    public Operation()
    {
    }
 
    public Operation(string op, string path, string from, object value)
        : base(op, path, from)
    {
        this.value = value;
    }
 
    public Operation(string op, string path, string from)
        : base(op, path, from)
    {
    }
 
    public void Apply(object objectToApplyTo, IObjectAdapter adapter)
    {
        ArgumentNullThrowHelper.ThrowIfNull(objectToApplyTo);
        ArgumentNullThrowHelper.ThrowIfNull(adapter);
 
        switch (OperationType)
        {
            case OperationType.Add:
                adapter.Add(this, objectToApplyTo);
                break;
            case OperationType.Remove:
                adapter.Remove(this, objectToApplyTo);
                break;
            case OperationType.Replace:
                adapter.Replace(this, objectToApplyTo);
                break;
            case OperationType.Move:
                adapter.Move(this, objectToApplyTo);
                break;
            case OperationType.Copy:
                adapter.Copy(this, objectToApplyTo);
                break;
            case OperationType.Test:
                if (adapter is IObjectAdapterWithTest adapterWithTest)
                {
                    adapterWithTest.Test(this, objectToApplyTo);
                    break;
                }
                else
                {
                    throw new NotSupportedException(Resources.TestOperationNotSupported);
                }
            default:
                break;
        }
    }
}