diff --git a/src/Neo.Json/JNumber.cs b/src/Neo.Json/JNumber.cs index 1ae5d6d0a2..5b142feb41 100644 --- a/src/Neo.Json/JNumber.cs +++ b/src/Neo.Json/JNumber.cs @@ -96,11 +96,12 @@ public override T GetEnum(bool ignoreCase = false) } catch (OverflowException) { - throw new InvalidCastException(); + throw new InvalidCastException($"The value is out of range for the enum {enumType.FullName}"); } + object result = Enum.ToObject(enumType, value); if (!Enum.IsDefined(enumType, result)) - throw new InvalidCastException(); + throw new InvalidCastException($"The value is not defined in the enum {enumType.FullName}"); return (T)result; } diff --git a/src/Neo.Json/JPathToken.cs b/src/Neo.Json/JPathToken.cs index 40054a1fb0..dce574c0e8 100644 --- a/src/Neo.Json/JPathToken.cs +++ b/src/Neo.Json/JPathToken.cs @@ -63,7 +63,7 @@ public static IEnumerable Parse(string expr) i += token.Content.Length - 1; break; default: - throw new FormatException(); + throw new FormatException($"Invalid character '{expr[i]}' at position {i}"); } yield return token; } @@ -78,7 +78,7 @@ private static string ParseString(string expr, int start) end++; if (c == '\'') return expr[start..end]; } - throw new FormatException(); + throw new FormatException("Unterminated string"); } public static string ParseIdentifier(string expr, int start) @@ -112,7 +112,7 @@ private static string ParseNumber(string expr, int start) private static JPathToken DequeueToken(Queue tokens) { if (!tokens.TryDequeue(out JPathToken? token)) - throw new FormatException(); + throw new FormatException("Unexpected end of expression"); return token; } @@ -132,7 +132,7 @@ public static void ProcessJsonPath(ref JToken?[] objects, Queue toke ProcessBracket(ref objects, ref maxDepth, maxObjects, tokens); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type}"); } } } @@ -152,7 +152,7 @@ private static void ProcessDot(ref JToken?[] objects, ref int maxDepth, int maxO Descent(ref objects, ref maxDepth, maxObjects, token.Content!); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type}"); } } @@ -162,8 +162,9 @@ private static void ProcessBracket(ref JToken?[] objects, ref int maxDepth, int switch (token.Type) { case JPathTokenType.Asterisk: - if (DequeueToken(tokens).Type != JPathTokenType.RightBracket) - throw new FormatException(); + var rightBracket = DequeueToken(tokens); + if (rightBracket.Type != JPathTokenType.RightBracket) + throw new FormatException($"Unexpected token {rightBracket.Type}"); Descent(ref objects, ref maxDepth, maxObjects); break; case JPathTokenType.Colon: @@ -183,7 +184,7 @@ private static void ProcessBracket(ref JToken?[] objects, ref int maxDepth, int Descent(ref objects, ref maxDepth, maxObjects, int.Parse(token.Content!)); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {next.Type}"); } break; case JPathTokenType.String: @@ -197,11 +198,11 @@ private static void ProcessBracket(ref JToken?[] objects, ref int maxDepth, int Descent(ref objects, ref maxDepth, maxObjects, JToken.Parse($"\"{token.Content!.Trim('\'')}\"")!.GetString()); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {next.Type}"); } break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type}"); } } @@ -209,7 +210,9 @@ private static void ProcessRecursiveDescent(ref JToken?[] objects, ref int maxDe { List results = new(); JPathToken token = DequeueToken(tokens); - if (token.Type != JPathTokenType.Identifier) throw new FormatException(); + if (token.Type != JPathTokenType.Identifier) + throw new FormatException($"Unexpected token {token.Type}"); + while (objects.Length > 0) { results.AddRange(objects.OfType().SelectMany(p => p.Properties).Where(p => p.Key == token.Content).Select(p => p.Value)); @@ -225,15 +228,16 @@ private static void ProcessSlice(ref JToken?[] objects, ref int maxDepth, int ma switch (token.Type) { case JPathTokenType.Number: - if (DequeueToken(tokens).Type != JPathTokenType.RightBracket) - throw new FormatException(); + var next = DequeueToken(tokens); + if (next.Type != JPathTokenType.RightBracket) + throw new FormatException($"Unexpected token {next.Type}"); DescentRange(ref objects, ref maxDepth, maxObjects, start, int.Parse(token.Content!)); break; case JPathTokenType.RightBracket: DescentRange(ref objects, ref maxDepth, maxObjects, start, 0); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type}"); } } @@ -243,14 +247,16 @@ private static void ProcessUnion(ref JToken?[] objects, ref int maxDepth, int ma while (true) { JPathToken token = DequeueToken(tokens); - if (token.Type != first.Type) throw new FormatException(); + if (token.Type != first.Type) + throw new FormatException($"Unexpected token {token.Type} != {first.Type}"); items.Add(token); token = DequeueToken(tokens); if (token.Type == JPathTokenType.RightBracket) break; if (token.Type != JPathTokenType.Comma) - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type} != {JPathTokenType.Comma}"); } + switch (first.Type) { case JPathTokenType.Number: @@ -260,16 +266,19 @@ private static void ProcessUnion(ref JToken?[] objects, ref int maxDepth, int ma Descent(ref objects, ref maxDepth, maxObjects, items.Select(p => JToken.Parse($"\"{p.Content!.Trim('\'')}\"")!.GetString()).ToArray()); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {first.Type}"); } } private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects) { - if (maxDepth <= 0) throw new InvalidOperationException(); + if (maxDepth <= 0) + throw new InvalidOperationException("Exceeded max depth"); --maxDepth; + objects = objects.OfType().SelectMany(p => p.Children).ToArray(); - if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); + if (objects.Length > maxObjects) + throw new InvalidOperationException(nameof(maxObjects)); } private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects, params string[] names) @@ -280,10 +289,14 @@ private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObje if (obj.ContainsProperty(name)) yield return obj[name]; } - if (maxDepth <= 0) throw new InvalidOperationException(); + + if (maxDepth <= 0) + throw new InvalidOperationException("Exceeded max depth"); --maxDepth; + objects = objects.OfType().SelectMany(p => GetProperties(p, names)).ToArray(); - if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); + if (objects.Length > maxObjects) + throw new InvalidOperationException(nameof(maxObjects)); } private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects, params int[] indexes) @@ -297,16 +310,22 @@ private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObje yield return array[i]; } } - if (maxDepth <= 0) throw new InvalidOperationException(); + + if (maxDepth <= 0) + throw new InvalidOperationException("Exceeded max depth"); --maxDepth; + objects = objects.OfType().SelectMany(p => GetElements(p, indexes)).ToArray(); - if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); + if (objects.Length > maxObjects) + throw new InvalidOperationException(nameof(maxObjects)); } private static void DescentRange(ref JToken?[] objects, ref int maxDepth, int maxObjects, int start, int end) { - if (maxDepth <= 0) throw new InvalidOperationException(); + if (maxDepth <= 0) + throw new InvalidOperationException("Exceeded max depth"); --maxDepth; + objects = objects.OfType().SelectMany(p => { int iStart = start >= 0 ? start : start + p.Count; @@ -315,6 +334,7 @@ private static void DescentRange(ref JToken?[] objects, ref int maxDepth, int ma int count = iEnd - iStart; return p.Skip(iStart).Take(count); }).ToArray(); + if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } } diff --git a/src/Neo.Json/JToken.cs b/src/Neo.Json/JToken.cs index 7a6955e414..a01beba974 100644 --- a/src/Neo.Json/JToken.cs +++ b/src/Neo.Json/JToken.cs @@ -171,7 +171,7 @@ public int GetInt32() JsonTokenType.StartObject => ReadObject(ref reader), JsonTokenType.String => ReadString(ref reader), JsonTokenType.True => true, - _ => throw new FormatException(), + _ => throw new FormatException($"Unexpected token {reader.TokenType}"), }; } @@ -189,7 +189,7 @@ private static JArray ReadArray(ref Utf8JsonReader reader) break; } } - throw new FormatException(); + throw new FormatException("Unterminated array"); } private static JObject ReadObject(ref Utf8JsonReader reader) @@ -203,15 +203,17 @@ private static JObject ReadObject(ref Utf8JsonReader reader) return obj; case JsonTokenType.PropertyName: string name = ReadString(ref reader); - if (obj.Properties.ContainsKey(name)) throw new FormatException(); + if (obj.Properties.ContainsKey(name)) + throw new FormatException($"Duplicate property name: {name}"); + JToken? value = Read(ref reader); obj.Properties.Add(name, value); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {reader.TokenType}"); } } - throw new FormatException(); + throw new FormatException("Unterminated object"); } private static string ReadString(ref Utf8JsonReader reader) @@ -271,9 +273,12 @@ public JArray JsonPath(string expr) { JToken?[] objects = { this }; if (expr.Length == 0) return objects; + Queue tokens = new(JPathToken.Parse(expr)); JPathToken first = tokens.Dequeue(); - if (first.Type != JPathTokenType.Root) throw new FormatException(); + if (first.Type != JPathTokenType.Root) + throw new FormatException($"Unexpected token {first.Type}"); + JPathToken.ProcessJsonPath(ref objects, tokens); return objects; }