static JsonDocumentBuilder UnflattenToObject(JsonElement value, IntegerTokenUnflattening options = IntegerTokenUnflattening.TryIndex) { if (value.ValueKind != JsonValueKind.Object) { throw new ArgumentException("The value to unflatten is not a JSON object"); } var result = new JsonDocumentBuilder(JsonValueKind.Object); foreach (var item in value.EnumerateObject()) { JsonDocumentBuilder part = result; JsonPointer ptr; if (!JsonPointer.TryParse(item.Name, out ptr)) { throw new ArgumentException("Name contains invalid JSON Pointer"); } var it = ptr.GetEnumerator(); bool more = it.MoveNext(); while (more) { var s = it.Current; more = it.MoveNext(); if (more) { JsonDocumentBuilder val; if (part.TryGetProperty(s, out val)) { part = val; } else { val = new JsonDocumentBuilder(JsonValueKind.Object); part.AddProperty(s,val); part = val; } } else { JsonDocumentBuilder val; if (part.TryGetProperty(s, out val)) { part = val; } else { val = new JsonDocumentBuilder(item.Value); part.AddProperty(s,val); part = val; } } } } return options == IntegerTokenUnflattening.TryIndex ? SafeUnflatten (result) : result; }
static void ApplyPatch(ref JsonDocumentBuilder target, JsonElement patch) { JsonElementEqualityComparer comparer = JsonElementEqualityComparer.Instance; Debug.Assert(target != null); if (patch.ValueKind != JsonValueKind.Array) { throw new ArgumentException("Patch must be an array"); } foreach (var operation in patch.EnumerateArray()) { JsonElement opElement; if (!operation.TryGetProperty("op", out opElement)) { throw new ArgumentException("Invalid patch"); } string op = opElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null"); JsonElement pathElement; if (!operation.TryGetProperty("path", out pathElement)) { throw new ArgumentException(op, "Invalid patch"); } string path = pathElement.GetString() ?? throw new InvalidOperationException("Operation cannot be null");; JsonPointer location; if (!JsonPointer.TryParse(path, out location)) { throw new ArgumentException(op, "Invalid patch"); } if (op == "test") { JsonElement value; if (!operation.TryGetProperty("value", out value)) { throw new ArgumentException(op, "Invalid patch"); } JsonDocumentBuilder tested; if (!location.TryGetValue(target, out tested)) { throw new ArgumentException(op, "Invalid patch"); } using (var doc = tested.ToJsonDocument()) { if (!comparer.Equals(doc.RootElement, value)) { throw new JsonPatchException(op, "Test failed"); } } } else if (op == "add") { JsonElement value; if (!operation.TryGetProperty("value", out value)) { throw new ArgumentException(op, "Invalid patch"); } var valueBuilder = new JsonDocumentBuilder(value); if (!location.TryAddIfAbsent(ref target, valueBuilder)) // try insert without replace { if (!location.TryReplace(ref target, valueBuilder)) // try insert without replace { throw new JsonPatchException(op, "Add failed"); } } } else if (op == "remove") { if (!location.TryRemove(ref target)) { throw new JsonPatchException(op, "Add failed"); } } else if (op == "replace") { JsonElement value; if (!operation.TryGetProperty("value", out value)) { throw new ArgumentException(op, "Invalid patch"); } var valueBuilder = new JsonDocumentBuilder(value); if (!location.TryReplace(ref target, valueBuilder)) { throw new JsonPatchException(op, "Replace failed"); } } else if (op == "move") { JsonElement fromElement; if (!operation.TryGetProperty("from", out fromElement)) { throw new ArgumentException(op, "Invalid patch"); } string from = fromElement.GetString() ?? throw new InvalidOperationException("From element cannot be null");; JsonPointer fromPointer; if (!JsonPointer.TryParse(from, out fromPointer)) { throw new ArgumentException(op, "Invalid patch"); } JsonDocumentBuilder value; if (!fromPointer.TryGetValue(target, out value)) { throw new JsonPatchException(op, "Move failed"); } if (!fromPointer.TryRemove(ref target)) { throw new JsonPatchException(op, "Move failed"); } if (!location.TryAddIfAbsent(ref target, value)) { if (!location.TryReplace(ref target, value)) // try insert without replace { throw new JsonPatchException(op, "Move failed"); } } } else if (op == "copy") { JsonElement fromElement; if (!operation.TryGetProperty("from", out fromElement)) { throw new ArgumentException(op, "Invalid patch"); } string from = fromElement.GetString() ?? throw new InvalidOperationException("from cannot be null"); JsonPointer fromPointer; if (!JsonPointer.TryParse(from, out fromPointer)) { throw new ArgumentException(op, "Invalid patch"); } JsonDocumentBuilder value; if (!fromPointer.TryGetValue(target, out value)) { throw new JsonPatchException(op, "Copy failed"); } if (!location.TryAddIfAbsent(ref target, value)) { if (!location.TryReplace(ref target, value)) // try insert without replace { throw new JsonPatchException(op, "Move failed"); } } } } }
static bool TryUnflattenArray(JsonElement value, out JsonDocumentBuilder result) { if (value.ValueKind != JsonValueKind.Object) { throw new ArgumentException("The value to unflatten is not a JSON object"); } result = new JsonDocumentBuilder(JsonValueKind.Object); foreach (var item in value.EnumerateObject()) { JsonDocumentBuilder? parent = null; JsonDocumentBuilder part = result; int parentIndex = 0; string parentName = ""; JsonPointer ptr; if (!JsonPointer.TryParse(item.Name, out ptr)) { throw new ArgumentException("Name contains invalid JSON Pointer"); } int index = 0; var it = ptr.GetEnumerator(); bool more = it.MoveNext(); while (more) { string token = it.Current; int n; if (int.TryParse(token, out n) && index++ == n) { if (part.ValueKind != JsonValueKind.Array) { if (parent != null && parent.ValueKind == JsonValueKind.Object) { parent.RemoveProperty(parentName); var val = new JsonDocumentBuilder(JsonValueKind.Array); parent.AddProperty(parentName, val); part = val; } else if (parent != null && parent.ValueKind == JsonValueKind.Array) { var val = new JsonDocumentBuilder(JsonValueKind.Array); parent[parentIndex] = val; part = val; } else { return false; } } parent = part; parentIndex = n; parentName = token; more = it.MoveNext(); if (more) { if (n >= part.GetArrayLength()) { part.AddArrayItem(new JsonDocumentBuilder(JsonValueKind.Object)); part = part[part.GetArrayLength() - 1]; } else { part = part[n]; } } else { part.AddArrayItem(new JsonDocumentBuilder(item.Value)); part = part[part.GetArrayLength() - 1]; } } else if (part.ValueKind == JsonValueKind.Object) { more = it.MoveNext(); if (more) { JsonDocumentBuilder val; if (part.TryGetProperty(token, out val)) { part = val; } else { val = new JsonDocumentBuilder(JsonValueKind.Object); part.AddProperty(token,val); part = val; } } else { JsonDocumentBuilder val; if (part.TryGetProperty(token, out val)) { part = val; } else { val = new JsonDocumentBuilder(item.Value); part.AddProperty(token,val); part = val; } } } else { return false; } } } return true; }