| File: Internal\ParsedPath.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.Collections.Generic; using Microsoft.AspNetCore.JsonPatch.SystemTextJson.Exceptions; using Microsoft.AspNetCore.Shared; namespace Microsoft.AspNetCore.JsonPatch.SystemTextJson.Internal; internal readonly struct ParsedPath { private readonly string[] _segments; public ParsedPath(string path) { ArgumentNullThrowHelper.ThrowIfNull(path); _segments = ParsePath(PathHelpers.NormalizePath(path)); } public string LastSegment { get { if (_segments == null || _segments.Length == 0) { return null; } return _segments[_segments.Length - 1]; } } public IReadOnlyList<string> Segments => _segments; private static string[] ParsePath(string path) { if (path.Length == 0) { return Array.Empty<string>(); } var span = path.AsSpan(); if (span[0] != '/') { // This shouldn't be reachable as the constructor enforces it. // But added to clarify and ensure that the Slice call below is always safe. throw new JsonPatchException(Resources.FormatInvalidValueForPath(path), null); } var strings = new string[span.Count('/')]; // When we have a path like "/a/b/c//d/e", the expectation is // to have the segments be ["a", "b", "c", "", "d", "e"]. // So, before splitting on "/", we want to slice off the leading "/". // Without this slice, we will always have an extra empty string at the beginning. span = span.Slice(1); int index = 0; foreach (var referenceTokenRange in span.Split('/')) { var referenceToken = span[referenceTokenRange].ToString(); strings[index++] = ValidateAndUnescapeReferenceToken(referenceToken); } return strings; } private static string ValidateAndUnescapeReferenceToken(string referenceToken) { var hasTilde = false; for (int i = 0; i < referenceToken.Length; i++) { if (referenceToken[i] == '~') { hasTilde = true; if (i + 1 >= referenceToken.Length || (referenceToken[i + 1] != '0' && referenceToken[i + 1] != '1')) { throw new JsonPatchException(Resources.FormatInvalidValueForPath(referenceToken), null); } } } return hasTilde ? referenceToken.Replace("~1", "/").Replace("~0", "~") : referenceToken; } }