/// <summary> /// Generates a JSON Pointer from a lambda expression. /// </summary> /// <typeparam name="T">The type of the object.</typeparam> /// <param name="expression">The lambda expression which gives the pointer path.</param> /// <returns>The JSON Pointer.</returns> /// <exception cref="NotSupportedException"> /// Thrown when the lambda expression contains a node that is not a property access or /// <see cref="int"/>-valued indexer. /// </exception> public static JsonPointer Create <T>(Expression <Func <T, object> > expression) { var body = expression.Body; var segments = new List <PointerSegment>(); while (body != null) { if (body.NodeType == ExpressionType.Convert && body is UnaryExpression unary) { body = unary.Operand; } if (body is MemberExpression me) { segments.Insert(0, PointerSegment.Create(me.Member.Name)); body = me.Expression; } else if (body is MethodCallExpression mce && mce.Method.Name.StartsWith("get_") && mce.Arguments.Count == 1 && mce.Arguments[0].Type == typeof(int)) { segments.Insert(0, PointerSegment.Create(mce.Arguments[0].ToString())); body = mce.Object; }
/// <summary> /// Concatenates additional segments onto the current pointer. /// </summary> /// <param name="additionalSegments">The additional segments.</param> /// <returns>A new pointer.</returns> public JsonPointer Combine(params PointerSegment[] additionalSegments) { var segments = new PointerSegment[Segments.Length + additionalSegments.Length]; Segments.CopyTo(segments, 0); additionalSegments.CopyTo(segments, Segments.Length); return(new JsonPointer { Segments = segments, Kind = IsUriEncoded ? JsonPointerKind.UriEncoded : JsonPointerKind.Plain }); }
/// <summary> /// Concatenates a pointer onto the current pointer. /// </summary> /// <param name="other">Another pointer.</param> /// <returns>A new pointer.</returns> public JsonPointer Combine(JsonPointer other) { var segments = new PointerSegment[Segments.Length + other.Segments.Length]; Segments.CopyTo(segments, 0); other.Segments.CopyTo(segments, Segments.Length); return(new JsonPointer { Segments = segments, Kind = IsUriEncoded ? JsonPointerKind.UriEncoded : JsonPointerKind.Plain }); }
/// <summary> /// Parses a JSON Pointer from a string. /// </summary> /// <param name="source">The source string.</param> /// <param name="pointerKind">(optional) Restricts the kind of pointer. <see cref="JsonPointerKind.Unspecified"/> (default) allows both.</param> /// <returns>A JSON Pointer.</returns> /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception> /// <exception cref="PointerParseException"><paramref name="source"/> does not contain a valid pointer or contains a pointer of the wrong kind.</exception> public static JsonPointer Parse(string source, JsonPointerKind pointerKind = JsonPointerKind.Unspecified) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (source == string.Empty) { return(Empty); } if (source == "#") { return(UrlEmpty); } bool isUriEncoded; var parts = source.Split('/'); var i = 0; if (parts[0] == "#" || parts[0] == string.Empty) { isUriEncoded = parts[0] == "#"; i++; } else { throw new PointerParseException("Pointer must start with either `#` or `/` or be empty"); } switch (pointerKind) { case JsonPointerKind.Unspecified: break; case JsonPointerKind.Plain: if (isUriEncoded) { throw new PointerParseException("Pointer is URI-encoded, but plain was expected."); } break; case JsonPointerKind.UriEncoded: if (!isUriEncoded) { throw new PointerParseException("Pointer is plain, but URI-encoded was expected."); } break; default: throw new ArgumentOutOfRangeException(nameof(pointerKind), pointerKind, null); } var segments = new PointerSegment[parts.Length - i]; for (; i < parts.Length; i++) { segments[i - 1] = PointerSegment.Parse(parts[i], isUriEncoded); } return(new JsonPointer { _source = source, _segments = segments, Kind = isUriEncoded ? JsonPointerKind.UriEncoded : JsonPointerKind.Plain }); }
/// <summary> /// Parses a JSON Pointer from a string. /// </summary> /// <param name="source">The source string.</param> /// <param name="pointer">The resulting pointer.</param> /// <param name="pointerKind">(optional) Restricts the kind of pointer. <see cref="JsonPointerKind.Unspecified"/> (default) allows both.</param> /// <returns><code>true</code> if the parse was successful; <code>false</code> otherwise.</returns> /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception> public static bool TryParse(string source, out JsonPointer pointer, JsonPointerKind pointerKind = JsonPointerKind.Unspecified) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (source == string.Empty) { pointer = Empty; return(true); } if (source == "#") { pointer = UrlEmpty; return(true); } bool isUriEncoded; var parts = source.Split('/'); var i = 0; if (parts[0] == "#" || parts[0] == string.Empty) { isUriEncoded = parts[0] == "#"; i++; } else { pointer = default; return(false); } switch (pointerKind) { case JsonPointerKind.Unspecified: break; case JsonPointerKind.Plain: if (isUriEncoded) { pointer = default; return(false); } break; case JsonPointerKind.UriEncoded: if (!isUriEncoded) { pointer = default; return(false); } break; default: throw new ArgumentOutOfRangeException(nameof(pointerKind), pointerKind, null); } var segments = new PointerSegment[parts.Length - i]; for (; i < parts.Length; i++) { var part = parts[i]; if (!PointerSegment.TryParse(part, isUriEncoded, out var segment)) { pointer = default; return(false); } segments[i - 1] = segment; } pointer = new JsonPointer { _source = source, _segments = segments, Kind = isUriEncoded ? JsonPointerKind.UriEncoded : JsonPointerKind.Plain }; return(true); }