// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.AspNet.JsonPatch.Adapters; using Microsoft.AspNet.JsonPatch.Converters; using Microsoft.AspNet.JsonPatch.Helpers; using Microsoft.AspNet.JsonPatch.Operations; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace Microsoft.AspNet.JsonPatch { // Implementation details: the purpose of this type of patch document is to allow creation of such // documents for cases where there's no class/DTO to work on. Typical use case: backend not built in // .NET or architecture doesn't contain a shared DTO layer. [JsonConverter(typeof(JsonPatchDocumentConverter))] public class JsonPatchDocument : IJsonPatchDocument { public List Operations { get; private set; } [JsonIgnore] public IContractResolver ContractResolver { get; set; } public JsonPatchDocument() { Operations = new List(); ContractResolver = new DefaultContractResolver(); } public JsonPatchDocument(List operations, IContractResolver contractResolver) { if (operations == null) { throw new ArgumentNullException(nameof(operations)); } if (contractResolver == null) { throw new ArgumentNullException(nameof(contractResolver)); } Operations = operations; ContractResolver = contractResolver; } /// /// Add operation. Will result in, for example, /// { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] } /// /// target location /// value /// public JsonPatchDocument Add(string path, object value) { if (path == null) { throw new ArgumentNullException(nameof(path)); } Operations.Add(new Operation("add", PathHelpers.NormalizePath(path), null, value)); return this; } /// /// Remove value at target location. Will result in, for example, /// { "op": "remove", "path": "/a/b/c" } /// /// target location /// public JsonPatchDocument Remove(string path) { if (path == null) { throw new ArgumentNullException(nameof(path)); } Operations.Add(new Operation("remove", PathHelpers.NormalizePath(path), null, null)); return this; } /// /// Replace value. Will result in, for example, /// { "op": "replace", "path": "/a/b/c", "value": 42 } /// /// target location /// value /// public JsonPatchDocument Replace(string path, object value) { if (path == null) { throw new ArgumentNullException(nameof(path)); } Operations.Add(new Operation("replace", PathHelpers.NormalizePath(path), null, value)); return this; } /// /// Removes value at specified location and add it to the target location. Will result in, for example: /// { "op": "move", "from": "/a/b/c", "path": "/a/b/d" } /// /// source location /// target location /// public JsonPatchDocument Move(string from, string path) { if (from == null) { throw new ArgumentNullException(nameof(from)); } if (path == null) { throw new ArgumentNullException(nameof(path)); } Operations.Add(new Operation("move", PathHelpers.NormalizePath(path), PathHelpers.NormalizePath(from))); return this; } /// /// Copy the value at specified location to the target location. Willr esult in, for example: /// { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" } /// /// source location /// target location /// public JsonPatchDocument Copy(string from, string path) { if (from == null) { throw new ArgumentNullException(nameof(from)); } if (path == null) { throw new ArgumentNullException(nameof(path)); } Operations.Add(new Operation("copy", PathHelpers.NormalizePath(path), PathHelpers.NormalizePath(from))); return this; } /// /// Apply this JsonPatchDocument /// /// Object to apply the JsonPatchDocument to public void ApplyTo(object objectToApplyTo) { if (objectToApplyTo == null) { throw new ArgumentNullException(nameof(objectToApplyTo)); } ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction: null)); } /// /// Apply this JsonPatchDocument /// /// Object to apply the JsonPatchDocument to /// Action to log errors public void ApplyTo(object objectToApplyTo, Action logErrorAction) { if (objectToApplyTo == null) { throw new ArgumentNullException(nameof(objectToApplyTo)); } ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction)); } /// /// Apply this JsonPatchDocument /// /// Object to apply the JsonPatchDocument to /// IObjectAdapter instance to use when applying public void ApplyTo(object objectToApplyTo, IObjectAdapter adapter) { if (objectToApplyTo == null) { throw new ArgumentNullException(nameof(objectToApplyTo)); } if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } // apply each operation in order foreach (var op in Operations) { op.Apply(objectToApplyTo, adapter); } } IList IJsonPatchDocument.GetOperations() { var allOps = new List(); if (Operations != null) { foreach (var op in Operations) { var untypedOp = new Operation(); untypedOp.op = op.op; untypedOp.value = op.value; untypedOp.path = op.path; untypedOp.from = op.from; allOps.Add(untypedOp); } } return allOps; } } }