* Enable nullable reference types.
* Update target framework to .NET 9.
* Other changes to adapt to new version of C#.
This commit is contained in:
2025-08-08 17:46:19 +02:00
parent 94b76a1001
commit 9d965e68ae
9 changed files with 1462 additions and 1468 deletions

View File

@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016-2022 Valeriano Alfonso Rodriguez Copyright (c) 2016-2025 Valeriano Alfonso Rodriguez
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,129 +1,132 @@
using System.Collections.Generic; using System.Collections.Generic;
using Xunit; using Xunit;
namespace VAR.Json.Tests namespace VAR.Json.Tests;
public class JsonParser_Tests
{ {
public class JsonParser_Tests
{
#region Parse #region Parse
private class SwallowObject private class SwallowObject
{ {
public string Text { get; set; } public string? Text { get; set; }
public int Number { get; set; } public int Number { get; set; }
} }
[Fact] [Fact]
public void Parse__SwallowObject() public void Parse__SwallowObject()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.KnownTypes.Add(typeof(SwallowObject)); parser.KnownTypes.Add(typeof(SwallowObject));
SwallowObject result = parser.Parse(@"{""Text"": ""AAAA"", ""Number"": 42}") as SwallowObject; SwallowObject? result = parser.Parse(@"{""Text"": ""AAAA"", ""Number"": 42}") as SwallowObject;
Assert.False(parser.Tainted); Assert.False(parser.Tainted);
Assert.Equal("AAAA", result?.Text); Assert.Equal("AAAA", result?.Text);
Assert.Equal(42, result.Number); Assert.Equal(42, result?.Number);
} }
private class DeeperObject_L1 private class DeeperObject_L1
{ {
public string Name { get; set; } public string? Name { get; set; }
public SwallowObject Object { get; set; } public SwallowObject? Object { get; set; }
} }
[Fact] [Fact]
public void Parse__DeeperObject_L1() public void Parse__DeeperObject_L1()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.KnownTypes.Add(typeof(SwallowObject)); parser.KnownTypes.Add(typeof(SwallowObject));
parser.KnownTypes.Add(typeof(DeeperObject_L1)); parser.KnownTypes.Add(typeof(DeeperObject_L1));
DeeperObject_L1 result = DeeperObject_L1? result =
parser.Parse(@"{""Name"": ""Thing"", ""Object"": {""Text"": ""AAAA"", ""Number"": 42}}") as parser.Parse(@"{""Name"": ""Thing"", ""Object"": {""Text"": ""AAAA"", ""Number"": 42}}") as
DeeperObject_L1; DeeperObject_L1;
Assert.False(parser.Tainted); Assert.False(parser.Tainted);
Assert.Equal("Thing", result.Name); Assert.Equal("Thing", result?.Name);
Assert.Equal("AAAA", result.Object.Text); Assert.Equal("AAAA", result?.Object?.Text);
Assert.Equal(42, result.Object.Number); Assert.Equal(42, result?.Object?.Number);
} }
private class DeeperObject_L2 private class DeeperObject_L2
{ {
public int Count { get; set; } public int Count { get; set; }
public DeeperObject_L1 Object { get; set; } public DeeperObject_L1? Object { get; set; }
} }
[Fact] [Fact]
public void Parse__DeeperObject_L2() public void Parse__DeeperObject_L2()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.KnownTypes.Add(typeof(SwallowObject)); parser.KnownTypes.Add(typeof(SwallowObject));
parser.KnownTypes.Add(typeof(DeeperObject_L1)); parser.KnownTypes.Add(typeof(DeeperObject_L1));
parser.KnownTypes.Add(typeof(DeeperObject_L2)); parser.KnownTypes.Add(typeof(DeeperObject_L2));
DeeperObject_L2 result = DeeperObject_L2? result =
parser.Parse( parser.Parse(
@"{""Count"": 1, ""Object"": {""Name"": ""Thing"", ""Object"": {""Text"": ""AAAA"", ""Number"": 42}}}") @"{""Count"": 1, ""Object"": {""Name"": ""Thing"", ""Object"": {""Text"": ""AAAA"", ""Number"": 42}}}")
as DeeperObject_L2; as DeeperObject_L2;
Assert.False(parser.Tainted); Assert.False(parser.Tainted);
Assert.Equal(1, result.Count); Assert.NotNull(result);
Assert.Equal("Thing", result.Object.Name); Assert.Equal(1, result?.Count);
Assert.Equal("AAAA", result.Object.Object.Text); Assert.Equal("Thing", result?.Object?.Name);
Assert.Equal(42, result.Object.Object.Number); Assert.Equal("AAAA", result?.Object?.Object?.Text);
Assert.Equal(42, result?.Object?.Object?.Number);
} }
[Fact] [Fact]
public void Parse__SwallowObjectArray() public void Parse__SwallowObjectArray()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.KnownTypes.Add(typeof(SwallowObject)); parser.KnownTypes.Add(typeof(SwallowObject));
List<SwallowObject> result = parser.Parse(@"[{""Text"": ""AAAA"", ""Number"": 42}]") as List<SwallowObject>; List<SwallowObject>? result = parser.Parse(@"[{""Text"": ""AAAA"", ""Number"": 42}]") as List<SwallowObject>;
Assert.False(parser.Tainted); Assert.False(parser.Tainted);
Assert.NotNull(result);
Assert.Single(result); Assert.Single(result);
Assert.Equal("AAAA", result[0].Text); Assert.Equal("AAAA", result?[0].Text);
Assert.Equal(42, result[0].Number); Assert.Equal(42, result?[0].Number);
} }
private class DeeperObjectArray_L1 private class DeeperObjectArray_L1
{ {
public int Count { get; set; } public int Count { get; set; }
public List<SwallowObject> Array { get; set; } public List<SwallowObject>? Array { get; set; }
} }
[Fact] [Fact]
public void Parse__DeeperObjectArray_L1() public void Parse__DeeperObjectArray_L1()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.KnownTypes.Add(typeof(SwallowObject)); parser.KnownTypes.Add(typeof(SwallowObject));
parser.KnownTypes.Add(typeof(DeeperObjectArray_L1)); parser.KnownTypes.Add(typeof(DeeperObjectArray_L1));
DeeperObjectArray_L1 result = DeeperObjectArray_L1? result =
parser.Parse(@"{""Count"": 1, ""Array"": [{""Text"": ""AAAA"", ""Number"": 42}]}") as parser.Parse(@"{""Count"": 1, ""Array"": [{""Text"": ""AAAA"", ""Number"": 42}]}") as
DeeperObjectArray_L1; DeeperObjectArray_L1;
Assert.False(parser.Tainted); Assert.False(parser.Tainted);
Assert.Equal(1, result.Count); Assert.NotNull(result);
Assert.Equal("AAAA", result.Array[0].Text); Assert.Equal(1, result?.Count);
Assert.Equal(42, result.Array[0].Number); Assert.Equal("AAAA", result?.Array?[0].Text);
Assert.Equal(42, result?.Array?[0].Number);
} }
private class DeeperObjectArray_L2 private class DeeperObjectArray_L2
{ {
public string Name { get; set; } public string Name { get; set; } = string.Empty;
public List<DeeperObjectArray_L1> Objects { get; set; } public List<DeeperObjectArray_L1> Objects { get; set; } = [];
} }
[Fact] [Fact]
public void Parse__DeeperObjectArray_L2() public void Parse__DeeperObjectArray_L2()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.KnownTypes.Add(typeof(SwallowObject)); parser.KnownTypes.Add(typeof(SwallowObject));
parser.KnownTypes.Add(typeof(DeeperObjectArray_L1)); parser.KnownTypes.Add(typeof(DeeperObjectArray_L1));
parser.KnownTypes.Add(typeof(DeeperObjectArray_L2)); parser.KnownTypes.Add(typeof(DeeperObjectArray_L2));
DeeperObjectArray_L2 result = DeeperObjectArray_L2? result =
parser.Parse( parser.Parse(
@"{""Name"": ""Thing"", ""Objects"": [{""Count"": 1, ""Array"": [{""Text"": ""AAAA"", ""Number"": 42}]}]}") @"{""Name"": ""Thing"", ""Objects"": [{""Count"": 1, ""Array"": [{""Text"": ""AAAA"", ""Number"": 42}]}]}")
as DeeperObjectArray_L2; as DeeperObjectArray_L2;
Assert.False(parser.Tainted); Assert.False(parser.Tainted);
Assert.Equal("Thing", result.Name); Assert.Equal("Thing", result?.Name);
Assert.Equal(1, result.Objects[0].Count); Assert.Equal(1, result?.Objects[0].Count);
Assert.Equal("AAAA", result.Objects[0].Array[0].Text); Assert.Equal("AAAA", result?.Objects[0].Array?[0].Text);
Assert.Equal(42, result.Objects[0].Array[0].Number); Assert.Equal(42, result?.Objects[0].Array?[0].Number);
} }
#endregion Parse #endregion Parse
@@ -133,7 +136,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail01() public void Parse__Validity_Fail01()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"""A JSON payload should be an object or array, not a string."""); parser.Parse(@"""A JSON payload should be an object or array, not a string.""");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -141,7 +144,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail02() public void Parse__Validity_Fail02()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""Unclosed array"""); parser.Parse(@"[""Unclosed array""");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -149,7 +152,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail03() public void Parse__Validity_Fail03()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{unquoted_key: ""keys must be quoted""}"); parser.Parse(@"{unquoted_key: ""keys must be quoted""}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -157,7 +160,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail04() public void Parse__Validity_Fail04()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""extra comma"",]"); parser.Parse(@"[""extra comma"",]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -165,7 +168,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail05() public void Parse__Validity_Fail05()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""double extra comma"",,]"); parser.Parse(@"[""double extra comma"",,]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -173,7 +176,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail06() public void Parse__Validity_Fail06()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[ , ""<-- missing value""]"); parser.Parse(@"[ , ""<-- missing value""]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -181,7 +184,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail07() public void Parse__Validity_Fail07()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""Comma after the close""],"); parser.Parse(@"[""Comma after the close""],");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -189,7 +192,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail08() public void Parse__Validity_Fail08()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""Extra close""]]"); parser.Parse(@"[""Extra close""]]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -197,7 +200,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail09() public void Parse__Validity_Fail09()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Extra comma"": true,}"); parser.Parse(@"{""Extra comma"": true,}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -205,7 +208,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail10() public void Parse__Validity_Fail10()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Extra value after close"": true} ""misplaced quoted value"""); parser.Parse(@"{""Extra value after close"": true} ""misplaced quoted value""");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -213,7 +216,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail11() public void Parse__Validity_Fail11()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Illegal expression"": 1 + 2}"); parser.Parse(@"{""Illegal expression"": 1 + 2}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -221,7 +224,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail12() public void Parse__Validity_Fail12()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Illegal invocation"": alert()}"); parser.Parse(@"{""Illegal invocation"": alert()}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -229,7 +232,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail13() public void Parse__Validity_Fail13()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Numbers cannot have leading zeroes"": 013}"); parser.Parse(@"{""Numbers cannot have leading zeroes"": 013}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -237,7 +240,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail14() public void Parse__Validity_Fail14()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Numbers cannot be hex"": 0x14}"); parser.Parse(@"{""Numbers cannot be hex"": 0x14}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -245,7 +248,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail15() public void Parse__Validity_Fail15()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""Illegal backslash escape: \x15""]"); parser.Parse(@"[""Illegal backslash escape: \x15""]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -253,7 +256,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail16() public void Parse__Validity_Fail16()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[\naked]"); parser.Parse(@"[\naked]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -261,7 +264,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail17() public void Parse__Validity_Fail17()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""Illegal backslash escape: \017""]"); parser.Parse(@"[""Illegal backslash escape: \017""]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -269,7 +272,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail18() public void Parse__Validity_Fail18()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[[[[[[[[[[[[[[[[[[[[""Too deep""]]]]]]]]]]]]]]]]]]]]"); parser.Parse(@"[[[[[[[[[[[[[[[[[[[[""Too deep""]]]]]]]]]]]]]]]]]]]]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -277,7 +280,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail19() public void Parse__Validity_Fail19()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Missing colon"" null}"); parser.Parse(@"{""Missing colon"" null}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -285,7 +288,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail20() public void Parse__Validity_Fail20()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Double colon"":: null}"); parser.Parse(@"{""Double colon"":: null}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -293,7 +296,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail21() public void Parse__Validity_Fail21()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Comma instead of colon"", null}"); parser.Parse(@"{""Comma instead of colon"", null}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -301,7 +304,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail22() public void Parse__Validity_Fail22()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""Colon instead of comma"": false]"); parser.Parse(@"[""Colon instead of comma"": false]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -309,7 +312,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail23() public void Parse__Validity_Fail23()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""Bad value"", truth]"); parser.Parse(@"[""Bad value"", truth]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -317,7 +320,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail24() public void Parse__Validity_Fail24()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"['single quote']"); parser.Parse(@"['single quote']");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -325,7 +328,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail25() public void Parse__Validity_Fail25()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"["" tab character in string ""]"); parser.Parse(@"["" tab character in string ""]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -333,7 +336,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail26() public void Parse__Validity_Fail26()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""tab\ character\ in\ string\ ""]"); parser.Parse(@"[""tab\ character\ in\ string\ ""]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -341,7 +344,7 @@ namespace VAR.Json.Tests
[Fact] [Fact]
public void Parse__Validity_Fail27() public void Parse__Validity_Fail27()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""line parser.Parse(@"[""line
break""]"); break""]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
@@ -350,7 +353,7 @@ break""]");
[Fact] [Fact]
public void Parse__Validity_Fail28() public void Parse__Validity_Fail28()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""line\ parser.Parse(@"[""line\
break""]"); break""]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
@@ -359,7 +362,7 @@ break""]");
[Fact] [Fact]
public void Parse__Validity_Fail29() public void Parse__Validity_Fail29()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[0e]"); parser.Parse(@"[0e]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -367,7 +370,7 @@ break""]");
[Fact] [Fact]
public void Parse__Validity_Fail30() public void Parse__Validity_Fail30()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[0e+]"); parser.Parse(@"[0e+]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -375,7 +378,7 @@ break""]");
[Fact] [Fact]
public void Parse__Validity_Fail31() public void Parse__Validity_Fail31()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[0e+-1]"); parser.Parse(@"[0e+-1]");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -383,7 +386,7 @@ break""]");
[Fact] [Fact]
public void Parse__Validity_Fail32() public void Parse__Validity_Fail32()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{""Comma instead if closing brace"": true,"); parser.Parse(@"{""Comma instead if closing brace"": true,");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -391,7 +394,7 @@ break""]");
[Fact] [Fact]
public void Parse__Validity_Fail33() public void Parse__Validity_Fail33()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[""mismatch""}"); parser.Parse(@"[""mismatch""}");
Assert.True(parser.Tainted); Assert.True(parser.Tainted);
} }
@@ -399,7 +402,7 @@ break""]");
[Fact] [Fact]
public void Parse__Validity_Pass01() public void Parse__Validity_Pass01()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[ parser.Parse(@"[
""JSON Test Pattern pass1"", ""JSON Test Pattern pass1"",
{""object with 1 member"":[""array with 1 element""]}, {""object with 1 member"":[""array with 1 element""]},
@@ -464,7 +467,7 @@ break""]");
[Fact] [Fact]
public void Parse__Validity_Pass02() public void Parse__Validity_Pass02()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"[[[[[[[[[[[[[[[[[[[""Not too deep""]]]]]]]]]]]]]]]]]]]"); parser.Parse(@"[[[[[[[[[[[[[[[[[[[""Not too deep""]]]]]]]]]]]]]]]]]]]");
Assert.False(parser.Tainted); Assert.False(parser.Tainted);
} }
@@ -472,7 +475,7 @@ break""]");
[Fact] [Fact]
public void Parse__Validity_Pass03() public void Parse__Validity_Pass03()
{ {
JsonParser parser = new JsonParser(); JsonParser parser = new();
parser.Parse(@"{ parser.Parse(@"{
""JSON Test Pattern pass3"": { ""JSON Test Pattern pass3"": {
""The outermost value"": ""must be an object or array."", ""The outermost value"": ""must be an object or array."",
@@ -484,5 +487,4 @@ break""]");
} }
#endregion Validity tests #endregion Validity tests
}
} }

View File

@@ -1,9 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<LangVersion>default</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -52,4 +52,25 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PublicFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PublicFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="_" Suffix="" Style="aaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="_" Suffix="" Style="aaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=53eecf85_002Dd821_002D40e8_002Dac97_002Dfdb734542b84/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=61a991a4_002Dd0a3_002D4d19_002D90a5_002Df8f4d75c30c1/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local variables"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_VARIABLE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=669e5282_002Dfb4b_002D4e90_002D91e7_002D07d269d04b60/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=70345118_002D4b40_002D4ece_002D937c_002Dbbeb7a0b2e70/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=8284009d_002De743_002D4d89_002D9402_002Da5bf9a89b657/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Methods"&gt;&lt;ElementKinds&gt;&lt;Kind Name="METHOD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=8a85b61a_002D1024_002D4f87_002Db9ef_002D1fdae19930a1/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Parameters"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PARAMETER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=8b8504e3_002Df0be_002D4c14_002D9103_002Dc732f2bddc15/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Enum members"&gt;&lt;ElementKinds&gt;&lt;Kind Name="ENUM_MEMBER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb"&gt;&lt;ExtraRule Prefix="T" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4f433b8_002Dabcd_002D4e55_002Da08f_002D82e78cef0f0c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_CONSTANT" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a7a3339e_002D4e89_002D4319_002D9735_002Da9dc4cb74cc7/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Interfaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="INTERFACE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="I" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=c85a0503_002D4de2_002D40f1_002D9cd6_002Da4054c05d384/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Properties"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=c873eafb_002Dd57f_002D481d_002D8c93_002D77f6863c2f88/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="_" Suffix="" Style="aaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary> </wpf:ResourceDictionary>

View File

@@ -4,18 +4,18 @@ using System.Globalization;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
namespace VAR.Json namespace VAR.Json;
public class JsonParser
{ {
public class JsonParser
{
#region Declarations #region Declarations
private const int MaxRecursiveCount = 20; private const int MaxRecursiveCount = 20;
private ParserContext _ctx; private readonly ParserContext _ctx = new();
private bool _tainted; private bool _tainted;
private readonly List<Type> _knownTypes = new List<Type>(); private readonly List<Type> _knownTypes = [];
#endregion Declarations #endregion Declarations
@@ -29,8 +29,7 @@ namespace VAR.Json
#region Private methods #region Private methods
private static readonly Dictionary<Type, PropertyInfo[]> _dictProperties = private static readonly Dictionary<Type, PropertyInfo[]> _dictProperties = new();
new Dictionary<Type, PropertyInfo[]>();
private PropertyInfo[] Type_GetProperties(Type type) private PropertyInfo[] Type_GetProperties(Type type)
{ {
@@ -49,7 +48,7 @@ namespace VAR.Json
return typeProperties; return typeProperties;
} }
private float CompareToType(Dictionary<string, object> obj, Type type) private float CompareToType(Dictionary<string, object?> obj, Type type)
{ {
PropertyInfo[] typeProperties = Type_GetProperties(type); PropertyInfo[] typeProperties = Type_GetProperties(type);
int count = 0; int count = 0;
@@ -64,7 +63,7 @@ namespace VAR.Json
return count / (float)typeProperties.Length; return count / (float)typeProperties.Length;
} }
private object ConvertToType(Dictionary<string, object> obj, Type type) private object ConvertToType(Dictionary<string, object?> obj, Type type)
{ {
PropertyInfo[] typeProperties = Type_GetProperties(type); PropertyInfo[] typeProperties = Type_GetProperties(type);
object newObj = ObjectActivator.CreateInstance(type); object newObj = ObjectActivator.CreateInstance(type);
@@ -72,10 +71,10 @@ namespace VAR.Json
{ {
if (obj.ContainsKey(prop.Name)) if (obj.ContainsKey(prop.Name))
{ {
Type underliningType = Nullable.GetUnderlyingType(prop.PropertyType); Type? underliningType = Nullable.GetUnderlyingType(prop.PropertyType);
Type effectiveType = underliningType ?? prop.PropertyType; Type effectiveType = underliningType ?? prop.PropertyType;
object valueOrig = obj[prop.Name]; object? valueOrig = obj[prop.Name];
object valueDest; object? valueDest;
if (underliningType != null && valueOrig == null) if (underliningType != null && valueOrig == null)
{ {
valueDest = null; valueDest = null;
@@ -103,9 +102,9 @@ namespace VAR.Json
return newObj; return newObj;
} }
private object TryConvertToTypes(Dictionary<string, object> obj) private object TryConvertToTypes(Dictionary<string, object?> obj)
{ {
Type bestMatch = null; Type? bestMatch = null;
float bestMatchFactor = 0.0f; float bestMatchFactor = 0.0f;
foreach (Type type in _knownTypes) foreach (Type type in _knownTypes)
{ {
@@ -158,7 +157,7 @@ namespace VAR.Json
private string ParseQuotedString() private string ParseQuotedString()
{ {
StringBuilder scratch = new StringBuilder(); StringBuilder scratch = new();
char c = _ctx.SkipWhite(); char c = _ctx.SkipWhite();
if (c == '"') if (c == '"')
{ {
@@ -234,7 +233,7 @@ namespace VAR.Json
private string ParseSingleQuotedString() private string ParseSingleQuotedString()
{ {
StringBuilder scratch = new StringBuilder(); StringBuilder scratch = new();
char c = _ctx.SkipWhite(); char c = _ctx.SkipWhite();
if (c == '\'') if (c == '\'')
{ {
@@ -324,7 +323,7 @@ namespace VAR.Json
if (mustBeQuoted) { _tainted = true; } if (mustBeQuoted) { _tainted = true; }
StringBuilder scratch = new StringBuilder(); StringBuilder scratch = new();
while (!_ctx.AtEnd() while (!_ctx.AtEnd()
&& (char.IsLetter(c) || char.IsDigit(c) || c == '_')) && (char.IsLetter(c) || char.IsDigit(c) || c == '_'))
@@ -336,9 +335,9 @@ namespace VAR.Json
return scratch.ToString(); return scratch.ToString();
} }
private object ParseNumber() private object? ParseNumber()
{ {
StringBuilder scratch = new StringBuilder(); StringBuilder scratch = new();
bool isFloat = false; bool isFloat = false;
bool isExp = false; bool isExp = false;
int numberLenght = 0; int numberLenght = 0;
@@ -422,32 +421,25 @@ namespace VAR.Json
// Build number from the parsed string // Build number from the parsed string
string s = scratch.ToString(); string s = scratch.ToString();
if (isFloat) if (!isFloat) { return Convert.ToInt32(s); }
{
if (numberLenght < 17) if (numberLenght < 17)
{ {
return Convert.ToDouble(s, CultureInfo.InvariantCulture); return Convert.ToDouble(s, CultureInfo.InvariantCulture);
} }
else
{
return Convert.ToDecimal(s, CultureInfo.InvariantCulture); return Convert.ToDecimal(s, CultureInfo.InvariantCulture);
} }
}
else
{
return Convert.ToInt32(s);
}
}
private object ParseArray(int recursiveCount = 1) private object? ParseArray(int recursiveCount = 1)
{ {
// StrictRules: Mark as tainted when MaxRecursiveCount is exceeded // StrictRules: Mark as tainted when MaxRecursiveCount is exceeded
if (recursiveCount >= MaxRecursiveCount) { _tainted = true; } if (recursiveCount >= MaxRecursiveCount) { _tainted = true; }
bool correct = false; bool correct = false;
char c = _ctx.SkipWhite(); char c = _ctx.SkipWhite();
List<object> array = new List<object>(); List<object?> array = [];
Type arrayContentType = null; Type? arrayContentType = null;
bool hasSameType = true; bool hasSameType = true;
bool hasNulls = false; bool hasNulls = false;
if (c == '[') if (c == '[')
@@ -481,13 +473,13 @@ namespace VAR.Json
// StrictRules: Mark as tainted when unexpected value on array // StrictRules: Mark as tainted when unexpected value on array
if (expectValue == false) { _tainted = true; } if (expectValue == false) { _tainted = true; }
object value = ParseValue(recursiveCount + 1); object? value = ParseValue(recursiveCount + 1);
array.Add(value); array.Add(value);
expectValue = false; expectValue = false;
if (hasSameType) if (hasSameType)
{ {
Type valueType = value?.GetType(); Type? valueType = value?.GetType();
if (valueType == null) { hasNulls = true; } if (valueType == null) { hasNulls = true; }
if (arrayContentType == null || arrayContentType == valueType) if (arrayContentType == null || arrayContentType == valueType)
@@ -507,16 +499,16 @@ namespace VAR.Json
_tainted = true; _tainted = true;
} }
object result = array; object? result = array;
bool isNullableType = arrayContentType?.IsClass == true; bool isNullableType = arrayContentType?.IsClass == true;
if (hasSameType && arrayContentType != null && (isNullableType || (hasNulls == false))) if (hasSameType && arrayContentType != null && (isNullableType || (hasNulls == false)))
{ {
Type enumerableType = typeof(System.Linq.Enumerable); Type enumerableType = typeof(System.Linq.Enumerable);
MethodInfo castMethod = enumerableType.GetMethod("Cast")?.MakeGenericMethod(arrayContentType); MethodInfo? castMethod = enumerableType.GetMethod("Cast")?.MakeGenericMethod(arrayContentType);
MethodInfo toListMethod = enumerableType.GetMethod("ToList")?.MakeGenericMethod(arrayContentType); MethodInfo? toListMethod = enumerableType.GetMethod("ToList")?.MakeGenericMethod(arrayContentType);
IEnumerable<object> itemsToCast = array; IEnumerable<object?> itemsToCast = array;
object castedItems = castMethod?.Invoke(null, new object[] { itemsToCast }); object? castedItems = castMethod?.Invoke(null, [itemsToCast,]);
result = toListMethod?.Invoke(null, new[] { castedItems }); result = toListMethod?.Invoke(null, [castedItems,]);
} }
return result; return result;
@@ -529,13 +521,13 @@ namespace VAR.Json
bool correct = false; bool correct = false;
char c = _ctx.SkipWhite(); char c = _ctx.SkipWhite();
Dictionary<string, object> obj = new Dictionary<string, object>(); Dictionary<string, object?> obj = new Dictionary<string, object?>();
if (c == '{') if (c == '{')
{ {
_ctx.Next(); _ctx.Next();
} }
string attributeName = null; string attributeName = string.Empty;
bool? expectedKey = null; bool? expectedKey = null;
bool? expectedValue = null; bool? expectedValue = null;
do do
@@ -546,7 +538,7 @@ namespace VAR.Json
_ctx.Next(); _ctx.Next();
if (expectedValue == true) if (expectedValue == true)
{ {
object attributeValue = ParseValue(recursiveCount + 1); object? attributeValue = ParseValue(recursiveCount + 1);
obj.Add(attributeName, attributeValue); obj.Add(attributeName, attributeValue);
expectedKey = null; expectedKey = null;
expectedValue = false; expectedValue = false;
@@ -598,10 +590,10 @@ namespace VAR.Json
return result; return result;
} }
private object ParseValue(int recursiveCount = 1) private object? ParseValue(int recursiveCount = 1)
{ {
char c = _ctx.SkipWhite(); char c = _ctx.SkipWhite();
object token; object? token;
switch (c) switch (c)
{ {
case '"': case '"':
@@ -665,13 +657,13 @@ namespace VAR.Json
#region Public methods #region Public methods
public object Parse(string text) public object? Parse(string text)
{ {
// Get the first object // Get the first object
_ctx = new ParserContext(text); _ctx.SetText(text);
_tainted = false; _tainted = false;
_ctx.Mark(); _ctx.Mark();
object obj = ParseValue(); object? obj = ParseValue();
_ctx.SkipWhite(); _ctx.SkipWhite();
if (_ctx.AtEnd()) if (_ctx.AtEnd())
{ {
@@ -690,9 +682,9 @@ namespace VAR.Json
return obj; return obj;
} }
private static JsonParser _currentInstance; private static JsonParser? _currentInstance;
public static object ParseText(string text, params Type[] knownTypes) public static object? ParseText(string text, params Type[] knownTypes)
{ {
if (_currentInstance == null) if (_currentInstance == null)
{ {
@@ -705,5 +697,4 @@ namespace VAR.Json
} }
#endregion Public methods #endregion Public methods
}
} }

View File

@@ -5,10 +5,10 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
namespace VAR.Json namespace VAR.Json;
public class JsonWriterConfiguration
{ {
public class JsonWriterConfiguration
{
private readonly bool _indent; private readonly bool _indent;
public bool Indent => _indent; public bool Indent => _indent;
@@ -37,17 +37,14 @@ namespace VAR.Json
_indentThreshold = indentThreshold; _indentThreshold = indentThreshold;
} }
public bool Equals(JsonWriterConfiguration other) public bool Equals(JsonWriterConfiguration other) =>
{
return
other.Indent == Indent && other.Indent == Indent &&
other.UseTabForIndent == UseTabForIndent && other.UseTabForIndent == UseTabForIndent &&
other.IndentChars == IndentChars && other.IndentChars == IndentChars &&
other.IndentThreshold == IndentThreshold && other.IndentThreshold == IndentThreshold &&
true; true;
}
public override bool Equals(object other) public override bool Equals(object? other)
{ {
if (other is JsonWriterConfiguration configuration) if (other is JsonWriterConfiguration configuration)
{ {
@@ -59,13 +56,12 @@ namespace VAR.Json
public override int GetHashCode() public override int GetHashCode()
{ {
return _indent.GetHashCode() ^ _useTabForIndent.GetHashCode() ^ _indentChars.GetHashCode() ^ return _indent.GetHashCode() ^ _useTabForIndent.GetHashCode() ^ _indentChars.GetHashCode() ^ _indentThreshold.GetHashCode();
_indentThreshold.GetHashCode();
}
} }
}
public class JsonWriter public class JsonWriter
{ {
#region Declarations #region Declarations
private readonly JsonWriterConfiguration _config; private readonly JsonWriterConfiguration _config;
@@ -74,7 +70,7 @@ namespace VAR.Json
#region Creator #region Creator
public JsonWriter(JsonWriterConfiguration config = null) public JsonWriter(JsonWriterConfiguration? config = null)
{ {
_config = config ?? new JsonWriterConfiguration(); _config = config ?? new JsonWriterConfiguration();
} }
@@ -83,7 +79,7 @@ namespace VAR.Json
#region Private methods #region Private methods
private bool IsValue(object obj) private bool IsValue(object? obj)
{ {
if (obj == null) if (obj == null)
{ {
@@ -147,7 +143,7 @@ namespace VAR.Json
textWriter.Write('"'); textWriter.Write('"');
} }
private void WriteValue(TextWriter textWriter, object obj, List<object> parentLevels) private void WriteValue(TextWriter textWriter, object? obj, List<object?> parentLevels)
{ {
if (obj == null || obj is DBNull) if (obj == null || obj is DBNull)
{ {
@@ -197,7 +193,7 @@ namespace VAR.Json
} }
} }
private void WriteList(TextWriter textWriter, object obj, List<object> parentLevels) private void WriteList(TextWriter textWriter, object obj, List<object?> parentLevels)
{ {
IEnumerable list = ((IEnumerable)obj).Cast<object>().ToList(); IEnumerable list = ((IEnumerable)obj).Cast<object>().ToList();
int n = 0; int n = 0;
@@ -254,7 +250,7 @@ namespace VAR.Json
textWriter.Write(" ]"); textWriter.Write(" ]");
} }
private void WriteObject(TextWriter textWriter, object obj, List<object> parentLevels) private void WriteObject(TextWriter textWriter, object obj, List<object?> parentLevels)
{ {
IDictionary map = (IDictionary)obj; IDictionary map = (IDictionary)obj;
int n = map.Count; int n = map.Count;
@@ -287,7 +283,7 @@ namespace VAR.Json
foreach (object key in map.Keys) foreach (object key in map.Keys)
{ {
object value = map[key]; object? value = map[key];
if (!first) if (!first)
{ {
textWriter.Write(", "); textWriter.Write(", ");
@@ -298,7 +294,7 @@ namespace VAR.Json
} }
first = false; first = false;
WriteString(textWriter, Convert.ToString(key)); WriteString(textWriter, Convert.ToString(key) ?? string.Empty);
textWriter.Write(": "); textWriter.Write(": ");
parentLevels.Add(obj); parentLevels.Add(obj);
WriteValue(textWriter, value, parentLevels); WriteValue(textWriter, value, parentLevels);
@@ -313,11 +309,11 @@ namespace VAR.Json
textWriter.Write(" }"); textWriter.Write(" }");
} }
private void WriteReflectedObject(TextWriter textWriter, object obj, List<object> parentLevels) private void WriteReflectedObject(TextWriter textWriter, object obj, List<object?> parentLevels)
{ {
Type type = obj.GetType(); Type type = obj.GetType();
PropertyInfo[] rawProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); PropertyInfo[] rawProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
List<PropertyInfo> properties = new List<PropertyInfo>(); List<PropertyInfo> properties = [];
foreach (PropertyInfo property in rawProperties) foreach (PropertyInfo property in rawProperties)
{ {
if (property.CanRead == false) { continue; } if (property.CanRead == false) { continue; }
@@ -338,7 +334,7 @@ namespace VAR.Json
bool isLeaf = true; bool isLeaf = true;
foreach (PropertyInfo property in properties) foreach (PropertyInfo property in properties)
{ {
object value = property.GetValue(obj, null); object? value = property.GetValue(obj, null);
if (!IsValue(value)) if (!IsValue(value))
{ {
isLeaf = false; isLeaf = false;
@@ -356,9 +352,9 @@ namespace VAR.Json
foreach (PropertyInfo property in properties) foreach (PropertyInfo property in properties)
{ {
object value = null; object? value = null;
MethodInfo getMethod = property.GetGetMethod(); MethodInfo? getMethod = property.GetGetMethod();
ParameterInfo[] parameters = getMethod.GetParameters(); ParameterInfo[] parameters = getMethod?.GetParameters() ?? [];
if (parameters.Length == 0) if (parameters.Length == 0)
{ {
value = property.GetValue(obj, null); value = property.GetValue(obj, null);
@@ -401,35 +397,31 @@ namespace VAR.Json
#region Public methods #region Public methods
public TextWriter Write(object obj, TextWriter textWriter) public TextWriter Write(object obj, TextWriter? textWriter)
{ {
if (textWriter == null) textWriter ??= new StringWriter();
{
textWriter = new StringWriter();
}
WriteValue(textWriter, obj, new List<object>()); WriteValue(textWriter, obj, []);
return textWriter; return textWriter;
} }
public string Write(object obj) public string Write(object obj)
{ {
StringWriter textWriter = new StringWriter(); StringWriter textWriter = new();
WriteValue(textWriter, obj, new List<object>()); WriteValue(textWriter, obj, []);
return textWriter.ToString(); return textWriter.ToString();
} }
private static readonly Dictionary<JsonWriterConfiguration, JsonWriter> _dictInstances = private static readonly Dictionary<JsonWriterConfiguration, JsonWriter> _dictInstances = new();
new Dictionary<JsonWriterConfiguration, JsonWriter>();
public static string WriteObject(object obj, public static string WriteObject(object obj,
JsonWriterConfiguration config = null, JsonWriterConfiguration? config = null,
bool indent = false, bool indent = false,
bool useTabForIndent = false, bool useTabForIndent = false,
int indentChars = 4, int indentChars = 4,
int indentThreshold = 3) int indentThreshold = 3)
{ {
JsonWriter jsonWriter = null; JsonWriter? jsonWriter = null;
if (config != null) if (config != null)
{ {
@@ -465,7 +457,7 @@ namespace VAR.Json
return jsonWriter.Write(obj); return jsonWriter.Write(obj);
} }
JsonWriterConfiguration jsonWriterConfiguration = new JsonWriterConfiguration( JsonWriterConfiguration jsonWriterConfiguration = new(
indent: indent, indent: indent,
useTabForIndent: useTabForIndent, useTabForIndent: useTabForIndent,
indentChars: indentChars, indentChars: indentChars,
@@ -477,5 +469,4 @@ namespace VAR.Json
} }
#endregion Public methods #endregion Public methods
}
} }

View File

@@ -2,26 +2,24 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq.Expressions; using System.Linq.Expressions;
namespace VAR.Json namespace VAR.Json;
public static class ObjectActivator
{ {
public static class ObjectActivator private static readonly Dictionary<Type, Func<object>> _creators = new();
{
private static readonly Dictionary<Type, Func<object>> _creators = new Dictionary<Type, Func<object>>();
private static Func<object> GetLambdaNew(Type type) private static Func<object> GetLambdaNew(Type type)
{ {
lock (_creators) lock (_creators)
{ {
if (_creators.ContainsKey(type)) if (_creators.TryGetValue(type, out Func<object>? creator)) { return creator; }
{
return _creators[type];
}
NewExpression newExp = Expression.New(type); NewExpression newExp = Expression.New(type);
LambdaExpression lambda = Expression.Lambda(typeof(Func<object>), newExp); LambdaExpression lambda = Expression.Lambda(typeof(Func<object>), newExp);
Func<object> compiledLambdaNew = (Func<object>)lambda.Compile(); Func<object> compiledLambdaNew = (Func<object>)lambda.Compile();
_creators.Add(type, compiledLambdaNew); _creators.Add(type, compiledLambdaNew);
return _creators[type]; return _creators[type];
} }
} }
@@ -31,5 +29,4 @@ namespace VAR.Json
Func<object> creator = GetLambdaNew(type); Func<object> creator = GetLambdaNew(type);
return creator(); return creator();
} }
}
} }

View File

@@ -1,19 +1,19 @@
namespace VAR.Json namespace VAR.Json;
public class ParserContext
{ {
public class ParserContext
{
#region Declarations #region Declarations
private readonly string _text; private string _text = string.Empty;
private readonly int _length; private int _length;
private int _i; private int _i;
private int _markStart; private int _markStart;
#endregion Declarations #endregion Declarations
#region Creator #region Public methods
public ParserContext(string text) public void SetText(string text)
{ {
_text = text; _text = text;
_length = text.Length; _length = text.Length;
@@ -21,10 +21,6 @@
_markStart = 0; _markStart = 0;
} }
#endregion Creator
#region Public methods
public char SkipWhite() public char SkipWhite()
{ {
while (_i < _length && char.IsWhiteSpace(_text[_i])) while (_i < _length && char.IsWhiteSpace(_text[_i]))
@@ -67,19 +63,11 @@
{ {
return _text.Substring(_markStart, _i - _markStart); return _text.Substring(_markStart, _i - _markStart);
} }
else
{ return _markStart < _length
if (_markStart < _length) ? _text.Substring(_markStart, _length - _markStart)
{ : string.Empty;
return _text.Substring(_markStart, _length - _markStart);
}
else
{
return string.Empty;
}
}
} }
#endregion Public methods #endregion Public methods
}
} }

View File

@@ -1,9 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Nullable>enable</Nullable>
<LangVersion>default</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>VAR.Json</PackageId> <PackageId>VAR.Json</PackageId>