diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3cee89eff5..52bef0db75 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,7 @@ name: .NET Core Test and Publish on: push: - branches: master + branches: [master] pull_request: env: @@ -17,9 +17,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout - uses: actions/checkout@v2 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v2 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Check format @@ -36,40 +36,17 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} - PublishGithub: - # Because sometimes this action is not working as expected we will disable it until determine that it's more stable - if: false && github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') - needs: Test - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: ${{ env.DOTNET_VERSION }} - - name: Setup NuGet.exe for use with actions - uses: NuGet/setup-nuget@v1 - - name: Pack with dotnet - run: git rev-list --count HEAD |xargs printf "CI%05d" |xargs dotnet pack -c Debug -o out --include-source --version-suffix - - name: Publish to Github Packages - run: | - nuget source Add -Name "GitHub" -Source "https://nuget.pkg.github.com/neo-project/index.json" -UserName neo-project -Password ${GITHUB_TOKEN} - nuget push out/*.nupkg -Source "GitHub" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PublishMyGet: if: github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') needs: Test runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + - name: Setup .NET + uses: actions/setup-dotnet@v2 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Pack with dotnet @@ -85,12 +62,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Get version id: get_version run: | sudo apt install xmlstarlet - find src -name *.csproj | xargs xmlstarlet sel -t -v "concat('::set-output name=version::v',//VersionPrefix/text())" | xargs echo + find src -name Directory.Build.props | xargs xmlstarlet sel -t -v "concat('::set-output name=version::v',//VersionPrefix/text())" | xargs echo - name: Check tag id: check_tag run: curl -s -I ${{ format('https://github.com/{0}/releases/tag/{1}', github.repository, steps.get_version.outputs.version) }} | head -n 1 | cut -d$' ' -f2 | xargs printf "::set-output name=statusCode::%s" | xargs echo @@ -104,9 +81,9 @@ jobs: tag_name: ${{ steps.get_version.outputs.version }} release_name: ${{ steps.get_version.outputs.version }} prerelease: ${{ contains(steps.get_version.outputs.version, '-') }} - - name: Setup .NET Core + - name: Setup .NET if: steps.check_tag.outputs.statusCode == '404' - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v2 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Publish to NuGet diff --git a/neo.sln b/neo.sln index 9cea69b437..e705e23988 100644 --- a/neo.sln +++ b/neo.sln @@ -4,8 +4,12 @@ VisualStudioVersion = 17.2.32516.85 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo", "src\Neo\Neo.csproj", "{36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Json", "src\Neo.Json\Neo.Json.csproj", "{6B709ED6-64C0-451D-B07F-8F49185AE191}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.UnitTests", "tests\Neo.UnitTests\Neo.UnitTests.csproj", "{5B783B30-B422-4C2F-AC22-187A8D1993F4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.Json.UnitTests", "tests\Neo.Json.UnitTests\Neo.Json.UnitTests.csproj", "{AE6C32EE-8447-4E01-8187-2AE02BB64251}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Benchmarks", "benchmarks\Neo.Benchmarks\Neo.Benchmarks.csproj", "{BCD03521-5F8F-4775-9ADF-FA361480804F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B5339DF7-5D1D-43BA-B332-74B825E1770E}" @@ -24,10 +28,18 @@ Global {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}.Release|Any CPU.Build.0 = Release|Any CPU + {6B709ED6-64C0-451D-B07F-8F49185AE191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B709ED6-64C0-451D-B07F-8F49185AE191}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B709ED6-64C0-451D-B07F-8F49185AE191}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B709ED6-64C0-451D-B07F-8F49185AE191}.Release|Any CPU.Build.0 = Release|Any CPU {5B783B30-B422-4C2F-AC22-187A8D1993F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B783B30-B422-4C2F-AC22-187A8D1993F4}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B783B30-B422-4C2F-AC22-187A8D1993F4}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B783B30-B422-4C2F-AC22-187A8D1993F4}.Release|Any CPU.Build.0 = Release|Any CPU + {AE6C32EE-8447-4E01-8187-2AE02BB64251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE6C32EE-8447-4E01-8187-2AE02BB64251}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE6C32EE-8447-4E01-8187-2AE02BB64251}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE6C32EE-8447-4E01-8187-2AE02BB64251}.Release|Any CPU.Build.0 = Release|Any CPU {BCD03521-5F8F-4775-9ADF-FA361480804F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BCD03521-5F8F-4775-9ADF-FA361480804F}.Debug|Any CPU.Build.0 = Debug|Any CPU {BCD03521-5F8F-4775-9ADF-FA361480804F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -38,7 +50,9 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} + {6B709ED6-64C0-451D-B07F-8F49185AE191} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} {5B783B30-B422-4C2F-AC22-187A8D1993F4} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1} + {AE6C32EE-8447-4E01-8187-2AE02BB64251} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1} {BCD03521-5F8F-4775-9ADF-FA361480804F} = {C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000000..011418804d --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,17 @@ + + + + + 2015-2022 The Neo Project + 3.3.1 + The Neo Project + net6.0 + https://github.com/neo-project/neo + MIT + git + https://github.com/neo-project/neo.git + The Neo Project + true + + + diff --git a/src/Neo/IO/Json/JArray.cs b/src/Neo.Json/JArray.cs similarity index 66% rename from src/Neo/IO/Json/JArray.cs rename to src/Neo.Json/JArray.cs index cf06393650..0263c77a3a 100644 --- a/src/Neo/IO/Json/JArray.cs +++ b/src/Neo.Json/JArray.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2022 The Neo Project. // -// The neo is free software distributed under the MIT software license, +// The Neo.Json is free software distributed under the MIT software license, // see the accompanying file LICENSE in the main directory of the // project or http://www.opensource.org/licenses/mit-license.php // for more details. @@ -9,24 +9,22 @@ // modifications are permitted. using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Text.Json; -namespace Neo.IO.Json +namespace Neo.Json { /// /// Represents a JSON array. /// - public class JArray : JObject, IList + public class JArray : JContainer, IList { - private readonly List items = new(); + private readonly List items = new(); /// /// Initializes a new instance of the class. /// /// The initial items in the array. - public JArray(params JObject[] items) : this((IEnumerable)items) + public JArray(params JToken?[] items) : this((IEnumerable)items) { } @@ -34,12 +32,12 @@ public JArray(params JObject[] items) : this((IEnumerable)items) /// Initializes a new instance of the class. /// /// The initial items in the array. - public JArray(IEnumerable items) + public JArray(IEnumerable items) { this.items.AddRange(items); } - public override JObject this[int index] + public override JToken? this[int index] { get { @@ -51,13 +49,7 @@ public override JObject this[int index] } } - public int Count - { - get - { - return items.Count; - } - } + public override IReadOnlyList Children => items; public bool IsReadOnly { @@ -67,7 +59,7 @@ public bool IsReadOnly } } - public void Add(JObject item) + public void Add(JToken? item) { items.Add(item); } @@ -77,24 +69,17 @@ public override string AsString() return string.Join(",", items.Select(p => p?.AsString())); } - public void Clear() + public override void Clear() { items.Clear(); } - public bool Contains(JObject item) + public bool Contains(JToken? item) { return items.Contains(item); } - public void CopyTo(JObject[] array, int arrayIndex) - { - items.CopyTo(array, arrayIndex); - } - - public override JArray GetArray() => this; - - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return items.GetEnumerator(); } @@ -104,17 +89,17 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } - public int IndexOf(JObject item) + public int IndexOf(JToken? item) { return items.IndexOf(item); } - public void Insert(int index, JObject item) + public void Insert(int index, JToken? item) { items.Insert(index, item); } - public bool Remove(JObject item) + public bool Remove(JToken? item) { return items.Remove(item); } @@ -127,7 +112,7 @@ public void RemoveAt(int index) internal override void Write(Utf8JsonWriter writer) { writer.WriteStartArray(); - foreach (JObject item in items) + foreach (JToken? item in items) { if (item is null) writer.WriteNullValue(); @@ -137,19 +122,19 @@ internal override void Write(Utf8JsonWriter writer) writer.WriteEndArray(); } - public override JObject Clone() + public override JArray Clone() { var cloned = new JArray(); - foreach (JObject item in items) + foreach (JToken? item in items) { - cloned.Add(item.Clone()); + cloned.Add(item?.Clone()); } return cloned; } - public static implicit operator JArray(JObject[] value) + public static implicit operator JArray(JToken?[] value) { return new JArray(value); } diff --git a/src/Neo/IO/Json/JBoolean.cs b/src/Neo.Json/JBoolean.cs similarity index 81% rename from src/Neo/IO/Json/JBoolean.cs rename to src/Neo.Json/JBoolean.cs index 80a4af9d2a..9398270aae 100644 --- a/src/Neo/IO/Json/JBoolean.cs +++ b/src/Neo.Json/JBoolean.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2022 The Neo Project. // -// The neo is free software distributed under the MIT software license, +// The Neo.Json is free software distributed under the MIT software license, // see the accompanying file LICENSE in the main directory of the // project or http://www.opensource.org/licenses/mit-license.php // for more details. @@ -10,22 +10,22 @@ using System.Text.Json; -namespace Neo.IO.Json +namespace Neo.Json { /// /// Represents a JSON boolean value. /// - public class JBoolean : JObject + public class JBoolean : JToken { /// - /// Gets the value of the JSON object. + /// Gets the value of the JSON token. /// public bool Value { get; } /// /// Initializes a new instance of the class with the specified value. /// - /// The value of the JSON object. + /// The value of the JSON token. public JBoolean(bool value = false) { this.Value = value; @@ -37,7 +37,7 @@ public override bool AsBoolean() } /// - /// Converts the current JSON object to a floating point number. + /// Converts the current JSON token to a floating point number. /// /// The number 1 if value is ; otherwise, 0. public override double AsNumber() @@ -62,7 +62,7 @@ internal override void Write(Utf8JsonWriter writer) writer.WriteBooleanValue(Value); } - public override JObject Clone() + public override JBoolean Clone() { return this; } diff --git a/src/Neo.Json/JContainer.cs b/src/Neo.Json/JContainer.cs new file mode 100644 index 0000000000..429fe68d81 --- /dev/null +++ b/src/Neo.Json/JContainer.cs @@ -0,0 +1,28 @@ +// Copyright (C) 2015-2022 The Neo Project. +// +// The Neo.Json is free software distributed under the MIT software license, +// see the accompanying file LICENSE in the main directory of the +// project or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +namespace Neo.Json; + +public abstract class JContainer : JToken +{ + public override JToken? this[int index] => Children[index]; + + public abstract IReadOnlyList Children { get; } + + public int Count => Children.Count; + + public abstract void Clear(); + + public void CopyTo(JToken?[] array, int arrayIndex) + { + for (int i = 0; i < Count && i + arrayIndex < array.Length; i++) + array[i + arrayIndex] = Children[i]; + } +} diff --git a/src/Neo/IO/Json/JNumber.cs b/src/Neo.Json/JNumber.cs similarity index 71% rename from src/Neo/IO/Json/JNumber.cs rename to src/Neo.Json/JNumber.cs index ce824dbb4b..7e58b72567 100644 --- a/src/Neo/IO/Json/JNumber.cs +++ b/src/Neo.Json/JNumber.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2022 The Neo Project. // -// The neo is free software distributed under the MIT software license, +// The Neo.Json is free software distributed under the MIT software license, // see the accompanying file LICENSE in the main directory of the // project or http://www.opensource.org/licenses/mit-license.php // for more details. @@ -8,16 +8,15 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; using System.Globalization; using System.Text.Json; -namespace Neo.IO.Json +namespace Neo.Json { /// /// Represents a JSON number. /// - public class JNumber : JObject + public class JNumber : JToken { /// /// Represents the largest safe integer in JSON. @@ -30,14 +29,14 @@ public class JNumber : JObject public static readonly long MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER; /// - /// Gets the value of the JSON object. + /// Gets the value of the JSON token. /// public double Value { get; } /// /// Initializes a new instance of the class with the specified value. /// - /// The value of the JSON object. + /// The value of the JSON token. public JNumber(double value = 0) { if (!double.IsFinite(value)) throw new FormatException(); @@ -45,7 +44,7 @@ public JNumber(double value = 0) } /// - /// Converts the current JSON object to a boolean value. + /// Converts the current JSON token to a boolean value. /// /// if value is not zero; otherwise, . public override bool AsBoolean() @@ -70,7 +69,7 @@ public override string ToString() return AsString(); } - public override T TryGetEnum(T defaultValue = default, bool ignoreCase = false) + public override T AsEnum(T defaultValue = default, bool ignoreCase = false) { Type enumType = typeof(T); object value; @@ -86,12 +85,30 @@ public override T TryGetEnum(T defaultValue = default, bool ignoreCase = fals return Enum.IsDefined(enumType, result) ? (T)result : defaultValue; } + public override T GetEnum(bool ignoreCase = false) + { + Type enumType = typeof(T); + object value; + try + { + value = Convert.ChangeType(Value, enumType.GetEnumUnderlyingType()); + } + catch (OverflowException) + { + throw new InvalidCastException(); + } + object result = Enum.ToObject(enumType, value); + if (!Enum.IsDefined(enumType, result)) + throw new InvalidCastException(); + return (T)result; + } + internal override void Write(Utf8JsonWriter writer) { writer.WriteNumberValue(Value); } - public override JObject Clone() + public override JNumber Clone() { return this; } diff --git a/src/Neo.Json/JObject.cs b/src/Neo.Json/JObject.cs new file mode 100644 index 0000000000..dc26463210 --- /dev/null +++ b/src/Neo.Json/JObject.cs @@ -0,0 +1,90 @@ +// Copyright (C) 2015-2022 The Neo Project. +// +// The Neo.Json is free software distributed under the MIT software license, +// see the accompanying file LICENSE in the main directory of the +// project or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using System.Text.Json; + +namespace Neo.Json +{ + /// + /// Represents a JSON object. + /// + public class JObject : JContainer + { + private readonly OrderedDictionary properties = new(); + + /// + /// Gets or sets the properties of the JSON object. + /// + public IDictionary Properties => properties; + + /// + /// Gets or sets the properties of the JSON object. + /// + /// The name of the property to get or set. + /// The property with the specified name. + public override JToken? this[string name] + { + get + { + if (Properties.TryGetValue(name, out JToken? value)) + return value; + return null; + } + set + { + Properties[name] = value; + } + } + + public override IReadOnlyList Children => properties.Values; + + /// + /// Determines whether the JSON object contains a property with the specified name. + /// + /// The property name to locate in the JSON object. + /// if the JSON object contains a property with the name; otherwise, . + public bool ContainsProperty(string key) + { + return Properties.ContainsKey(key); + } + + public override void Clear() => properties.Clear(); + + internal override void Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + foreach (var (key, value) in Properties) + { + writer.WritePropertyName(key); + if (value is null) + writer.WriteNullValue(); + else + value.Write(writer); + } + writer.WriteEndObject(); + } + + /// + /// Creates a copy of the current JSON object. + /// + /// A copy of the current JSON object. + public override JObject Clone() + { + var cloned = new JObject(); + + foreach (var (key, value) in Properties) + { + cloned[key] = value != null ? value.Clone() : Null; + } + + return cloned; + } + } +} diff --git a/src/Neo/IO/Json/JPathToken.cs b/src/Neo.Json/JPathToken.cs similarity index 86% rename from src/Neo/IO/Json/JPathToken.cs rename to src/Neo.Json/JPathToken.cs index e84cdd4b8d..b2d8a867fe 100644 --- a/src/Neo/IO/Json/JPathToken.cs +++ b/src/Neo.Json/JPathToken.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2022 The Neo Project. // -// The neo is free software distributed under the MIT software license, +// The Neo.Json is free software distributed under the MIT software license, // see the accompanying file LICENSE in the main directory of the // project or http://www.opensource.org/licenses/mit-license.php // for more details. @@ -8,16 +8,12 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Neo.IO.Json +namespace Neo.Json { sealed class JPathToken { public JPathTokenType Type { get; private set; } - public string Content { get; private set; } + public string? Content { get; private set; } public static IEnumerable Parse(string expr) { @@ -114,12 +110,12 @@ private static string ParseNumber(string expr, int start) private static JPathToken DequeueToken(Queue tokens) { - if (!tokens.TryDequeue(out JPathToken token)) + if (!tokens.TryDequeue(out JPathToken? token)) throw new FormatException(); return token; } - public static void ProcessJsonPath(ref JObject[] objects, Queue tokens) + public static void ProcessJsonPath(ref JToken?[] objects, Queue tokens) { int maxDepth = 6; int maxObjects = 1024; @@ -140,7 +136,7 @@ public static void ProcessJsonPath(ref JObject[] objects, Queue toke } } - private static void ProcessDot(ref JObject[] objects, ref int maxDepth, int maxObjects, Queue tokens) + private static void ProcessDot(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens) { JPathToken token = DequeueToken(tokens); switch (token.Type) @@ -152,14 +148,14 @@ private static void ProcessDot(ref JObject[] objects, ref int maxDepth, int maxO ProcessRecursiveDescent(ref objects, ref maxDepth, maxObjects, tokens); break; case JPathTokenType.Identifier: - Descent(ref objects, ref maxDepth, maxObjects, token.Content); + Descent(ref objects, ref maxDepth, maxObjects, token.Content!); break; default: throw new FormatException(); } } - private static void ProcessBracket(ref JObject[] objects, ref int maxDepth, int maxObjects, Queue tokens) + private static void ProcessBracket(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens) { JPathToken token = DequeueToken(tokens); switch (token.Type) @@ -177,13 +173,13 @@ private static void ProcessBracket(ref JObject[] objects, ref int maxDepth, int switch (next.Type) { case JPathTokenType.Colon: - ProcessSlice(ref objects, ref maxDepth, maxObjects, tokens, int.Parse(token.Content)); + ProcessSlice(ref objects, ref maxDepth, maxObjects, tokens, int.Parse(token.Content!)); break; case JPathTokenType.Comma: ProcessUnion(ref objects, ref maxDepth, maxObjects, tokens, token); break; case JPathTokenType.RightBracket: - Descent(ref objects, ref maxDepth, maxObjects, int.Parse(token.Content)); + Descent(ref objects, ref maxDepth, maxObjects, int.Parse(token.Content!)); break; default: throw new FormatException(); @@ -197,7 +193,7 @@ private static void ProcessBracket(ref JObject[] objects, ref int maxDepth, int ProcessUnion(ref objects, ref maxDepth, maxObjects, tokens, token); break; case JPathTokenType.RightBracket: - Descent(ref objects, ref maxDepth, maxObjects, JObject.Parse($"\"{token.Content.Trim('\'')}\"").GetString()); + Descent(ref objects, ref maxDepth, maxObjects, JToken.Parse($"\"{token.Content!.Trim('\'')}\"")!.GetString()); break; default: throw new FormatException(); @@ -208,21 +204,21 @@ private static void ProcessBracket(ref JObject[] objects, ref int maxDepth, int } } - private static void ProcessRecursiveDescent(ref JObject[] objects, ref int maxDepth, int maxObjects, Queue tokens) + private static void ProcessRecursiveDescent(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens) { - List results = new(); + List results = new(); JPathToken token = DequeueToken(tokens); if (token.Type != JPathTokenType.Identifier) throw new FormatException(); while (objects.Length > 0) { - results.AddRange(objects.Where(p => p is not null).SelectMany(p => p.Properties).Where(p => p.Key == token.Content).Select(p => p.Value)); + results.AddRange(objects.OfType().SelectMany(p => p.Properties).Where(p => p.Key == token.Content).Select(p => p.Value)); Descent(ref objects, ref maxDepth, maxObjects); if (results.Count > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } objects = results.ToArray(); } - private static void ProcessSlice(ref JObject[] objects, ref int maxDepth, int maxObjects, Queue tokens, int start) + private static void ProcessSlice(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens, int start) { JPathToken token = DequeueToken(tokens); switch (token.Type) @@ -230,7 +226,7 @@ private static void ProcessSlice(ref JObject[] objects, ref int maxDepth, int ma case JPathTokenType.Number: if (DequeueToken(tokens).Type != JPathTokenType.RightBracket) throw new FormatException(); - DescentRange(ref objects, ref maxDepth, maxObjects, start, int.Parse(token.Content)); + DescentRange(ref objects, ref maxDepth, maxObjects, start, int.Parse(token.Content!)); break; case JPathTokenType.RightBracket: DescentRange(ref objects, ref maxDepth, maxObjects, start, 0); @@ -240,7 +236,7 @@ private static void ProcessSlice(ref JObject[] objects, ref int maxDepth, int ma } } - private static void ProcessUnion(ref JObject[] objects, ref int maxDepth, int maxObjects, Queue tokens, JPathToken first) + private static void ProcessUnion(ref JToken?[] objects, ref int maxDepth, int maxObjects, Queue tokens, JPathToken first) { List items = new() { first }; while (true) @@ -257,27 +253,27 @@ private static void ProcessUnion(ref JObject[] objects, ref int maxDepth, int ma switch (first.Type) { case JPathTokenType.Number: - Descent(ref objects, ref maxDepth, maxObjects, items.Select(p => int.Parse(p.Content)).ToArray()); + Descent(ref objects, ref maxDepth, maxObjects, items.Select(p => int.Parse(p.Content!)).ToArray()); break; case JPathTokenType.String: - Descent(ref objects, ref maxDepth, maxObjects, items.Select(p => JObject.Parse($"\"{p.Content.Trim('\'')}\"").GetString()).ToArray()); + Descent(ref objects, ref maxDepth, maxObjects, items.Select(p => JToken.Parse($"\"{p.Content!.Trim('\'')}\"")!.GetString()).ToArray()); break; default: throw new FormatException(); } } - private static void Descent(ref JObject[] objects, ref int maxDepth, int maxObjects) + private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects) { if (maxDepth <= 0) throw new InvalidOperationException(); --maxDepth; - objects = objects.Where(p => p is not null).SelectMany(p => p is JArray array ? array : p.Properties.Values).ToArray(); + objects = objects.OfType().SelectMany(p => p.Children).ToArray(); if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } - private static void Descent(ref JObject[] objects, ref int maxDepth, int maxObjects, params string[] names) + private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects, params string[] names) { - static IEnumerable GetProperties(JObject obj, string[] names) + static IEnumerable GetProperties(JObject obj, string[] names) { foreach (string name in names) if (obj.ContainsProperty(name)) @@ -285,13 +281,13 @@ static IEnumerable GetProperties(JObject obj, string[] names) } if (maxDepth <= 0) throw new InvalidOperationException(); --maxDepth; - objects = objects.SelectMany(p => GetProperties(p, names)).ToArray(); + objects = objects.OfType().SelectMany(p => GetProperties(p, names)).ToArray(); if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } - private static void Descent(ref JObject[] objects, ref int maxDepth, int maxObjects, params int[] indexes) + private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects, params int[] indexes) { - static IEnumerable GetElements(JArray array, int[] indexes) + static IEnumerable GetElements(JArray array, int[] indexes) { foreach (int index in indexes) { @@ -306,7 +302,7 @@ static IEnumerable GetElements(JArray array, int[] indexes) if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } - private static void DescentRange(ref JObject[] objects, ref int maxDepth, int maxObjects, int start, int end) + private static void DescentRange(ref JToken?[] objects, ref int maxDepth, int maxObjects, int start, int end) { if (maxDepth <= 0) throw new InvalidOperationException(); --maxDepth; diff --git a/src/Neo/IO/Json/JPathTokenType.cs b/src/Neo.Json/JPathTokenType.cs similarity index 50% rename from src/Neo/IO/Json/JPathTokenType.cs rename to src/Neo.Json/JPathTokenType.cs index 8a4bdf4a10..7ec540372e 100644 --- a/src/Neo/IO/Json/JPathTokenType.cs +++ b/src/Neo.Json/JPathTokenType.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2022 The Neo Project. // -// The neo is free software distributed under the MIT software license, +// The Neo.Json is free software distributed under the MIT software license, // see the accompanying file LICENSE in the main directory of the // project or http://www.opensource.org/licenses/mit-license.php // for more details. @@ -8,19 +8,18 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.IO.Json +namespace Neo.Json; + +enum JPathTokenType : byte { - enum JPathTokenType : byte - { - Root, - Dot, - LeftBracket, - RightBracket, - Asterisk, - Comma, - Colon, - Identifier, - String, - Number - } + Root, + Dot, + LeftBracket, + RightBracket, + Asterisk, + Comma, + Colon, + Identifier, + String, + Number } diff --git a/src/Neo/IO/Json/JString.cs b/src/Neo.Json/JString.cs similarity index 71% rename from src/Neo/IO/Json/JString.cs rename to src/Neo.Json/JString.cs index b6ecfa5eea..714ff85928 100644 --- a/src/Neo/IO/Json/JString.cs +++ b/src/Neo.Json/JString.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2022 The Neo Project. // -// The neo is free software distributed under the MIT software license, +// The Neo.Json is free software distributed under the MIT software license, // see the accompanying file LICENSE in the main directory of the // project or http://www.opensource.org/licenses/mit-license.php // for more details. @@ -8,33 +8,32 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; using System.Globalization; using System.Text.Json; -namespace Neo.IO.Json +namespace Neo.Json { /// /// Represents a JSON string. /// - public class JString : JObject + public class JString : JToken { /// - /// Gets the value of the JSON object. + /// Gets the value of the JSON token. /// public string Value { get; } /// /// Initializes a new instance of the class with the specified value. /// - /// The value of the JSON object. + /// The value of the JSON token. public JString(string value) { this.Value = value ?? throw new ArgumentNullException(nameof(value)); } /// - /// Converts the current JSON object to a boolean value. + /// Converts the current JSON token to a boolean value. /// /// if value is not empty; otherwise, . public override bool AsBoolean() @@ -55,11 +54,11 @@ public override string AsString() public override string GetString() => Value; - public override T TryGetEnum(T defaultValue = default, bool ignoreCase = false) + public override T AsEnum(T defaultValue = default, bool ignoreCase = false) { try { - return (T)Enum.Parse(typeof(T), Value, ignoreCase); + return Enum.Parse(Value, ignoreCase); } catch { @@ -67,12 +66,19 @@ public override T TryGetEnum(T defaultValue = default, bool ignoreCase = fals } } + public override T GetEnum(bool ignoreCase = false) + { + T result = Enum.Parse(Value, ignoreCase); + if (!Enum.IsDefined(result)) throw new InvalidCastException(); + return result; + } + internal override void Write(Utf8JsonWriter writer) { writer.WriteStringValue(Value); } - public override JObject Clone() + public override JString Clone() { return this; } @@ -82,7 +88,7 @@ public static implicit operator JString(Enum value) return new JString(value.ToString()); } - public static implicit operator JString(string value) + public static implicit operator JString?(string? value) { return value == null ? null : new JString(value); } diff --git a/src/Neo.Json/JToken.cs b/src/Neo.Json/JToken.cs new file mode 100644 index 0000000000..63ab9f4752 --- /dev/null +++ b/src/Neo.Json/JToken.cs @@ -0,0 +1,304 @@ +// Copyright (C) 2015-2022 The Neo Project. +// +// The Neo.Json is free software distributed under the MIT software license, +// see the accompanying file LICENSE in the main directory of the +// project or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using System.Text.Json; +using static Neo.Json.Utility; + +namespace Neo.Json; + +/// +/// Represents an abstract JSON token. +/// +public abstract class JToken +{ + /// + /// Represents a token. + /// + public const JToken? Null = null; + + /// + /// Gets or sets the child token at the specified index. + /// + /// The zero-based index of the child token to get or set. + /// The child token at the specified index. + public virtual JToken? this[int index] + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + /// + /// Gets or sets the properties of the JSON object. + /// + /// The key of the property to get or set. + /// The property with the specified name. + public virtual JToken? this[string key] + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + /// + /// Converts the current JSON token to a boolean value. + /// + /// The converted value. + public virtual bool AsBoolean() + { + return true; + } + + /// + /// Converts the current JSON token to an . + /// + /// The type of the . + /// If the current JSON token cannot be converted to type , then the default value is returned. + /// Indicates whether case should be ignored during conversion. + /// The converted value. + public virtual T AsEnum(T defaultValue = default, bool ignoreCase = false) where T : unmanaged, Enum + { + return defaultValue; + } + + /// + /// Converts the current JSON token to a floating point number. + /// + /// The converted value. + public virtual double AsNumber() + { + return double.NaN; + } + + /// + /// Converts the current JSON token to a . + /// + /// The converted value. + public virtual string AsString() + { + return ToString(); + } + + /// + /// Converts the current JSON token to a boolean value. + /// + /// The converted value. + /// The JSON token is not a . + public virtual bool GetBoolean() => throw new InvalidCastException(); + + public virtual T GetEnum(bool ignoreCase = false) where T : unmanaged, Enum => throw new InvalidCastException(); + + /// + /// Converts the current JSON token to a 32-bit signed integer. + /// + /// The converted value. + /// The JSON token is not a . + /// The JSON token cannot be converted to an integer. + /// The JSON token cannot be converted to a 32-bit signed integer. + public int GetInt32() + { + double d = GetNumber(); + if (d % 1 != 0) throw new InvalidCastException(); + return checked((int)d); + } + + /// + /// Converts the current JSON token to a floating point number. + /// + /// The converted value. + /// The JSON token is not a . + public virtual double GetNumber() => throw new InvalidCastException(); + + /// + /// Converts the current JSON token to a . + /// + /// The converted value. + /// The JSON token is not a . + public virtual string GetString() => throw new InvalidCastException(); + + /// + /// Parses a JSON token from a byte array. + /// + /// The byte array that contains the JSON token. + /// The maximum nesting depth when parsing the JSON token. + /// The parsed JSON token. + public static JToken? Parse(ReadOnlySpan value, int max_nest = 100) + { + Utf8JsonReader reader = new(value, new JsonReaderOptions + { + AllowTrailingCommas = false, + CommentHandling = JsonCommentHandling.Skip, + MaxDepth = max_nest + }); + try + { + JToken? json = Read(ref reader); + if (reader.Read()) throw new FormatException(); + return json; + } + catch (JsonException ex) + { + throw new FormatException(ex.Message, ex); + } + } + + /// + /// Parses a JSON token from a . + /// + /// The that contains the JSON token. + /// The maximum nesting depth when parsing the JSON token. + /// The parsed JSON token. + public static JToken? Parse(string value, int max_nest = 100) + { + return Parse(StrictUTF8.GetBytes(value), max_nest); + } + + private static JToken? Read(ref Utf8JsonReader reader, bool skipReading = false) + { + if (!skipReading && !reader.Read()) throw new FormatException(); + return reader.TokenType switch + { + JsonTokenType.False => false, + JsonTokenType.Null => Null, + JsonTokenType.Number => reader.GetDouble(), + JsonTokenType.StartArray => ReadArray(ref reader), + JsonTokenType.StartObject => ReadObject(ref reader), + JsonTokenType.String => ReadString(ref reader), + JsonTokenType.True => true, + _ => throw new FormatException(), + }; + } + + private static JArray ReadArray(ref Utf8JsonReader reader) + { + JArray array = new(); + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.EndArray: + return array; + default: + array.Add(Read(ref reader, skipReading: true)); + break; + } + } + throw new FormatException(); + } + + private static JObject ReadObject(ref Utf8JsonReader reader) + { + JObject obj = new(); + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.EndObject: + return obj; + case JsonTokenType.PropertyName: + string name = ReadString(ref reader); + if (obj.Properties.ContainsKey(name)) throw new FormatException(); + JToken? value = Read(ref reader); + obj.Properties.Add(name, value); + break; + default: + throw new FormatException(); + } + } + throw new FormatException(); + } + + private static string ReadString(ref Utf8JsonReader reader) + { + try + { + return reader.GetString()!; + } + catch (InvalidOperationException ex) + { + throw new FormatException(ex.Message, ex); + } + } + + /// + /// Encode the current JSON token into a byte array. + /// + /// Indicates whether indentation is required. + /// The encoded JSON token. + public byte[] ToByteArray(bool indented) + { + using MemoryStream ms = new(); + using Utf8JsonWriter writer = new(ms, new JsonWriterOptions + { + Indented = indented, + SkipValidation = true + }); + Write(writer); + writer.Flush(); + return ms.ToArray(); + } + + /// + /// Encode the current JSON token into a . + /// + /// The encoded JSON token. + public override string ToString() + { + return ToString(false); + } + + /// + /// Encode the current JSON token into a . + /// + /// Indicates whether indentation is required. + /// The encoded JSON token. + public string ToString(bool indented) + { + return StrictUTF8.GetString(ToByteArray(indented)); + } + + internal abstract void Write(Utf8JsonWriter writer); + + public abstract JToken Clone(); + + 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(); + JPathToken.ProcessJsonPath(ref objects, tokens); + return objects; + } + + public static implicit operator JToken(Enum value) + { + return (JString)value; + } + + public static implicit operator JToken(JToken?[] value) + { + return (JArray)value; + } + + public static implicit operator JToken(bool value) + { + return (JBoolean)value; + } + + public static implicit operator JToken(double value) + { + return (JNumber)value; + } + + public static implicit operator JToken?(string? value) + { + return (JString?)value; + } +} diff --git a/src/Neo.Json/Neo.Json.csproj b/src/Neo.Json/Neo.Json.csproj new file mode 100644 index 0000000000..94943d4402 --- /dev/null +++ b/src/Neo.Json/Neo.Json.csproj @@ -0,0 +1,9 @@ + + + + enable + enable + NEO;JSON + + + diff --git a/src/Neo.Json/OrderedDictionary.KeyCollection.cs b/src/Neo.Json/OrderedDictionary.KeyCollection.cs new file mode 100644 index 0000000000..bdc79411f3 --- /dev/null +++ b/src/Neo.Json/OrderedDictionary.KeyCollection.cs @@ -0,0 +1,50 @@ +// Copyright (C) 2015-2022 The Neo Project. +// +// The Neo.Json is free software distributed under the MIT software license, +// see the accompanying file LICENSE in the main directory of the +// project or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using System.Collections; + +namespace Neo.Json; + +partial class OrderedDictionary +{ + class KeyCollection : ICollection, IReadOnlyList + { + private readonly InternalCollection internalCollection; + + public KeyCollection(InternalCollection internalCollection) + { + this.internalCollection = internalCollection; + } + + public TKey this[int index] => internalCollection[index].Key; + + public int Count => internalCollection.Count; + + public bool IsReadOnly => true; + + public void Add(TKey item) => throw new NotSupportedException(); + + public void Clear() => throw new NotSupportedException(); + + public bool Contains(TKey item) => internalCollection.Contains(item); + + public void CopyTo(TKey[] array, int arrayIndex) + { + for (int i = 0; i < internalCollection.Count && i + arrayIndex < array.Length; i++) + array[i + arrayIndex] = internalCollection[i].Key; + } + + public IEnumerator GetEnumerator() => internalCollection.Select(p => p.Key).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public bool Remove(TKey item) => throw new NotSupportedException(); + } +} diff --git a/src/Neo.Json/OrderedDictionary.ValueCollection.cs b/src/Neo.Json/OrderedDictionary.ValueCollection.cs new file mode 100644 index 0000000000..f10425aca8 --- /dev/null +++ b/src/Neo.Json/OrderedDictionary.ValueCollection.cs @@ -0,0 +1,50 @@ +// Copyright (C) 2015-2022 The Neo Project. +// +// The Neo.Json is free software distributed under the MIT software license, +// see the accompanying file LICENSE in the main directory of the +// project or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using System.Collections; + +namespace Neo.Json; + +partial class OrderedDictionary +{ + class ValueCollection : ICollection, IReadOnlyList + { + private readonly InternalCollection internalCollection; + + public ValueCollection(InternalCollection internalCollection) + { + this.internalCollection = internalCollection; + } + + public TValue this[int index] => internalCollection[index].Value; + + public int Count => internalCollection.Count; + + public bool IsReadOnly => true; + + public void Add(TValue item) => throw new NotSupportedException(); + + public void Clear() => throw new NotSupportedException(); + + public bool Contains(TValue item) => item is null ? internalCollection.Any(p => p is null) : internalCollection.Any(p => item.Equals(p.Value)); + + public void CopyTo(TValue[] array, int arrayIndex) + { + for (int i = 0; i < internalCollection.Count && i + arrayIndex < array.Length; i++) + array[i + arrayIndex] = internalCollection[i].Value; + } + + public IEnumerator GetEnumerator() => internalCollection.Select(p => p.Value).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public bool Remove(TValue item) => throw new NotSupportedException(); + } +} diff --git a/src/Neo/IO/Caching/OrderedDictionary.cs b/src/Neo.Json/OrderedDictionary.cs similarity index 77% rename from src/Neo/IO/Caching/OrderedDictionary.cs rename to src/Neo.Json/OrderedDictionary.cs index cd9f00fd7e..261b15a725 100644 --- a/src/Neo/IO/Caching/OrderedDictionary.cs +++ b/src/Neo.Json/OrderedDictionary.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2022 The Neo Project. // -// The neo is free software distributed under the MIT software license, +// The Neo.Json is free software distributed under the MIT software license, // see the accompanying file LICENSE in the main directory of the // project or http://www.opensource.org/licenses/mit-license.php // for more details. @@ -9,18 +9,23 @@ // modifications are permitted. using System.Collections; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; +using System.Diagnostics.CodeAnalysis; -namespace Neo.IO.Caching +namespace Neo.Json { - internal class OrderedDictionary : IDictionary + partial class OrderedDictionary : IDictionary where TKey : notnull { private class TItem { public TKey Key; public TValue Value; + + public TItem(TKey key, TValue value) + { + Key = key; + Value = value; + } } private class InternalCollection : KeyedCollection @@ -35,8 +40,16 @@ protected override TKey GetKeyForItem(TItem item) public int Count => collection.Count; public bool IsReadOnly => false; - public ICollection Keys => collection.Select(p => p.Key).ToArray(); - public ICollection Values => collection.Select(p => p.Value).ToArray(); + public IReadOnlyList Keys { get; } + public IReadOnlyList Values { get; } + ICollection IDictionary.Keys => (KeyCollection)Keys; + ICollection IDictionary.Values => (ValueCollection)Values; + + public OrderedDictionary() + { + Keys = new KeyCollection(collection); + Values = new ValueCollection(collection); + } public TValue this[TKey key] { @@ -63,11 +76,7 @@ public TValue this[int index] public void Add(TKey key, TValue value) { - collection.Add(new TItem - { - Key = key, - Value = value - }); + collection.Add(new TItem(key, value)); } public bool ContainsKey(TKey key) @@ -80,7 +89,7 @@ public bool Remove(TKey key) return collection.Remove(key); } - public bool TryGetValue(TKey key, out TValue value) + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { if (collection.TryGetValue(key, out var entry)) { diff --git a/src/Neo.Json/Properties/AssemblyInfo.cs b/src/Neo.Json/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..1d0d02140b --- /dev/null +++ b/src/Neo.Json/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Neo.Json.UnitTests")] diff --git a/src/Neo.Json/Utility.cs b/src/Neo.Json/Utility.cs new file mode 100644 index 0000000000..711ba25901 --- /dev/null +++ b/src/Neo.Json/Utility.cs @@ -0,0 +1,25 @@ +// Copyright (C) 2015-2022 The Neo Project. +// +// The Neo.Json is free software distributed under the MIT software license, +// see the accompanying file LICENSE in the main directory of the +// project or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using System.Text; + +namespace Neo.Json; + +static class Utility +{ + public static Encoding StrictUTF8 { get; } + + static Utility() + { + StrictUTF8 = (Encoding)Encoding.UTF8.Clone(); + StrictUTF8.DecoderFallback = DecoderFallback.ExceptionFallback; + StrictUTF8.EncoderFallback = EncoderFallback.ExceptionFallback; + } +} diff --git a/src/Neo/IO/Json/JObject.cs b/src/Neo/IO/Json/JObject.cs deleted file mode 100644 index 94a545b0e9..0000000000 --- a/src/Neo/IO/Json/JObject.cs +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright (C) 2015-2022 The Neo Project. -// -// The neo is free software distributed under the MIT software license, -// see the accompanying file LICENSE in the main directory of the -// project or http://www.opensource.org/licenses/mit-license.php -// for more details. -// -// Redistribution and use in source and binary forms with or without -// modifications are permitted. - -using Neo.IO.Caching; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; - -namespace Neo.IO.Json -{ - /// - /// Represents a JSON object. - /// - public class JObject - { - /// - /// Represents a object. - /// - public static readonly JObject Null = null; - - private readonly OrderedDictionary properties = new(); - - /// - /// Gets or sets the properties of the JSON object. - /// - public IDictionary Properties => properties; - - /// - /// Gets or sets the properties of the JSON object. - /// - /// The name of the property to get or set. - /// The property with the specified name. - public JObject this[string name] - { - get - { - if (Properties.TryGetValue(name, out JObject value)) - return value; - return null; - } - set - { - Properties[name] = value; - } - } - - /// - /// Gets the property at the specified index. - /// - /// The zero-based index of the property to get. - /// The property at the specified index. - public virtual JObject this[int index] - { - get => properties[index]; - set => throw new NotSupportedException(); - } - - /// - /// Converts the current JSON object to a boolean value. - /// - /// The converted value. - public virtual bool AsBoolean() - { - return true; - } - - /// - /// Converts the current JSON object to a floating point number. - /// - /// The converted value. - public virtual double AsNumber() - { - return double.NaN; - } - - /// - /// Converts the current JSON object to a . - /// - /// The converted value. - public virtual string AsString() - { - return ToString(); - } - - /// - /// Determines whether the JSON object contains a property with the specified name. - /// - /// The property name to locate in the JSON object. - /// if the JSON object contains a property with the name; otherwise, . - public bool ContainsProperty(string key) - { - return Properties.ContainsKey(key); - } - - /// - /// Converts the current JSON object to a object. - /// - /// The converted value. - /// The JSON object is not a . - public virtual JArray GetArray() => throw new InvalidCastException(); - - /// - /// Converts the current JSON object to a boolean value. - /// - /// The converted value. - /// The JSON object is not a . - public virtual bool GetBoolean() => throw new InvalidCastException(); - - /// - /// Converts the current JSON object to a 32-bit signed integer. - /// - /// The converted value. - /// The JSON object is not a . - /// The JSON object cannot be converted to an integer. - /// The JSON object cannot be converted to a 32-bit signed integer. - public int GetInt32() - { - double d = GetNumber(); - if (d % 1 != 0) throw new InvalidCastException(); - return checked((int)d); - } - - /// - /// Converts the current JSON object to a floating point number. - /// - /// The converted value. - /// The JSON object is not a . - public virtual double GetNumber() => throw new InvalidCastException(); - - /// - /// Converts the current JSON object to a . - /// - /// The converted value. - /// The JSON object is not a . - public virtual string GetString() => throw new InvalidCastException(); - - /// - /// Parses a JSON object from a byte array. - /// - /// The byte array that contains the JSON object. - /// The maximum nesting depth when parsing the JSON object. - /// The parsed JSON object. - public static JObject Parse(ReadOnlySpan value, int max_nest = 100) - { - Utf8JsonReader reader = new(value, new JsonReaderOptions - { - AllowTrailingCommas = false, - CommentHandling = JsonCommentHandling.Skip, - MaxDepth = max_nest - }); - try - { - JObject json = Read(ref reader); - if (reader.Read()) throw new FormatException(); - return json; - } - catch (JsonException ex) - { - throw new FormatException(ex.Message, ex); - } - } - - /// - /// Parses a JSON object from a . - /// - /// The that contains the JSON object. - /// The maximum nesting depth when parsing the JSON object. - /// The parsed JSON object. - public static JObject Parse(string value, int max_nest = 100) - { - return Parse(Utility.StrictUTF8.GetBytes(value), max_nest); - } - - private static JObject Read(ref Utf8JsonReader reader, bool skipReading = false) - { - if (!skipReading && !reader.Read()) throw new FormatException(); - return reader.TokenType switch - { - JsonTokenType.False => false, - JsonTokenType.Null => Null, - JsonTokenType.Number => reader.GetDouble(), - JsonTokenType.StartArray => ReadArray(ref reader), - JsonTokenType.StartObject => ReadObject(ref reader), - JsonTokenType.String => ReadString(ref reader), - JsonTokenType.True => true, - _ => throw new FormatException(), - }; - } - - private static JArray ReadArray(ref Utf8JsonReader reader) - { - JArray array = new(); - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonTokenType.EndArray: - return array; - default: - array.Add(Read(ref reader, skipReading: true)); - break; - } - } - throw new FormatException(); - } - - private static JObject ReadObject(ref Utf8JsonReader reader) - { - JObject obj = new(); - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonTokenType.EndObject: - return obj; - case JsonTokenType.PropertyName: - string name = ReadString(ref reader); - if (obj.Properties.ContainsKey(name)) throw new FormatException(); - JObject value = Read(ref reader); - obj.Properties.Add(name, value); - break; - default: - throw new FormatException(); - } - } - throw new FormatException(); - } - - private static string ReadString(ref Utf8JsonReader reader) - { - try - { - return reader.GetString(); - } - catch (InvalidOperationException ex) - { - throw new FormatException(ex.Message, ex); - } - } - - /// - /// Encode the current JSON object into a byte array. - /// - /// Indicates whether indentation is required. - /// The encoded JSON object. - public byte[] ToByteArray(bool indented) - { - using MemoryStream ms = new(); - using Utf8JsonWriter writer = new(ms, new JsonWriterOptions - { - Indented = indented, - SkipValidation = true - }); - Write(writer); - writer.Flush(); - return ms.ToArray(); - } - - /// - /// Encode the current JSON object into a . - /// - /// The encoded JSON object. - public override string ToString() - { - return ToString(false); - } - - /// - /// Encode the current JSON object into a . - /// - /// Indicates whether indentation is required. - /// The encoded JSON object. - public string ToString(bool indented) - { - return Utility.StrictUTF8.GetString(ToByteArray(indented)); - } - - /// - /// Converts the current JSON object to an . - /// - /// The type of the . - /// If the current JSON object cannot be converted to type , then the default value is returned. - /// Indicates whether case should be ignored during conversion. - /// The converted value. - public virtual T TryGetEnum(T defaultValue = default, bool ignoreCase = false) where T : Enum - { - return defaultValue; - } - - internal virtual void Write(Utf8JsonWriter writer) - { - writer.WriteStartObject(); - foreach (KeyValuePair pair in Properties) - { - writer.WritePropertyName(pair.Key); - if (pair.Value is null) - writer.WriteNullValue(); - else - pair.Value.Write(writer); - } - writer.WriteEndObject(); - } - - /// - /// Creates a copy of the current JSON object. - /// - /// A copy of the current JSON object. - public virtual JObject Clone() - { - var cloned = new JObject(); - - foreach (KeyValuePair pair in Properties) - { - cloned[pair.Key] = pair.Value != null ? pair.Value.Clone() : Null; - } - - return cloned; - } - - public JArray JsonPath(string expr) - { - JObject[] 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(); - JPathToken.ProcessJsonPath(ref objects, tokens); - return objects; - } - - public static implicit operator JObject(Enum value) - { - return (JString)value; - } - - public static implicit operator JObject(JObject[] value) - { - return (JArray)value; - } - - public static implicit operator JObject(bool value) - { - return (JBoolean)value; - } - - public static implicit operator JObject(double value) - { - return (JNumber)value; - } - - public static implicit operator JObject(string value) - { - return (JString)value; - } - } -} diff --git a/src/Neo/Neo.csproj b/src/Neo/Neo.csproj index fa7d43e1b3..878316f460 100644 --- a/src/Neo/Neo.csproj +++ b/src/Neo/Neo.csproj @@ -1,20 +1,8 @@ - 2015-2022 The Neo Project - 3.3.1 - The Neo Project - net6.0 true - Neo NEO;AntShares;Blockchain;Smart Contract - https://github.com/neo-project/neo - MIT - git - https://github.com/neo-project/neo.git - The Neo Project - Neo - true @@ -29,4 +17,8 @@ + + + + diff --git a/src/Neo/Network/P2P/Payloads/Block.cs b/src/Neo/Network/P2P/Payloads/Block.cs index 116d23f9bf..955bf98bf6 100644 --- a/src/Neo/Network/P2P/Payloads/Block.cs +++ b/src/Neo/Network/P2P/Payloads/Block.cs @@ -10,7 +10,7 @@ using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Ledger; using Neo.Persistence; using System; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs index bd1ee0471f..096b47622b 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; @@ -51,7 +51,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) private protected override void ParseJson(JObject json) { - Expressions = json["expressions"].GetArray().Select(FromJson).ToArray(); + Expressions = ((JArray)json["expressions"]).Select(p => FromJson((JObject)p)).ToArray(); } public override JObject ToJson() diff --git a/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs index 22ead0d304..ad6a914dd1 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs index 17d56673f2..82f24c7676 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs index 6d70052859..d542d031db 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs @@ -10,7 +10,7 @@ using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs index c6e51c35c3..1acd19f190 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs @@ -10,7 +10,7 @@ using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs index 0de4386fcc..667a66770c 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; @@ -49,7 +49,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) private protected override void ParseJson(JObject json) { - Expression = FromJson(json["expression"]); + Expression = FromJson((JObject)json["expression"]); } public override JObject ToJson() diff --git a/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs index 88885a8cb3..c66becde2a 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; @@ -51,7 +51,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) private protected override void ParseJson(JObject json) { - Expressions = json["expressions"].GetArray().Select(FromJson).ToArray(); + Expressions = ((JArray)json["expressions"]).Select(p => FromJson((JObject)p)).ToArray(); } public override JObject ToJson() diff --git a/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs index 7584d0130d..315c1fde02 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs index 0b47bde071..ed9f55b941 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs @@ -10,7 +10,7 @@ using Neo.IO; using Neo.IO.Caching; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/Network/P2P/Payloads/Header.cs b/src/Neo/Network/P2P/Payloads/Header.cs index 0cee55b052..d971ac2aaa 100644 --- a/src/Neo/Network/P2P/Payloads/Header.cs +++ b/src/Neo/Network/P2P/Payloads/Header.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; diff --git a/src/Neo/Network/P2P/Payloads/OracleResponse.cs b/src/Neo/Network/P2P/Payloads/OracleResponse.cs index f09c653345..0babd9933a 100644 --- a/src/Neo/Network/P2P/Payloads/OracleResponse.cs +++ b/src/Neo/Network/P2P/Payloads/OracleResponse.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; diff --git a/src/Neo/Network/P2P/Payloads/Signer.cs b/src/Neo/Network/P2P/Payloads/Signer.cs index e6a2ce0a78..321f5e16ec 100644 --- a/src/Neo/Network/P2P/Payloads/Signer.cs +++ b/src/Neo/Network/P2P/Payloads/Signer.cs @@ -10,7 +10,7 @@ using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads.Conditions; using Neo.SmartContract; using Neo.VM; @@ -156,11 +156,11 @@ public static Signer FromJson(JObject json) signer.Account = UInt160.Parse(json["account"].GetString()); signer.Scopes = Enum.Parse(json["scopes"].GetString()); if (signer.Scopes.HasFlag(WitnessScope.CustomContracts)) - signer.AllowedContracts = json["allowedcontracts"].GetArray().Select(p => UInt160.Parse(p.GetString())).ToArray(); + signer.AllowedContracts = ((JArray)json["allowedcontracts"]).Select(p => UInt160.Parse(p.GetString())).ToArray(); if (signer.Scopes.HasFlag(WitnessScope.CustomGroups)) - signer.AllowedGroups = json["allowedgroups"].GetArray().Select(p => ECPoint.Parse(p.GetString(), ECCurve.Secp256r1)).ToArray(); + signer.AllowedGroups = ((JArray)json["allowedgroups"]).Select(p => ECPoint.Parse(p.GetString(), ECCurve.Secp256r1)).ToArray(); if (signer.Scopes.HasFlag(WitnessScope.WitnessRules)) - signer.Rules = json["rules"].GetArray().Select(WitnessRule.FromJson).ToArray(); + signer.Rules = ((JArray)json["rules"]).Select(p => WitnessRule.FromJson((JObject)p)).ToArray(); return signer; } @@ -174,9 +174,9 @@ public JObject ToJson() json["account"] = Account.ToString(); json["scopes"] = Scopes; if (Scopes.HasFlag(WitnessScope.CustomContracts)) - json["allowedcontracts"] = AllowedContracts.Select(p => (JObject)p.ToString()).ToArray(); + json["allowedcontracts"] = AllowedContracts.Select(p => (JToken)p.ToString()).ToArray(); if (Scopes.HasFlag(WitnessScope.CustomGroups)) - json["allowedgroups"] = AllowedGroups.Select(p => (JObject)p.ToString()).ToArray(); + json["allowedgroups"] = AllowedGroups.Select(p => (JToken)p.ToString()).ToArray(); if (Scopes.HasFlag(WitnessScope.WitnessRules)) json["rules"] = Rules.Select(p => p.ToJson()).ToArray(); return json; diff --git a/src/Neo/Network/P2P/Payloads/Transaction.cs b/src/Neo/Network/P2P/Payloads/Transaction.cs index 82900545c1..b99449ac48 100644 --- a/src/Neo/Network/P2P/Payloads/Transaction.cs +++ b/src/Neo/Network/P2P/Payloads/Transaction.cs @@ -11,7 +11,7 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; diff --git a/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs index 54160d3ebd..da8620a95c 100644 --- a/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -10,7 +10,7 @@ using Neo.IO; using Neo.IO.Caching; -using Neo.IO.Json; +using Neo.Json; using Neo.Persistence; using System; using System.IO; diff --git a/src/Neo/Network/P2P/Payloads/Witness.cs b/src/Neo/Network/P2P/Payloads/Witness.cs index 113487cd6c..e8733bd9e2 100644 --- a/src/Neo/Network/P2P/Payloads/Witness.cs +++ b/src/Neo/Network/P2P/Payloads/Witness.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using System; using System.IO; diff --git a/src/Neo/Network/P2P/Payloads/WitnessRule.cs b/src/Neo/Network/P2P/Payloads/WitnessRule.cs index d4d866349b..a020191e79 100644 --- a/src/Neo/Network/P2P/Payloads/WitnessRule.cs +++ b/src/Neo/Network/P2P/Payloads/WitnessRule.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads.Conditions; using Neo.SmartContract; using Neo.VM; @@ -60,7 +60,7 @@ public static WitnessRule FromJson(JObject json) return new() { Action = Enum.Parse(json["action"].GetString()), - Condition = WitnessCondition.FromJson(json["condition"]) + Condition = WitnessCondition.FromJson((JObject)json["condition"]) }; } diff --git a/src/Neo/SmartContract/ApplicationEngine.cs b/src/Neo/SmartContract/ApplicationEngine.cs index 9466cb14bb..66bc453eed 100644 --- a/src/Neo/SmartContract/ApplicationEngine.cs +++ b/src/Neo/SmartContract/ApplicationEngine.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; diff --git a/src/Neo/SmartContract/ContractParameter.cs b/src/Neo/SmartContract/ContractParameter.cs index 7441fa0d07..ba8583934b 100644 --- a/src/Neo/SmartContract/ContractParameter.cs +++ b/src/Neo/SmartContract/ContractParameter.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; -using Neo.IO.Json; +using Neo.Json; using System; using System.Collections.Generic; using System.Linq; @@ -83,8 +83,8 @@ public static ContractParameter FromJson(JObject json) ContractParameterType.Hash256 => UInt256.Parse(json["value"].AsString()), ContractParameterType.PublicKey => ECPoint.Parse(json["value"].AsString(), ECCurve.Secp256r1), ContractParameterType.String => json["value"].AsString(), - ContractParameterType.Array => ((JArray)json["value"]).Select(p => FromJson(p)).ToList(), - ContractParameterType.Map => ((JArray)json["value"]).Select(p => new KeyValuePair(FromJson(p["key"]), FromJson(p["value"]))).ToList(), + ContractParameterType.Array => ((JArray)json["value"]).Select(p => FromJson((JObject)p)).ToList(), + ContractParameterType.Map => ((JArray)json["value"]).Select(p => new KeyValuePair(FromJson((JObject)p["key"]), FromJson((JObject)p["value"]))).ToList(), _ => throw new ArgumentException(null, nameof(json)), }; return parameter; diff --git a/src/Neo/SmartContract/ContractParametersContext.cs b/src/Neo/SmartContract/ContractParametersContext.cs index 643c655a7e..b042f4e263 100644 --- a/src/Neo/SmartContract/ContractParametersContext.cs +++ b/src/Neo/SmartContract/ContractParametersContext.cs @@ -10,7 +10,7 @@ using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.VM; @@ -44,8 +44,8 @@ public ContextItem(Contract contract) public ContextItem(JObject json) { this.Script = Convert.FromBase64String(json["script"].AsString()); - this.Parameters = ((JArray)json["parameters"]).Select(p => ContractParameter.FromJson(p)).ToArray(); - this.Signatures = json["signatures"].Properties.Select(p => new + this.Parameters = ((JArray)json["parameters"]).Select(p => ContractParameter.FromJson((JObject)p)).ToArray(); + this.Signatures = ((JObject)json["signatures"]).Properties.Select(p => new { PublicKey = ECPoint.Parse(p.Key, ECCurve.Secp256r1), Signature = Convert.FromBase64String(p.Value.AsString()) @@ -238,9 +238,9 @@ public static ContractParametersContext FromJson(JObject json, DataCache snapsho if (hash != verifiable.Hash) throw new FormatException(); } ContractParametersContext context = new(snapshot, verifiable, (uint)json["network"].GetInt32()); - foreach (var property in json["items"].Properties) + foreach (var (key, value) in ((JObject)json["items"]).Properties) { - context.ContextItems.Add(UInt160.Parse(property.Key), new ContextItem(property.Value)); + context.ContextItems.Add(UInt160.Parse(key), new ContextItem((JObject)value)); } return context; } @@ -326,7 +326,7 @@ public Witness[] GetWitnesses() /// The parsed context. public static ContractParametersContext Parse(string value, DataCache snapshot) { - return FromJson(JObject.Parse(value), snapshot); + return FromJson((JObject)JToken.Parse(value), snapshot); } /// diff --git a/src/Neo/SmartContract/ContractState.cs b/src/Neo/SmartContract/ContractState.cs index c041d86478..53f90cd30a 100644 --- a/src/Neo/SmartContract/ContractState.cs +++ b/src/Neo/SmartContract/ContractState.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/SmartContract/JsonSerializer.cs b/src/Neo/SmartContract/JsonSerializer.cs index 993c64738a..fed5354d76 100644 --- a/src/Neo/SmartContract/JsonSerializer.cs +++ b/src/Neo/SmartContract/JsonSerializer.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -30,11 +30,11 @@ namespace Neo.SmartContract public static class JsonSerializer { /// - /// Serializes a to a . + /// Serializes a to a . /// /// The to serialize. /// The serialized object. - public static JObject Serialize(StackItem item) + public static JToken Serialize(StackItem item) { switch (item) { @@ -76,7 +76,7 @@ public static JObject Serialize(StackItem item) } case Null _: { - return JObject.Null; + return JToken.Null; } default: throw new FormatException(); } @@ -156,19 +156,19 @@ public static byte[] SerializeToByteArray(StackItem item, uint maxSize) } /// - /// Deserializes a from . + /// Deserializes a from . /// - /// The to deserialize. + /// The to deserialize. /// The limits for the deserialization. /// The used by the . /// The deserialized . - public static StackItem Deserialize(JObject json, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(JToken json, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) { uint maxStackSize = limits.MaxStackSize; return Deserialize(json, ref maxStackSize, referenceCounter); } - private static StackItem Deserialize(JObject json, ref uint maxStackSize, ReferenceCounter referenceCounter) + private static StackItem Deserialize(JToken json, ref uint maxStackSize, ReferenceCounter referenceCounter) { if (maxStackSize-- == 0) throw new FormatException(); switch (json) @@ -180,7 +180,7 @@ private static StackItem Deserialize(JObject json, ref uint maxStackSize, Refere case JArray array: { List list = new(array.Count); - foreach (JObject obj in array) + foreach (JToken obj in array) list.Add(Deserialize(obj, ref maxStackSize, referenceCounter)); return new Array(referenceCounter, list); } diff --git a/src/Neo/SmartContract/Manifest/ContractAbi.cs b/src/Neo/SmartContract/Manifest/ContractAbi.cs index 7edfef1057..709668391c 100644 --- a/src/Neo/SmartContract/Manifest/ContractAbi.cs +++ b/src/Neo/SmartContract/Manifest/ContractAbi.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -61,8 +61,8 @@ public static ContractAbi FromJson(JObject json) { ContractAbi abi = new() { - Methods = ((JArray)json["methods"]).Select(u => ContractMethodDescriptor.FromJson(u)).ToArray(), - Events = ((JArray)json["events"]).Select(u => ContractEventDescriptor.FromJson(u)).ToArray() + Methods = ((JArray)json["methods"]).Select(u => ContractMethodDescriptor.FromJson((JObject)u)).ToArray(), + Events = ((JArray)json["events"]).Select(u => ContractEventDescriptor.FromJson((JObject)u)).ToArray() }; if (abi.Methods.Length == 0) throw new FormatException(); return abi; diff --git a/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs index 165b2cba05..1b6bd24fe7 100644 --- a/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -58,7 +58,7 @@ public static ContractEventDescriptor FromJson(JObject json) ContractEventDescriptor descriptor = new() { Name = json["name"].GetString(), - Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), + Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson((JObject)u)).ToArray(), }; if (string.IsNullOrEmpty(descriptor.Name)) throw new FormatException(); _ = descriptor.Parameters.ToDictionary(p => p.Name); diff --git a/src/Neo/SmartContract/Manifest/ContractGroup.cs b/src/Neo/SmartContract/Manifest/ContractGroup.cs index 22ab762d88..ebb0dbd12c 100644 --- a/src/Neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/Neo/SmartContract/Manifest/ContractGroup.cs @@ -11,7 +11,7 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; diff --git a/src/Neo/SmartContract/Manifest/ContractManifest.cs b/src/Neo/SmartContract/Manifest/ContractManifest.cs index 5a8a228e29..7a067409f2 100644 --- a/src/Neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/Neo/SmartContract/Manifest/ContractManifest.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -83,7 +83,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) Array array => WildcardContainer.Create(array.Select(p => new ContractPermissionDescriptor(p.GetSpan())).ToArray()), _ => throw new ArgumentException(null, nameof(stackItem)) }; - Extra = JObject.Parse(@struct[7].GetSpan()); + Extra = (JObject)JToken.Parse(@struct[7].GetSpan()); } public StackItem ToStackItem(ReferenceCounter referenceCounter) @@ -111,17 +111,17 @@ public static ContractManifest FromJson(JObject json) ContractManifest manifest = new() { Name = json["name"].GetString(), - Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson(u)).ToArray(), + Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson((JObject)u)).ToArray(), SupportedStandards = ((JArray)json["supportedstandards"]).Select(u => u.GetString()).ToArray(), - Abi = ContractAbi.FromJson(json["abi"]), - Permissions = ((JArray)json["permissions"]).Select(u => ContractPermission.FromJson(u)).ToArray(), - Trusts = WildcardContainer.FromJson(json["trusts"], u => ContractPermissionDescriptor.FromJson(u)), - Extra = json["extra"] + Abi = ContractAbi.FromJson((JObject)json["abi"]), + Permissions = ((JArray)json["permissions"]).Select(u => ContractPermission.FromJson((JObject)u)).ToArray(), + Trusts = WildcardContainer.FromJson(json["trusts"], u => ContractPermissionDescriptor.FromJson((JString)u)), + Extra = (JObject)json["extra"] }; if (string.IsNullOrEmpty(manifest.Name)) throw new FormatException(); _ = manifest.Groups.ToDictionary(p => p.PubKey); - if (json["features"].Properties.Count != 0) + if (json["features"] is not JObject features || features.Count != 0) throw new FormatException(); if (manifest.SupportedStandards.Any(p => string.IsNullOrEmpty(p))) throw new FormatException(); @@ -139,7 +139,7 @@ public static ContractManifest FromJson(JObject json) public static ContractManifest Parse(ReadOnlySpan json) { if (json.Length > MaxLength) throw new ArgumentException(null, nameof(json)); - return FromJson(JObject.Parse(json)); + return FromJson((JObject)JToken.Parse(json)); } /// diff --git a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs index 69db0780a7..45041d54b3 100644 --- a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -65,7 +65,7 @@ public override StackItem ToStackItem(ReferenceCounter referenceCounter) ContractMethodDescriptor descriptor = new() { Name = json["name"].GetString(), - Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), + Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson((JObject)u)).ToArray(), ReturnType = Enum.Parse(json["returntype"].GetString()), Offset = json["offset"].GetInt32(), Safe = json["safe"].GetBoolean() diff --git a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs index 9375f47ea2..f54abd8c90 100644 --- a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; diff --git a/src/Neo/SmartContract/Manifest/ContractPermission.cs b/src/Neo/SmartContract/Manifest/ContractPermission.cs index e1539b6463..a99c63c52b 100644 --- a/src/Neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/Neo/SmartContract/Manifest/ContractPermission.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.VM; using Neo.VM.Types; using System; @@ -85,7 +85,7 @@ public static ContractPermission FromJson(JObject json) { ContractPermission permission = new() { - Contract = ContractPermissionDescriptor.FromJson(json["contract"]), + Contract = ContractPermissionDescriptor.FromJson((JString)json["contract"]), Methods = WildcardContainer.FromJson(json["methods"], u => u.GetString()), }; if (permission.Methods.Any(p => string.IsNullOrEmpty(p))) diff --git a/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs index 6a7e3b299e..133c338c6c 100644 --- a/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs @@ -10,7 +10,7 @@ using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using System; namespace Neo.SmartContract.Manifest @@ -120,7 +120,7 @@ public override int GetHashCode() /// /// The permission descriptor represented by a JSON object. /// The converted permission descriptor. - public static ContractPermissionDescriptor FromJson(JObject json) + public static ContractPermissionDescriptor FromJson(JString json) { string str = json.GetString(); if (str.Length == 42) @@ -136,7 +136,7 @@ public static ContractPermissionDescriptor FromJson(JObject json) /// Converts the permission descriptor to a JSON object. /// /// The permission descriptor represented by a JSON object. - public JObject ToJson() + public JString ToJson() { if (IsHash) return Hash.ToString(); if (IsGroup) return Group.ToString(); diff --git a/src/Neo/SmartContract/Manifest/WildCardContainer.cs b/src/Neo/SmartContract/Manifest/WildCardContainer.cs index 75718ea743..847090cbb1 100644 --- a/src/Neo/SmartContract/Manifest/WildCardContainer.cs +++ b/src/Neo/SmartContract/Manifest/WildCardContainer.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; using System; using System.Collections; using System.Collections.Generic; @@ -57,7 +57,7 @@ private WildcardContainer(T[] data) /// The list represented by a JSON object. /// A converter for elements. /// The converted list. - public static WildcardContainer FromJson(JObject json, Func elementSelector) + public static WildcardContainer FromJson(JToken json, Func elementSelector) { switch (json) { @@ -84,7 +84,7 @@ public IEnumerator GetEnumerator() /// Converts the list to a JSON object. /// /// The list represented by a JSON object. - public JObject ToJson(Func elementSelector) + public JToken ToJson(Func elementSelector) { if (IsWildcard) return "*"; return _data.Select(p => elementSelector(p)).ToArray(); diff --git a/src/Neo/SmartContract/MethodToken.cs b/src/Neo/SmartContract/MethodToken.cs index fd663dd461..09ed7b1eac 100644 --- a/src/Neo/SmartContract/MethodToken.cs +++ b/src/Neo/SmartContract/MethodToken.cs @@ -9,7 +9,7 @@ // modifications are permitted. using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using System; using System.IO; diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index ec96272f14..b36527fbc6 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -11,7 +11,7 @@ #pragma warning disable IDE0051 using Neo.Cryptography; -using Neo.IO.Json; +using Neo.Json; using Neo.VM.Types; using System; using System.Globalization; @@ -49,7 +49,7 @@ private static byte[] JsonSerialize(ApplicationEngine engine, StackItem item) [ContractMethod(CpuFee = 1 << 14)] private static StackItem JsonDeserialize(ApplicationEngine engine, byte[] json) { - return JsonSerializer.Deserialize(JObject.Parse(json, 10), engine.Limits, engine.ReferenceCounter); + return JsonSerializer.Deserialize(JToken.Parse(json, 10), engine.Limits, engine.ReferenceCounter); } /// diff --git a/src/Neo/SmartContract/NefFile.cs b/src/Neo/SmartContract/NefFile.cs index d31309e240..346b74bf4c 100644 --- a/src/Neo/SmartContract/NefFile.cs +++ b/src/Neo/SmartContract/NefFile.cs @@ -10,7 +10,7 @@ using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using System; using System.Buffers.Binary; using System.IO; diff --git a/src/Neo/VM/Helper.cs b/src/Neo/VM/Helper.cs index e49a438d79..7ff36e1ed5 100644 --- a/src/Neo/VM/Helper.cs +++ b/src/Neo/VM/Helper.cs @@ -10,7 +10,7 @@ using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM.Types; using System; @@ -308,7 +308,7 @@ private static JObject ToJson(StackItem item, HashSet context, ref in { ["type"] = item.Type }; - JObject value = null; + JToken value = null; maxSize -= 11/*{"type":""}*/+ item.Type.ToString().Length; switch (item) { diff --git a/src/Neo/Wallets/NEP6/NEP6Account.cs b/src/Neo/Wallets/NEP6/NEP6Account.cs index c0b2abd7b6..55bbc603c2 100644 --- a/src/Neo/Wallets/NEP6/NEP6Account.cs +++ b/src/Neo/Wallets/NEP6/NEP6Account.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; using System; using System.Threading; @@ -20,7 +20,7 @@ sealed class NEP6Account : WalletAccount private string nep2key; private string nep2KeyNew = null; private KeyPair key; - public JObject Extra; + public JToken Extra; public bool Decrypted => nep2key == null || key != null; public override bool HasKey => nep2key != null; @@ -45,7 +45,7 @@ public static NEP6Account FromJson(JObject json, NEP6Wallet wallet) Label = json["label"]?.GetString(), IsDefault = json["isDefault"].GetBoolean(), Lock = json["lock"].GetBoolean(), - Contract = NEP6Contract.FromJson(json["contract"]), + Contract = NEP6Contract.FromJson((JObject)json["contract"]), Extra = json["extra"] }; } diff --git a/src/Neo/Wallets/NEP6/NEP6Contract.cs b/src/Neo/Wallets/NEP6/NEP6Contract.cs index d6b959ce43..289ef4387c 100644 --- a/src/Neo/Wallets/NEP6/NEP6Contract.cs +++ b/src/Neo/Wallets/NEP6/NEP6Contract.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using System; using System.Linq; @@ -26,7 +26,7 @@ public static NEP6Contract FromJson(JObject json) return new NEP6Contract { Script = Convert.FromBase64String(json["script"].AsString()), - ParameterList = ((JArray)json["parameters"]).Select(p => p["type"].TryGetEnum()).ToArray(), + ParameterList = ((JArray)json["parameters"]).Select(p => p["type"].GetEnum()).ToArray(), ParameterNames = ((JArray)json["parameters"]).Select(p => p["name"].AsString()).ToArray(), Deployed = json["deployed"].AsBoolean() }; diff --git a/src/Neo/Wallets/NEP6/NEP6Wallet.cs b/src/Neo/Wallets/NEP6/NEP6Wallet.cs index e6d4c92fb4..5a22f5f32a 100644 --- a/src/Neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/Neo/Wallets/NEP6/NEP6Wallet.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using System; using System.Collections.Generic; @@ -30,7 +30,7 @@ public class NEP6Wallet : Wallet private string name; private Version version; private readonly Dictionary accounts; - private readonly JObject extra; + private readonly JToken extra; /// /// The parameters of the SCrypt algorithm used for encrypting and decrypting the private keys in the wallet. @@ -56,7 +56,7 @@ public NEP6Wallet(string path, string password, ProtocolSettings settings, strin this.password = password; if (File.Exists(path)) { - JObject wallet = JObject.Parse(File.ReadAllBytes(path)); + JObject wallet = (JObject)JToken.Parse(File.ReadAllBytes(path)); LoadFromJson(wallet, out Scrypt, out accounts, out extra); } else @@ -65,7 +65,7 @@ public NEP6Wallet(string path, string password, ProtocolSettings settings, strin this.version = Version.Parse("1.0"); this.Scrypt = ScryptParameters.Default; this.accounts = new Dictionary(); - this.extra = JObject.Null; + this.extra = JToken.Null; } } @@ -82,12 +82,12 @@ public NEP6Wallet(string path, string password, ProtocolSettings settings, JObje LoadFromJson(json, out Scrypt, out accounts, out extra); } - private void LoadFromJson(JObject wallet, out ScryptParameters scrypt, out Dictionary accounts, out JObject extra) + private void LoadFromJson(JObject wallet, out ScryptParameters scrypt, out Dictionary accounts, out JToken extra) { this.version = Version.Parse(wallet["version"].AsString()); this.name = wallet["name"]?.AsString(); - scrypt = ScryptParameters.FromJson(wallet["scrypt"]); - accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson(p, this)).ToDictionary(p => p.ScriptHash); + scrypt = ScryptParameters.FromJson((JObject)wallet["scrypt"]); + accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson((JObject)p, this)).ToDictionary(p => p.ScriptHash); extra = wallet["extra"]; if (!VerifyPasswordInternal(password)) throw new InvalidOperationException("Wrong password."); diff --git a/src/Neo/Wallets/NEP6/ScryptParameters.cs b/src/Neo/Wallets/NEP6/ScryptParameters.cs index 66c14f2bcf..aaa6e04b89 100644 --- a/src/Neo/Wallets/NEP6/ScryptParameters.cs +++ b/src/Neo/Wallets/NEP6/ScryptParameters.cs @@ -8,7 +8,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Json; +using Neo.Json; namespace Neo.Wallets.NEP6 { diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props new file mode 100644 index 0000000000..ba2678873c --- /dev/null +++ b/tests/Directory.Build.props @@ -0,0 +1,17 @@ + + + + + net6.0 + false + + + + + + + + + + + diff --git a/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj b/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj new file mode 100644 index 0000000000..3643b6ec17 --- /dev/null +++ b/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj @@ -0,0 +1,11 @@ + + + + enable + + + + + + + diff --git a/tests/Neo.UnitTests/IO/Json/UT_JArray.cs b/tests/Neo.Json.UnitTests/UT_JArray.cs similarity index 97% rename from tests/Neo.UnitTests/IO/Json/UT_JArray.cs rename to tests/Neo.Json.UnitTests/UT_JArray.cs index 913fb9b10b..e838be9c92 100644 --- a/tests/Neo.UnitTests/IO/Json/UT_JArray.cs +++ b/tests/Neo.Json.UnitTests/UT_JArray.cs @@ -1,11 +1,6 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using System; using System.Collections; -using System.Linq; -namespace Neo.UnitTests.IO.Json +namespace Neo.Json.UnitTests { enum Foo { diff --git a/tests/Neo.UnitTests/IO/Json/UT_JBoolean.cs b/tests/Neo.Json.UnitTests/UT_JBoolean.cs similarity index 77% rename from tests/Neo.UnitTests/IO/Json/UT_JBoolean.cs rename to tests/Neo.Json.UnitTests/UT_JBoolean.cs index ea080cd423..679c9b622d 100644 --- a/tests/Neo.UnitTests/IO/Json/UT_JBoolean.cs +++ b/tests/Neo.Json.UnitTests/UT_JBoolean.cs @@ -1,8 +1,4 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; - -namespace Neo.UnitTests.IO.Json +namespace Neo.Json.UnitTests { [TestClass] public class UT_JBoolean diff --git a/tests/Neo.UnitTests/IO/Json/UT_JNumber.cs b/tests/Neo.Json.UnitTests/UT_JNumber.cs similarity index 72% rename from tests/Neo.UnitTests/IO/Json/UT_JNumber.cs rename to tests/Neo.Json.UnitTests/UT_JNumber.cs index 677157a1f0..6156bc1cb5 100644 --- a/tests/Neo.UnitTests/IO/Json/UT_JNumber.cs +++ b/tests/Neo.Json.UnitTests/UT_JNumber.cs @@ -1,9 +1,4 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using System; - -namespace Neo.UnitTests.IO.Json +namespace Neo.Json.UnitTests { enum Woo { @@ -48,12 +43,15 @@ public void TestAsString() } [TestMethod] - public void TestTryGetEnum() + public void TestGetEnum() { - zero.TryGetEnum().Should().Be(Woo.Tom); - new JNumber(1).TryGetEnum().Should().Be(Woo.Jerry); - new JNumber(2).TryGetEnum().Should().Be(Woo.James); - new JNumber(3).TryGetEnum().Should().Be(Woo.Tom); + zero.GetEnum().Should().Be(Woo.Tom); + new JNumber(1).GetEnum().Should().Be(Woo.Jerry); + new JNumber(2).GetEnum().Should().Be(Woo.James); + new JNumber(3).AsEnum().Should().Be(Woo.Tom); + + Action action = () => new JNumber(3).GetEnum(); + action.Should().Throw(); } } } diff --git a/tests/Neo.UnitTests/IO/Json/UT_JObject.cs b/tests/Neo.Json.UnitTests/UT_JObject.cs similarity index 81% rename from tests/Neo.UnitTests/IO/Json/UT_JObject.cs rename to tests/Neo.Json.UnitTests/UT_JObject.cs index e2c394f5bb..2ff3d25c02 100644 --- a/tests/Neo.UnitTests/IO/Json/UT_JObject.cs +++ b/tests/Neo.Json.UnitTests/UT_JObject.cs @@ -1,9 +1,4 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using System; - -namespace Neo.UnitTests.IO.Json +namespace Neo.Json.UnitTests { [TestClass] public class UT_JObject @@ -77,24 +72,25 @@ public void TestParse() } [TestMethod] - public void TestTryGetEnum() + public void TestGetEnum() { - alice.TryGetEnum().Should().Be(Woo.Tom); + alice.AsEnum().Should().Be(Woo.Tom); + + Action action = () => alice.GetEnum(); + action.Should().Throw(); } [TestMethod] public void TestOpImplicitEnum() { - var obj = new JObject(); - obj = Woo.Tom; + JToken obj = Woo.Tom; obj.AsString().Should().Be("Tom"); } [TestMethod] public void TestOpImplicitString() { - var obj = new JObject(); - obj = null; + JToken obj = null; obj.Should().BeNull(); obj = "{\"aaa\":\"111\"}"; @@ -104,7 +100,7 @@ public void TestOpImplicitString() [TestMethod] public void TestGetNull() { - JObject.Null.Should().BeNull(); + JToken.Null.Should().BeNull(); } [TestMethod] @@ -114,7 +110,18 @@ public void TestClone() bobClone.Should().NotBeSameAs(bob); foreach (var key in bobClone.Properties.Keys) { - bobClone[key].Should().BeEquivalentTo(bob[key]); + switch (bob[key]) + { + case JToken.Null: + bobClone[key].Should().BeNull(); + break; + case JObject obj: + ((JObject)bobClone[key]).Properties.Should().BeEquivalentTo(obj.Properties); + break; + default: + bobClone[key].Should().BeEquivalentTo(bob[key]); + break; + } } } } diff --git a/tests/Neo.UnitTests/IO/Json/UT_JPath.cs b/tests/Neo.Json.UnitTests/UT_JPath.cs similarity index 97% rename from tests/Neo.UnitTests/IO/Json/UT_JPath.cs rename to tests/Neo.Json.UnitTests/UT_JPath.cs index d3d085c4d3..be2733c4ff 100644 --- a/tests/Neo.UnitTests/IO/Json/UT_JPath.cs +++ b/tests/Neo.Json.UnitTests/UT_JPath.cs @@ -1,9 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using System; -using System.Linq; - -namespace Neo.UnitTests.IO.Json +namespace Neo.Json.UnitTests { [TestClass] public class UT_JPath @@ -59,7 +54,7 @@ public class UT_JPath public void TestOOM() { var filter = "$" + string.Concat(Enumerable.Repeat("[0" + string.Concat(Enumerable.Repeat(",0", 64)) + "]", 6)); - Assert.ThrowsException(() => JObject.Parse("[[[[[[{}]]]]]]").JsonPath(filter)); + Assert.ThrowsException(() => JObject.Parse("[[[[[[{}]]]]]]")!.JsonPath(filter)); } [TestMethod] diff --git a/tests/Neo.UnitTests/IO/Json/UT_JString.cs b/tests/Neo.Json.UnitTests/UT_JString.cs similarity index 67% rename from tests/Neo.UnitTests/IO/Json/UT_JString.cs rename to tests/Neo.Json.UnitTests/UT_JString.cs index 0b6df4c087..3188bf0e59 100644 --- a/tests/Neo.UnitTests/IO/Json/UT_JString.cs +++ b/tests/Neo.Json.UnitTests/UT_JString.cs @@ -1,9 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.SmartContract; -using System; - -namespace Neo.UnitTests.IO +namespace Neo.Json.UnitTests { [TestClass] public class UT_JString @@ -43,15 +38,15 @@ public void TestAsNumber() } [TestMethod] - public void TestTryGetEnum() + public void TestGetEnum() { - JString s = new JString("Signature"); - ContractParameterType cp = s.TryGetEnum(ContractParameterType.Void, false); - Assert.AreEqual(ContractParameterType.Signature, cp); + JString s = "James"; + Woo woo = s.GetEnum(); + Assert.AreEqual(Woo.James, woo); - s = new JString(""); - cp = s.TryGetEnum(ContractParameterType.Void, false); - Assert.AreEqual(ContractParameterType.Void, cp); + s = ""; + woo = s.AsEnum(Woo.Jerry, false); + Assert.AreEqual(Woo.Jerry, woo); } } } diff --git a/tests/Neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs b/tests/Neo.Json.UnitTests/UT_OrderedDictionary.cs similarity index 92% rename from tests/Neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs rename to tests/Neo.Json.UnitTests/UT_OrderedDictionary.cs index 66b1dd0e7c..e8b112b711 100644 --- a/tests/Neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs +++ b/tests/Neo.Json.UnitTests/UT_OrderedDictionary.cs @@ -1,10 +1,6 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Caching; using System.Collections; -using System.Collections.Generic; -namespace Neo.UnitTests.IO.Caching +namespace Neo.Json.UnitTests { [TestClass] public class UT_OrderedDictionary @@ -57,7 +53,7 @@ public void TestSetAndGetItem() public void TestGetKeys() { var keys = od.Keys; - keys.Contains("a").Should().BeTrue(); + keys.Should().Contain("a"); keys.Count.Should().Be(3); } @@ -65,7 +61,7 @@ public void TestGetKeys() public void TestGetValues() { var values = od.Values; - values.Contains(1).Should().BeTrue(); + values.Should().Contain(1); values.Count.Should().Be(3); } diff --git a/tests/Neo.Json.UnitTests/Usings.cs b/tests/Neo.Json.UnitTests/Usings.cs new file mode 100644 index 0000000000..6f971bbc56 --- /dev/null +++ b/tests/Neo.Json.UnitTests/Usings.cs @@ -0,0 +1,2 @@ +global using FluentAssertions; +global using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/tests/Neo.UnitTests/Neo.UnitTests.csproj b/tests/Neo.UnitTests/Neo.UnitTests.csproj index 83a7602cee..53362b924d 100644 --- a/tests/Neo.UnitTests/Neo.UnitTests.csproj +++ b/tests/Neo.UnitTests/Neo.UnitTests.csproj @@ -1,22 +1,13 @@ - Exe - net6.0 - Neo.UnitTests - Neo.UnitTests true - false - - - - diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index cdad1f21d0..d839af965b 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -1,7 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; namespace Neo.UnitTests.Network.P2P.Payloads @@ -174,17 +174,17 @@ public void ToJson() jObj["index"].AsNumber().Should().Be(indexVal); jObj["nextconsensus"].AsString().Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); - JObject scObj = ((JArray)jObj["witnesses"])[0]; + JObject scObj = (JObject)jObj["witnesses"][0]; scObj["invocation"].AsString().Should().Be(""); scObj["verification"].AsString().Should().Be("EQ=="); jObj["tx"].Should().NotBeNull(); - JArray txObj = (JArray)jObj["tx"]; - txObj[0]["hash"].AsString().Should().Be("0xb9bbfb2804f7582fd4340f5d87d741242afd29d3a02a5c9caa9b67325dbe236c"); - txObj[0]["size"].AsNumber().Should().Be(53); - txObj[0]["version"].AsNumber().Should().Be(0); - ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); - txObj[0]["netfee"].AsString().Should().Be("0"); + JObject txObj = (JObject)jObj["tx"][0]; + txObj["hash"].AsString().Should().Be("0xb9bbfb2804f7582fd4340f5d87d741242afd29d3a02a5c9caa9b67325dbe236c"); + txObj["size"].AsNumber().Should().Be(53); + txObj["version"].AsNumber().Should().Be(0); + ((JArray)txObj["attributes"]).Count.Should().Be(0); + txObj["netfee"].AsString().Should().Be("0"); } } } diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index c2f9cd4fbc..98c49f31c4 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -2,7 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 83dd5c7806..d142ffc927 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -1,7 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.Wallets; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs index 4078d504ff..97a7ede352 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs @@ -1,6 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads.Conditions; namespace Neo.UnitTests.Network.P2P.Payloads @@ -41,7 +41,7 @@ public void TestFromJson2() var hash1 = UInt160.Zero; var hash2 = UInt160.Parse("0xd2a4cff31913016155e38e474a2c06d08be276cf"); var jstr = "{\"type\":\"Or\",\"expressions\":[{\"type\":\"And\",\"expressions\":[{\"type\":\"CalledByContract\",\"hash\":\"0x0000000000000000000000000000000000000000\"},{\"type\":\"ScriptHash\",\"hash\":\"0xd2a4cff31913016155e38e474a2c06d08be276cf\"}]},{\"type\":\"Or\",\"expressions\":[{\"type\":\"CalledByGroup\",\"group\":\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\"},{\"type\":\"Boolean\",\"expression\":true}]}]}"; - var json = JObject.Parse(jstr); + var json = (JObject)JToken.Parse(jstr); var condi = WitnessCondition.FromJson(json); var or_condi = (OrCondition)condi; Assert.AreEqual(2, or_condi.Expressions.Length); diff --git a/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index 2141cc03b0..7d895a6e3b 100644 --- a/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -1,6 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Manifest; @@ -87,7 +87,7 @@ public void ParseFromJson_Extra() public void TestDeserializeAndSerialize() { var expected = TestUtils.CreateDefaultManifest(); - expected.Extra = JObject.Parse(@"{""a"":123}"); + expected.Extra = (JObject)JToken.Parse(@"{""a"":123}"); var clone = new ContractManifest(); ((IInteroperable)clone).FromStackItem(expected.ToStackItem(null)); diff --git a/tests/Neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs index f983d65f09..9bd8dc111f 100644 --- a/tests/Neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs @@ -1,6 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract.Manifest; using System; using System.Collections; diff --git a/tests/Neo.UnitTests/SmartContract/UT_ContractParameter.cs b/tests/Neo.UnitTests/SmartContract/UT_ContractParameter.cs index 8b8dd8d22d..6a6db9e8dd 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_ContractParameter.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ContractParameter.cs @@ -1,7 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using System; using System.Collections.Generic; diff --git a/tests/Neo.UnitTests/SmartContract/UT_ContractState.cs b/tests/Neo.UnitTests/SmartContract/UT_ContractState.cs index ace7444eba..4e1eab87fe 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_ContractState.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ContractState.cs @@ -1,6 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.VM; diff --git a/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs index 7bea6ffc43..4b5e1618d8 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; diff --git a/tests/Neo.UnitTests/TestUtils.cs b/tests/Neo.UnitTests/TestUtils.cs index 5442a8ca18..e3cb45d16c 100644 --- a/tests/Neo.UnitTests/TestUtils.cs +++ b/tests/Neo.UnitTests/TestUtils.cs @@ -1,7 +1,7 @@ using FluentAssertions; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Manifest; diff --git a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index 520ad1f1fd..3090752ee8 100644 --- a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -1,7 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.Wallets; using Neo.Wallets.NEP6; diff --git a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs index aad22b2b73..73262e390e 100644 --- a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs @@ -1,6 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.SmartContract; using Neo.Wallets.NEP6; using System; @@ -22,7 +22,7 @@ public void TestFromJson() { string json = "{\"script\":\"IQPviR30wLfu+5N9IeoPuIzejg2Cp/8RhytecEeWna+062h0dHaq\"," + "\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false}"; - JObject @object = JObject.Parse(json); + JObject @object = (JObject)JToken.Parse(json); NEP6Contract nep6Contract = NEP6Contract.FromJson(@object); nep6Contract.Script.Should().BeEquivalentTo("2103ef891df4c0b7eefb937d21ea0fb88cde8e0d82a7ff11872b5e7047969dafb4eb68747476aa".HexToBytes()); @@ -54,14 +54,14 @@ public void TestToJson() JArray parameters = (JArray)@object["parameters"]; parameters.Count.Should().Be(2); - jString = (JString)(parameters[0]["name"]); + jString = (JString)parameters[0]["name"]; jString.Value.Should().Be("param1"); - jString = (JString)(parameters[0]["type"]); + jString = (JString)parameters[0]["type"]; jString.Value.Should().Be(ContractParameterType.Boolean.ToString()); - jString = (JString)(parameters[1]["name"]); + jString = (JString)parameters[1]["name"]; jString.Value.Should().Be("param2"); - jString = (JString)(parameters[1]["type"]); + jString = (JString)parameters[1]["type"]; jString.Value.Should().Be(ContractParameterType.Integer.ToString()); } } diff --git a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index c346758cf9..b7905a8f9c 100644 --- a/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -1,6 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.Wallets; diff --git a/tests/Neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs b/tests/Neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs index 65d29e9fce..0c3aba328c 100644 --- a/tests/Neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs +++ b/tests/Neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs @@ -1,6 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; +using Neo.Json; using Neo.Wallets.NEP6; namespace Neo.UnitTests.Wallets.NEP6