/// <summary>
        /// Applies the PATH operation to the given Document.
        /// </summary>
        /// <param name="operation">The PATCH operation.</param>
        /// <param name="document">The Document.</param>
        /// <exception cref="System.ArgumentNullException"></exception>
        public static void Apply(this PatchOperationDto operation, Document document)
        {
            operation.Validate();

            if (document == null)
                throw new ArgumentNullException(nameof(document));

            var dictionary = document.AsDictionary();
            var path = new JsonPath(operation.path);

            operation.Apply(dictionary, path);
        }
        private static void Apply(this PatchOperationDto operation, object parentObject, JsonPath path)
        {
            if (parentObject == null)
                throw new ArgumentNullException(nameof(parentObject));
            if (path == null)
                throw new ArgumentNullException(nameof(path));
            if (path.Segments.Count < 1)
                throw new ArgumentException($"Invalid path: '{path}'");

            var currentSegment = path.Segments[0];
            if (parentObject is IList)
            {
                var array = parentObject as IList;
                var index = currentSegment.ToArrayIndex(array);

                if (path.HasNext())
                {
                    // We're not yet on the target location, continue traversing the path.
                    var nextPath = path.GetNext();
                    object childObject = index < array.Count ? array[index] : null;
                    operation.Apply(childObject, nextPath);
                }
                else
                {
                    // We are on the target location, perform the operation.
                    switch (operation.op)
                    {
                        case PatchOpCode.ADD:
                            array.Insert(index, operation.value);
                            break;

                        case PatchOpCode.REPLACE:
                            array[index] = operation.value;
                            break;

                        case PatchOpCode.REMOVE:
                            array.RemoveAt(index);
                            break;
                    }
                }
            }
            else if (parentObject is IDictionary<string, object>)
            {
                var dictionary = parentObject as IDictionary<string, object>;
                var fieldName = currentSegment;

                if (path.HasNext())
                {
                    // We're not yet on the target location, continue traversing the path.
                    var nextPath = path.GetNext();
                    object childObject = dictionary.ContainsKey(fieldName) ? dictionary[fieldName] : null;
                    operation.Apply(childObject, nextPath);
                }
                else
                {
                    // We are on the target location, perform the operation.
                    switch (operation.op)
                    {
                        case PatchOpCode.ADD:
                        case PatchOpCode.REPLACE:
                            dictionary[fieldName] = operation.value;
                            break;

                        case PatchOpCode.REMOVE:
                            dictionary.Remove(fieldName);
                            break;
                    }
                }
            }
        }