14 Commits
1_1_0 ... 1_2_0

49 changed files with 771 additions and 403 deletions

1
.gitignore vendored
View File

@@ -28,3 +28,4 @@ _ReSharper*/
*.userprefs *.userprefs
*.nupkg *.nupkg
/.vs/* /.vs/*
/packages/*

View File

@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016-2017 Valeriano Alfonso Rodriguez Copyright (c) 2016-2020 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

@@ -0,0 +1,479 @@
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace VAR.Json.Tests
{
[TestClass()]
public class JsonParser_Tests
{
#region Parse
public class SwallowObject
{
public string Text { get; set; }
public int Number { get; set; }
}
[TestMethod()]
public void Parse__SwallowObject()
{
JsonParser parser = new JsonParser();
parser.KnownTypes.Add(typeof(SwallowObject));
SwallowObject result = parser.Parse(@"{""Text"": ""AAAA"", ""Number"": 42}") as SwallowObject;
Assert.AreEqual(false, parser.Tainted);
Assert.AreEqual("AAAA", result.Text);
Assert.AreEqual(42, result.Number);
}
public class DeeperObject_L1
{
public string Name { get; set; }
public SwallowObject Object { get; set; }
}
[TestMethod()]
public void Parse__DeeperObject_L1()
{
JsonParser parser = new JsonParser();
parser.KnownTypes.Add(typeof(SwallowObject));
parser.KnownTypes.Add(typeof(DeeperObject_L1));
DeeperObject_L1 result = parser.Parse(@"{""Name"": ""Thing"", ""Object"": {""Text"": ""AAAA"", ""Number"": 42}}") as DeeperObject_L1;
Assert.AreEqual(false, parser.Tainted);
Assert.AreEqual("Thing", result.Name);
Assert.AreEqual("AAAA", result.Object.Text);
Assert.AreEqual(42, result.Object.Number);
}
public class DeeperObject_L2
{
public int Count { get; set; }
public DeeperObject_L1 Object { get; set; }
}
[TestMethod()]
public void Parse__DeeperObject_L2()
{
JsonParser parser = new JsonParser();
parser.KnownTypes.Add(typeof(SwallowObject));
parser.KnownTypes.Add(typeof(DeeperObject_L1));
parser.KnownTypes.Add(typeof(DeeperObject_L2));
DeeperObject_L2 result = parser.Parse(@"{""Count"": 1, ""Object"": {""Name"": ""Thing"", ""Object"": {""Text"": ""AAAA"", ""Number"": 42}}}") as DeeperObject_L2;
Assert.AreEqual(false, parser.Tainted);
Assert.AreEqual(1, result.Count);
Assert.AreEqual("Thing", result.Object.Name);
Assert.AreEqual("AAAA", result.Object.Object.Text);
Assert.AreEqual(42, result.Object.Object.Number);
}
[TestMethod()]
public void Parse__SwallowObjectArray()
{
JsonParser parser = new JsonParser();
parser.KnownTypes.Add(typeof(SwallowObject));
List<SwallowObject> result = parser.Parse(@"[{""Text"": ""AAAA"", ""Number"": 42}]") as List<SwallowObject>;
Assert.AreEqual(false, parser.Tainted);
Assert.AreEqual(1, result.Count);
Assert.AreEqual("AAAA", result[0].Text);
Assert.AreEqual(42, result[0].Number);
}
public class DeeperObjectArray_L1
{
public int Count { get; set; }
public List<SwallowObject> Array { get; set; }
}
[TestMethod()]
public void Parse__DeeperObjectArray_L1()
{
JsonParser parser = new JsonParser();
parser.KnownTypes.Add(typeof(SwallowObject));
parser.KnownTypes.Add(typeof(DeeperObjectArray_L1));
DeeperObjectArray_L1 result = parser.Parse(@"{""Count"": 1, ""Array"": [{""Text"": ""AAAA"", ""Number"": 42}]}") as DeeperObjectArray_L1;
Assert.AreEqual(false, parser.Tainted);
Assert.AreEqual(1, result.Count);
Assert.AreEqual("AAAA", result.Array[0].Text);
Assert.AreEqual(42, result.Array[0].Number);
}
public class DeeperObjectArray_L2
{
public string Name { get; set; }
public List<DeeperObjectArray_L1> Objects { get; set; }
}
[TestMethod()]
public void Parse__DeeperObjectArray_L2()
{
JsonParser parser = new JsonParser();
parser.KnownTypes.Add(typeof(SwallowObject));
parser.KnownTypes.Add(typeof(DeeperObjectArray_L1));
parser.KnownTypes.Add(typeof(DeeperObjectArray_L2));
DeeperObjectArray_L2 result = parser.Parse(@"{""Name"": ""Thing"", ""Objects"": [{""Count"": 1, ""Array"": [{""Text"": ""AAAA"", ""Number"": 42}]}]}") as DeeperObjectArray_L2;
Assert.AreEqual(false, parser.Tainted);
Assert.AreEqual("Thing", result.Name);
Assert.AreEqual(1, result.Objects[0].Count);
Assert.AreEqual("AAAA", result.Objects[0].Array[0].Text);
Assert.AreEqual(42, result.Objects[0].Array[0].Number);
}
#endregion Parse
#region Validity tests
[TestMethod()]
public void Parse__Validity_Fail01()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"""A JSON payload should be an object or array, not a string.""");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail02()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""Unclosed array""");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail03()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{unquoted_key: ""keys must be quoted""}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail04()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""extra comma"",]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail05()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""double extra comma"",,]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail06()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[ , ""<-- missing value""]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail07()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""Comma after the close""],");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail08()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""Extra close""]]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail09()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Extra comma"": true,}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail10()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Extra value after close"": true} ""misplaced quoted value""");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail11()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Illegal expression"": 1 + 2}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail12()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Illegal invocation"": alert()}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail13()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Numbers cannot have leading zeroes"": 013}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail14()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Numbers cannot be hex"": 0x14}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail15()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""Illegal backslash escape: \x15""]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail16()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[\naked]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail17()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""Illegal backslash escape: \017""]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail18()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[[[[[[[[[[[[[[[[[[[[""Too deep""]]]]]]]]]]]]]]]]]]]]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail19()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Missing colon"" null}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail20()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Double colon"":: null}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail21()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Comma instead of colon"", null}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail22()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""Colon instead of comma"": false]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail23()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""Bad value"", truth]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail24()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"['single quote']");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail25()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"["" tab character in string ""]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail26()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""tab\ character\ in\ string\ ""]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail27()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""line
break""]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail28()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""line\
break""]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail29()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[0e]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail30()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[0e+]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail31()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[0e+-1]");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail32()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{""Comma instead if closing brace"": true,");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Fail33()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[""mismatch""}");
Assert.AreEqual(true, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Pass01()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[
""JSON Test Pattern pass1"",
{""object with 1 member"":[""array with 1 element""]},
{},
[],
-42,
true,
false,
null,
{
""integer"": 1234567890,
""real"": -9876.543210,
""e"": 0.123456789e-12,
""E"": 1.234567890E+34,
"""": 23456789012E66,
""zero"": 0,
""one"": 1,
""space"": "" "",
""quote"": ""\"""",
""backslash"": ""\\"",
""controls"": ""\b\f\n\r\t"",
""slash"": ""/ & \/"",
""alpha"": ""abcdefghijklmnopqrstuvwyz"",
""ALPHA"": ""ABCDEFGHIJKLMNOPQRSTUVWYZ"",
""digit"": ""0123456789"",
""0123456789"": ""digit"",
""special"": ""`1~!@#$%^&*()_+-={':[,]}|;.</>?"",
""hex"": ""\u0123\u4567\u89AB\uCDEF\uabcd\uef4A"",
""true"": true,
""false"": false,
""null"": null,
""array"":[ ],
""object"":{ },
""address"": ""50 St. James Street"",
""url"": ""http://www.JSON.org/"",
""comment"": ""// /* <!-- --"",
""# -- --> */"": "" "",
"" s p a c e d "" :[1,2 , 3
,
4 , 5 , 6 ,7 ],""compact"":[1,2,3,4,5,6,7],
""jsontext"": ""{\""object with 1 member\"":[\""array with 1 element\""]}"",
""quotes"": ""&#34; \u0022 %22 0x22 034 &#x22;"",
""\/\\\""\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?""
: ""A key can be any string""
},
0.5 ,98.6
,
99.44
,
1066,
1e1,
0.1e1,
1e-1,
1e00,2e+00,2e-00
,""rosebud""]");
Assert.AreEqual(false, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Pass02()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"[[[[[[[[[[[[[[[[[[[""Not too deep""]]]]]]]]]]]]]]]]]]]");
Assert.AreEqual(false, parser.Tainted);
}
[TestMethod()]
public void Parse__Validity_Pass03()
{
JsonParser parser = new JsonParser();
object result = parser.Parse(@"{
""JSON Test Pattern pass3"": {
""The outermost value"": ""must be an object or array."",
""In this test"": ""It is an object.""
}
}
");
Assert.AreEqual(false, parser.Tainted);
}
#endregion Validity tests
}
}

View File

@@ -1,129 +0,0 @@
using System;
using System.IO;
using System.Text;
namespace VAR.Json.Tests
{
class Program
{
static void Main(string[] args)
{
// http://www.json.org/JSON_checker/
string currentPath = System.Reflection.Assembly.GetEntryAssembly().Location;
currentPath = FindPath(currentPath, "tests");
// Test all files
string[] files;
files = Directory.GetFiles(currentPath, "*.json");
foreach (string file in files)
{
TestFile(file);
}
Console.Read();
}
private static void TestFile(string fileName)
{
string testName = Path.GetFileNameWithoutExtension(fileName);
string fileContent = File.ReadAllText(fileName, Encoding.UTF8);
if (testName.StartsWith("fail"))
{
TestFailCase(testName, fileContent);
}
if (testName.StartsWith("pass"))
{
TestPassCase(testName, fileContent);
}
}
private static void TestFailCase(string testName, string fileContent)
{
JsonParser parser = new JsonParser();
object result;
try
{
result = parser.Parse(fileContent);
}
catch (Exception ex)
{
OutputFailure(testName, fileContent, ex);
return;
}
if (parser.Tainted == false)
{
OutputFailure(testName, fileContent, result);
return;
}
Console.Out.WriteLine("OK! {0}", testName);
}
private static void TestPassCase(string testName, string fileContent)
{
JsonParser parser = new JsonParser();
object result;
try
{
result = parser.Parse(fileContent);
}
catch (Exception ex)
{
OutputFailure(testName, fileContent, ex);
return;
}
if (parser.Tainted)
{
OutputFailure(testName, fileContent, result);
return;
}
Console.Out.WriteLine("OK! {0}", testName);
}
private static void OutputFailure(string testName, string fileContent, object obj)
{
Console.Out.WriteLine("Failure! {0}", testName);
Console.Out.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Console.Out.WriteLine("Content:\n{0}", fileContent);
Console.Out.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
if (obj is Exception)
{
Exception ex = obj as Exception;
Console.Out.WriteLine("Ex.Message: {0}", ex.Message);
Console.Out.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Console.Out.WriteLine("Ex.Stacktrace:\n{0}", ex.StackTrace);
Console.Out.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
if (obj != null && (obj is Exception) == false)
{
JsonWriter writter = new JsonWriter(new JsonWriterConfiguration(indent: true));
Console.Out.WriteLine("Parsed:\n{0}", writter.Write(obj));
Console.Out.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
}
private static string FindPath(string currentPath, string directory)
{
do
{
string testPath = Path.Combine(currentPath, directory);
if (Directory.Exists(testPath))
{
currentPath = testPath;
Console.Out.WriteLine(testPath);
break;
}
else
{
DirectoryInfo dirInfo = Directory.GetParent(currentPath);
if (dirInfo == null)
{
throw new Exception(string.Format("FindPath: Directory {0} not found", directory));
}
currentPath = dirInfo.ToString();
}
} while (string.IsNullOrEmpty(currentPath) == false);
return currentPath;
}
}
}

View File

@@ -1,14 +1,36 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("VAR.Json.Tests")] [assembly: AssemblyTitle("VAR.Json.Tests")]
[assembly: AssemblyDescription("Json Tests")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("VAR")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("VAR.Json.Tests")] [assembly: AssemblyProduct("VAR.Json.Tests")]
[assembly: AssemblyCopyright("Copyright © VAR 2016")] [assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
[assembly: Guid("576297b8-423d-4533-b75a-f186ccff0d2a")]
[assembly: AssemblyVersion("1.0.*")] // The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b92ac920-87d7-46de-afd8-d9c5eff7debe")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -1,20 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{576297B8-423D-4533-B75A-F186CCFF0D2A}</ProjectGuid> <ProjectGuid>{B92AC920-87D7-46DE-AFD8-D9C5EFF7DEBE}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VAR.Json.Tests</RootNamespace> <RootNamespace>VAR.Json.Tests</RootNamespace>
<AssemblyName>VAR.Json.Tests</AssemblyName> <AssemblyName>VAR.Json.Tests</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
@@ -24,7 +31,6 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
@@ -33,58 +39,63 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup> </ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup> <ItemGroup>
<Compile Include="Program.cs" /> <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="JsonParser_Tests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="tests\fail01.json" /> <None Include="packages.config" />
<None Include="tests\fail10.json" />
<None Include="tests\fail11.json" />
<None Include="tests\fail12.json" />
<None Include="tests\fail13.json" />
<None Include="tests\fail14.json" />
<None Include="tests\fail15.json" />
<None Include="tests\fail16.json" />
<None Include="tests\fail17.json" />
<None Include="tests\fail18.json" />
<None Include="tests\fail19.json" />
<None Include="tests\fail02.json" />
<None Include="tests\fail20.json" />
<None Include="tests\fail21.json" />
<None Include="tests\fail22.json" />
<None Include="tests\fail23.json" />
<None Include="tests\fail24.json" />
<None Include="tests\fail25.json" />
<None Include="tests\fail26.json" />
<None Include="tests\fail27.json" />
<None Include="tests\fail28.json" />
<None Include="tests\fail29.json" />
<None Include="tests\fail03.json" />
<None Include="tests\fail30.json" />
<None Include="tests\fail31.json" />
<None Include="tests\fail32.json" />
<None Include="tests\fail33.json" />
<None Include="tests\fail04.json" />
<None Include="tests\fail05.json" />
<None Include="tests\fail06.json" />
<None Include="tests\fail07.json" />
<None Include="tests\fail08.json" />
<None Include="tests\fail09.json" />
<None Include="tests\pass01.json" />
<None Include="tests\pass02.json" />
<None Include="tests\pass03.json" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\VAR.Json\VAR.Json.csproj"> <ProjectReference Include="..\VAR.Json\VAR.Json.csproj">
<Project>{28b3f937-145c-4fd4-a75b-a25ea4cc0428}</Project> <Project>{28B3F937-145C-4FD4-A75B-A25EA4CC0428}</Project>
<Name>VAR.Json</Name> <Name>VAR.Json</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MSTest.TestAdapter" version="2.1.1" targetFramework="net461" />
<package id="MSTest.TestFramework" version="2.1.1" targetFramework="net461" />
</packages>

View File

@@ -1 +0,0 @@
"A JSON payload should be an object or array, not a string."

View File

@@ -1 +0,0 @@
["Unclosed array"

View File

@@ -1 +0,0 @@
{unquoted_key: "keys must be quoted"}

View File

@@ -1 +0,0 @@
["extra comma",]

View File

@@ -1 +0,0 @@
["double extra comma",,]

View File

@@ -1 +0,0 @@
[ , "<-- missing value"]

View File

@@ -1 +0,0 @@
["Comma after the close"],

View File

@@ -1 +0,0 @@
["Extra close"]]

View File

@@ -1 +0,0 @@
{"Extra comma": true,}

View File

@@ -1 +0,0 @@
{"Extra value after close": true} "misplaced quoted value"

View File

@@ -1 +0,0 @@
{"Illegal expression": 1 + 2}

View File

@@ -1 +0,0 @@
{"Illegal invocation": alert()}

View File

@@ -1 +0,0 @@
{"Numbers cannot have leading zeroes": 013}

View File

@@ -1 +0,0 @@
{"Numbers cannot be hex": 0x14}

View File

@@ -1 +0,0 @@
["Illegal backslash escape: \x15"]

View File

@@ -1 +0,0 @@
[\naked]

View File

@@ -1 +0,0 @@
["Illegal backslash escape: \017"]

View File

@@ -1 +0,0 @@
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]

View File

@@ -1 +0,0 @@
{"Missing colon" null}

View File

@@ -1 +0,0 @@
{"Double colon":: null}

View File

@@ -1 +0,0 @@
{"Comma instead of colon", null}

View File

@@ -1 +0,0 @@
["Colon instead of comma": false]

View File

@@ -1 +0,0 @@
["Bad value", truth]

View File

@@ -1 +0,0 @@
['single quote']

View File

@@ -1 +0,0 @@
[" tab character in string "]

View File

@@ -1 +0,0 @@
["tab\ character\ in\ string\ "]

View File

@@ -1,2 +0,0 @@
["line
break"]

View File

@@ -1,2 +0,0 @@
["line\
break"]

View File

@@ -1 +0,0 @@
[0e]

View File

@@ -1 +0,0 @@
[0e+]

View File

@@ -1 +0,0 @@
[0e+-1]

View File

@@ -1 +0,0 @@
{"Comma instead if closing brace": true,

View File

@@ -1 +0,0 @@
["mismatch"}

View File

@@ -1,58 +0,0 @@
[
"JSON Test Pattern pass1",
{"object with 1 member":["array with 1 element"]},
{},
[],
-42,
true,
false,
null,
{
"integer": 1234567890,
"real": -9876.543210,
"e": 0.123456789e-12,
"E": 1.234567890E+34,
"": 23456789012E66,
"zero": 0,
"one": 1,
"space": " ",
"quote": "\"",
"backslash": "\\",
"controls": "\b\f\n\r\t",
"slash": "/ & \/",
"alpha": "abcdefghijklmnopqrstuvwyz",
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
"digit": "0123456789",
"0123456789": "digit",
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
"true": true,
"false": false,
"null": null,
"array":[ ],
"object":{ },
"address": "50 St. James Street",
"url": "http://www.JSON.org/",
"comment": "// /* <!-- --",
"# -- --> */": " ",
" s p a c e d " :[1,2 , 3
,
4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
"quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
: "A key can be any string"
},
0.5 ,98.6
,
99.44
,
1066,
1e1,
0.1e1,
1e-1,
1e00,2e+00,2e-00
,"rosebud"]

View File

@@ -1 +0,0 @@
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]

View File

@@ -1,6 +0,0 @@
{
"JSON Test Pattern pass3": {
"The outermost value": "must be an object or array.",
"In this test": "It is an object."
}
}

View File

@@ -1,18 +1,18 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio Version 16
VisualStudioVersion = 14.0.25420.1 VisualStudioVersion = 16.0.30330.147
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.Json", "VAR.Json\VAR.Json.csproj", "{28B3F937-145C-4FD4-A75B-A25EA4CC0428}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.Json", "VAR.Json\VAR.Json.csproj", "{28B3F937-145C-4FD4-A75B-A25EA4CC0428}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.Json.Tests", "VAR.Json.Tests\VAR.Json.Tests.csproj", "{576297B8-423D-4533-B75A-F186CCFF0D2A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notes", "Notes", "{4C23A421-5348-48F1-8B67-A4D43E616FDE}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notes", "Notes", "{4C23A421-5348-48F1-8B67-A4D43E616FDE}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
LICENSE.txt = LICENSE.txt LICENSE.txt = LICENSE.txt
README.md = README.md README.md = README.md
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.Json.Tests", "VAR.Json.Tests\VAR.Json.Tests.csproj", "{B92AC920-87D7-46DE-AFD8-D9C5EFF7DEBE}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -23,12 +23,15 @@ Global
{28B3F937-145C-4FD4-A75B-A25EA4CC0428}.Debug|Any CPU.Build.0 = Debug .Net 4.6.1|Any CPU {28B3F937-145C-4FD4-A75B-A25EA4CC0428}.Debug|Any CPU.Build.0 = Debug .Net 4.6.1|Any CPU
{28B3F937-145C-4FD4-A75B-A25EA4CC0428}.Release|Any CPU.ActiveCfg = Release .Net 4.6.1|Any CPU {28B3F937-145C-4FD4-A75B-A25EA4CC0428}.Release|Any CPU.ActiveCfg = Release .Net 4.6.1|Any CPU
{28B3F937-145C-4FD4-A75B-A25EA4CC0428}.Release|Any CPU.Build.0 = Release .Net 4.6.1|Any CPU {28B3F937-145C-4FD4-A75B-A25EA4CC0428}.Release|Any CPU.Build.0 = Release .Net 4.6.1|Any CPU
{576297B8-423D-4533-B75A-F186CCFF0D2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B92AC920-87D7-46DE-AFD8-D9C5EFF7DEBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{576297B8-423D-4533-B75A-F186CCFF0D2A}.Debug|Any CPU.Build.0 = Debug|Any CPU {B92AC920-87D7-46DE-AFD8-D9C5EFF7DEBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{576297B8-423D-4533-B75A-F186CCFF0D2A}.Release|Any CPU.ActiveCfg = Release|Any CPU {B92AC920-87D7-46DE-AFD8-D9C5EFF7DEBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{576297B8-423D-4533-B75A-F186CCFF0D2A}.Release|Any CPU.Build.0 = Release|Any CPU {B92AC920-87D7-46DE-AFD8-D9C5EFF7DEBE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B9700B54-1919-4B81-B123-D4D3DE74124A}
EndGlobalSection
EndGlobal EndGlobal

View File

@@ -91,9 +91,16 @@ namespace VAR.Json
valueDest = new Guid((string)valueOrig); valueDest = new Guid((string)valueOrig);
} }
else else
{
try
{ {
valueDest = Convert.ChangeType(obj[prop.Name], effectiveType); valueDest = Convert.ChangeType(obj[prop.Name], effectiveType);
} }
catch (Exception)
{
continue;
}
}
prop.SetValue(newObj, valueDest, null); prop.SetValue(newObj, valueDest, null);
} }
} }
@@ -115,7 +122,12 @@ namespace VAR.Json
} }
if (bestMatch != null) if (bestMatch != null)
{ {
return ConvertToType(obj, bestMatch); try
{
object newObj = ConvertToType(obj, bestMatch);
return newObj;
}
catch (Exception) { } /* Nom Nom */
} }
return obj; return obj;
} }
@@ -416,7 +428,7 @@ namespace VAR.Json
} }
} }
private List<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; }
@@ -424,6 +436,9 @@ namespace VAR.Json
bool correct = false; bool correct = false;
char c = _ctx.SkipWhite(); char c = _ctx.SkipWhite();
List<object> array = new List<object>(); List<object> array = new List<object>();
Type arrayContentType = null;
bool hasSameType = true;
bool hasNulls = false;
if (c == '[') if (c == '[')
{ {
_ctx.Next(); _ctx.Next();
@@ -452,19 +467,44 @@ 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);
array.Add(ParseValue(recursiveCount + 1)); array.Add(value);
expectValue = false; expectValue = false;
if (hasSameType)
{
Type valueType = value?.GetType();
if (valueType == null) { hasNulls = true; }
if (arrayContentType == null || arrayContentType == valueType)
{
arrayContentType = valueType;
}
else
{
hasSameType = false;
}
}
} }
} while (!_ctx.AtEnd()); } while (!_ctx.AtEnd());
if (correct == false) if (correct == false)
{ {
_tainted = true; _tainted = true;
} }
return array; object result = array;
bool isNullableType = arrayContentType?.IsClass == true;
if (hasSameType && arrayContentType != null && (isNullableType == true || (isNullableType == false && hasNulls == false)))
{
var enumerableType = typeof(System.Linq.Enumerable);
var castMethod = enumerableType.GetMethod("Cast").MakeGenericMethod(arrayContentType);
var toListMethod = enumerableType.GetMethod("ToList").MakeGenericMethod(arrayContentType);
IEnumerable<object> itemsToCast = array;
var castedItems = castMethod.Invoke(null, new[] { itemsToCast });
result = toListMethod.Invoke(null, new[] { castedItems });
}
return result;
} }
private Dictionary<string, object> ParseObject(int recursiveCount = 1) private object ParseObject(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; }
@@ -533,7 +573,8 @@ namespace VAR.Json
{ {
_tainted = true; _tainted = true;
} }
return obj; object result = TryConvertToTypes(obj);
return result;
} }
private object ParseValue(int recusiveCount = 1) private object ParseValue(int recusiveCount = 1)
@@ -553,8 +594,7 @@ namespace VAR.Json
break; break;
case '{': case '{':
Dictionary<string, object> obj = ParseObject(recusiveCount); token = ParseObject(recusiveCount);
token = TryConvertToTypes(obj);
break; break;
case '[': case '[':
@@ -628,12 +668,14 @@ namespace VAR.Json
private static JsonParser _currentInstance = null; private static JsonParser _currentInstance = null;
public static object ParseText(string text) public static object ParseText(string text, params Type[] knownTypes)
{ {
if(_currentInstance == null) if (_currentInstance == null)
{ {
_currentInstance = new JsonParser(); _currentInstance = new JsonParser();
} }
_currentInstance.KnownTypes.Clear();
_currentInstance.KnownTypes.AddRange(knownTypes);
return _currentInstance.Parse(text); return _currentInstance.Parse(text);
} }

View File

@@ -1,8 +1,8 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Reflection; using System.Reflection;
using System.Text;
namespace VAR.Json namespace VAR.Json
{ {
@@ -22,12 +22,12 @@ namespace VAR.Json
public JsonWriterConfiguration( public JsonWriterConfiguration(
bool indent = false, bool indent = false,
bool useTablForIndent = false, bool useTabForIndent = false,
int indentChars = 4, int indentChars = 4,
int indentThresold = 3) int indentThresold = 3)
{ {
_indent = indent; _indent = indent;
_useTabForIndent = useTablForIndent; _useTabForIndent = useTabForIndent;
_indentChars = indentChars; _indentChars = indentChars;
_indentThresold = indentThresold; _indentThresold = indentThresold;
} }
@@ -44,7 +44,7 @@ namespace VAR.Json
public override bool Equals(object other) public override bool Equals(object other)
{ {
if(other is JsonWriterConfiguration) if (other is JsonWriterConfiguration)
{ {
return Equals(other as JsonWriterConfiguration); return Equals(other as JsonWriterConfiguration);
} }
@@ -101,52 +101,52 @@ namespace VAR.Json
return false; return false;
} }
private void WriteIndent(StringBuilder sbOutput, int level) private void WriteIndent(TextWriter textWriter, int level)
{ {
if (!_config.Indent) if (!_config.Indent)
{ {
return; return;
} }
sbOutput.Append('\n'); textWriter.Write('\n');
if (_config.UseTabForIndent) if (_config.UseTabForIndent)
{ {
for (int i = 0; i < level; i++) { sbOutput.Append('\t'); } for (int i = 0; i < level; i++) { textWriter.Write('\t'); }
} }
else else
{ {
int n = level * _config.IndentChars; int n = level * _config.IndentChars;
for (int i = 0; i < n; i++) { sbOutput.Append(' '); } for (int i = 0; i < n; i++) { textWriter.Write(' '); }
} }
} }
private void WriteString(StringBuilder sbOutput, string str) private void WriteString(TextWriter textWriter, string str)
{ {
sbOutput.Append('"'); textWriter.Write('"');
char c; char c;
int n = str.Length; int n = str.Length;
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
c = str[i]; c = str[i];
if (c == '"') { sbOutput.Append("\\\""); } if (c == '"') { textWriter.Write("\\\""); }
else if (c == '\\') { sbOutput.Append("\\\\"); } else if (c == '\\') { textWriter.Write("\\\\"); }
else if (c == '/') { sbOutput.Append("\\/"); } else if (c == '/') { textWriter.Write("\\/"); }
else if (c == '\b') { sbOutput.Append("\\b"); } else if (c == '\b') { textWriter.Write("\\b"); }
else if (c == '\f') { sbOutput.Append("\\f"); } else if (c == '\f') { textWriter.Write("\\f"); }
else if (c == '\n') { sbOutput.Append("\\n"); } else if (c == '\n') { textWriter.Write("\\n"); }
else if (c == '\r') { sbOutput.Append("\\r"); } else if (c == '\r') { textWriter.Write("\\r"); }
else if (c == '\t') { sbOutput.Append("\\t"); } else if (c == '\t') { textWriter.Write("\\t"); }
else if (c < 32 || c >= 127) { sbOutput.AppendFormat("\\u{0:X04}", (int)c); } else if (c < 32 || c >= 127) { textWriter.Write("\\u{0:X04}", (int)c); }
else { sbOutput.Append(c); } else { textWriter.Write(c); }
} }
sbOutput.Append('"'); textWriter.Write('"');
} }
private void WriteValue(StringBuilder sbOutput, object obj, List<object> parentLevels, bool useReflection) private void WriteValue(TextWriter textWriter, object obj, List<object> parentLevels)
{ {
if (obj == null || obj is DBNull) if (obj == null || obj is DBNull)
{ {
// NULL // NULL
sbOutput.Append("null"); textWriter.Write("null");
} }
else if ( else if (
(obj is float) || (obj is float) ||
@@ -157,50 +157,43 @@ namespace VAR.Json
false) false)
{ {
// Numbers // Numbers
sbOutput.Append(obj.ToString()); textWriter.Write(obj.ToString());
} }
else if (obj is string) else if (obj is string)
{ {
// Strings // Strings
WriteString(sbOutput, (string)obj); WriteString(textWriter, (string)obj);
} }
else if (obj is bool) else if (obj is bool)
{ {
// Booleans // Booleans
sbOutput.Append(((bool)obj) ? "true" : "false"); textWriter.Write(((bool)obj) ? "true" : "false");
} }
else if (obj is DateTime) else if (obj is DateTime)
{ {
// DateTime // DateTime
sbOutput.Append('"'); textWriter.Write('"');
sbOutput.Append(((DateTime)obj).ToString("yyyy-MM-ddTHH:mm:ssZ")); textWriter.Write(((DateTime)obj).ToString("yyyy-MM-ddTHH:mm:ss"));
sbOutput.Append('"'); textWriter.Write('"');
} }
else if (obj is IDictionary) else if (obj is IDictionary)
{ {
// Objects // Objects
WriteObject(sbOutput, obj, parentLevels); WriteObject(textWriter, obj, parentLevels);
} }
else if (obj is IEnumerable) else if (obj is IEnumerable)
{ {
// Array/List // Array/List
WriteList(sbOutput, obj, parentLevels); WriteList(textWriter, obj, parentLevels);
} }
else else
{
if (useReflection)
{ {
// Reflected object // Reflected object
WriteReflectedObject(sbOutput, obj, parentLevels); WriteReflectedObject(textWriter, obj, parentLevels);
}
else
{
WriteString(sbOutput, Convert.ToString(obj));
}
} }
} }
private void WriteList(StringBuilder sbOutput, object obj, List<object> parentLevels) private void WriteList(TextWriter textWriter, object obj, List<object> parentLevels)
{ {
IEnumerable list = (IEnumerable)obj; IEnumerable list = (IEnumerable)obj;
int n = 0; int n = 0;
@@ -219,40 +212,40 @@ namespace VAR.Json
// Empty // Empty
if (n == 0) if (n == 0)
{ {
sbOutput.Append("[ ]"); textWriter.Write("[ ]");
return; return;
} }
// Write array // Write array
bool first = true; bool first = true;
sbOutput.Append("[ "); textWriter.Write("[ ");
if (!isLeaf || n > _config.IndentThresold) if (!isLeaf || n > _config.IndentThresold)
{ {
WriteIndent(sbOutput, parentLevels.Count + 1); WriteIndent(textWriter, parentLevels.Count + 1);
} }
foreach (object childObj in list) foreach (object childObj in list)
{ {
if (!first) if (!first)
{ {
sbOutput.Append(", "); textWriter.Write(", ");
if (!isLeaf || n > _config.IndentThresold) if (!isLeaf || n > _config.IndentThresold)
{ {
WriteIndent(sbOutput, parentLevels.Count + 1); WriteIndent(textWriter, parentLevels.Count + 1);
} }
} }
first = false; first = false;
parentLevels.Add(obj); parentLevels.Add(obj);
WriteValue(sbOutput, childObj, parentLevels, true); WriteValue(textWriter, childObj, parentLevels);
parentLevels.Remove(obj); parentLevels.Remove(obj);
} }
if (!isLeaf || n > _config.IndentThresold) if (!isLeaf || n > _config.IndentThresold)
{ {
WriteIndent(sbOutput, parentLevels.Count); WriteIndent(textWriter, parentLevels.Count);
} }
sbOutput.Append(" ]"); textWriter.Write(" ]");
} }
private void WriteObject(StringBuilder sbOutput, 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;
@@ -260,7 +253,7 @@ namespace VAR.Json
// Empty // Empty
if (map.Count == 0) if (map.Count == 0)
{ {
sbOutput.Append("{ }"); textWriter.Write("{ }");
return; return;
} }
@@ -277,37 +270,37 @@ namespace VAR.Json
// Write object // Write object
bool first = true; bool first = true;
sbOutput.Append("{ "); textWriter.Write("{ ");
if (!isLeaf || n > _config.IndentThresold) if (!isLeaf || n > _config.IndentThresold)
{ {
WriteIndent(sbOutput, parentLevels.Count + 1); WriteIndent(textWriter, parentLevels.Count + 1);
} }
foreach (object key in map.Keys) foreach (object key in map.Keys)
{ {
object value = map[key]; object value = map[key];
if (!first) if (!first)
{ {
sbOutput.Append(", "); textWriter.Write(", ");
if (!isLeaf || n > _config.IndentThresold) if (!isLeaf || n > _config.IndentThresold)
{ {
WriteIndent(sbOutput, parentLevels.Count + 1); WriteIndent(textWriter, parentLevels.Count + 1);
} }
} }
first = false; first = false;
WriteString(sbOutput, Convert.ToString(key)); WriteString(textWriter, Convert.ToString(key));
sbOutput.Append(": "); textWriter.Write(": ");
parentLevels.Add(obj); parentLevels.Add(obj);
WriteValue(sbOutput, value, parentLevels, true); WriteValue(textWriter, value, parentLevels);
parentLevels.Remove(obj); parentLevels.Remove(obj);
} }
if (!isLeaf || n > _config.IndentThresold) if (!isLeaf || n > _config.IndentThresold)
{ {
WriteIndent(sbOutput, parentLevels.Count); WriteIndent(textWriter, parentLevels.Count);
} }
sbOutput.Append(" }"); textWriter.Write(" }");
} }
private void WriteReflectedObject(StringBuilder sbOutput, 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);
@@ -323,7 +316,7 @@ namespace VAR.Json
// Empty // Empty
if (n == 0) if (n == 0)
{ {
sbOutput.Append("{ }"); textWriter.Write("{ }");
return; return;
} }
@@ -341,10 +334,10 @@ namespace VAR.Json
// Write object // Write object
bool first = true; bool first = true;
sbOutput.Append("{ "); textWriter.Write("{ ");
if (!isLeaf || n > _config.IndentThresold) if (!isLeaf || n > _config.IndentThresold)
{ {
WriteIndent(sbOutput, parentLevels.Count + 1); WriteIndent(textWriter, parentLevels.Count + 1);
} }
foreach (PropertyInfo property in properties) foreach (PropertyInfo property in properties)
{ {
@@ -357,69 +350,106 @@ namespace VAR.Json
} }
if (!first) if (!first)
{ {
sbOutput.Append(", "); textWriter.Write(", ");
if (!isLeaf || n > _config.IndentThresold) if (!isLeaf || n > _config.IndentThresold)
{ {
WriteIndent(sbOutput, parentLevels.Count + 1); WriteIndent(textWriter, parentLevels.Count + 1);
} }
} }
first = false; first = false;
WriteString(sbOutput, property.Name); WriteString(textWriter, property.Name);
sbOutput.Append(": "); textWriter.Write(": ");
parentLevels.Add(obj); parentLevels.Add(obj);
if (value != obj && parentLevels.Contains(value) == false) if (value != obj && parentLevels.Contains(value) == false)
{ {
WriteValue(sbOutput, value, parentLevels, false); WriteValue(textWriter, value, parentLevels);
} }
else else
{ {
WriteValue(sbOutput, null, parentLevels, false); WriteValue(textWriter, null, parentLevels);
} }
parentLevels.Remove(obj); parentLevels.Remove(obj);
} }
if (!isLeaf || n > _config.IndentThresold) if (!isLeaf || n > _config.IndentThresold)
{ {
WriteIndent(sbOutput, parentLevels.Count); WriteIndent(textWriter, parentLevels.Count);
} }
sbOutput.Append(" }"); textWriter.Write(" }");
} }
#endregion Private methods #endregion Private methods
#region Public methods #region Public methods
public TextWriter Write(object obj, TextWriter textWriter)
{
if (textWriter == null)
{
textWriter = new StringWriter();
}
WriteValue(textWriter, obj, new List<object>());
return textWriter;
}
public string Write(object obj) public string Write(object obj)
{ {
StringBuilder sbOutput = new StringBuilder(); StringWriter textWriter = new StringWriter();
WriteValue(sbOutput, obj, new List<object>(), true); WriteValue(textWriter, obj, new List<object>());
return sbOutput.ToString(); return textWriter.ToString();
} }
private static Dictionary<JsonWriterConfiguration, JsonWriter> _dictInstances = new Dictionary<JsonWriterConfiguration, JsonWriter>(); private static Dictionary<JsonWriterConfiguration, JsonWriter> _dictInstances = new Dictionary<JsonWriterConfiguration, JsonWriter>();
public static string WriteObject(object obj, JsonWriterConfiguration config = null)
{
if(_dictInstances.ContainsKey(config) == false)
{
JsonWriter newJsonWriter = new JsonWriter(config);
_dictInstances.Add(config, newJsonWriter);
}
JsonWriter jsonWriter = _dictInstances[config];
return jsonWriter.Write(obj);
}
public static string WriteObject(object obj, public static string WriteObject(object obj,
JsonWriterConfiguration config = null,
bool indent = false, bool indent = false,
bool useTablForIndent = false, bool useTabForIndent = false,
int indentChars = 4, int indentChars = 4,
int indentThresold = 3) int indentThresold = 3)
{ {
return WriteObject(obj, new JsonWriterConfiguration( JsonWriter jsonWriter = null;
if (config != null)
{
if (_dictInstances.ContainsKey(config) == false)
{
jsonWriter = new JsonWriter(config);
_dictInstances.Add(config, jsonWriter);
}
else
{
jsonWriter = _dictInstances[config];
}
return jsonWriter.Write(obj);
}
foreach (KeyValuePair<JsonWriterConfiguration, JsonWriter> pair in _dictInstances)
{
if (
pair.Key.Indent == indent &&
pair.Key.UseTabForIndent == useTabForIndent &&
pair.Key.IndentChars == indentChars &&
pair.Key.IndentThresold == indentThresold &&
true)
{
jsonWriter = pair.Value;
break;
}
}
if (jsonWriter != null)
{
return jsonWriter.Write(obj);
}
JsonWriterConfiguration jsonWriterConfiguration = new JsonWriterConfiguration(
indent: indent, indent: indent,
useTablForIndent: useTablForIndent, useTabForIndent: useTabForIndent,
indentChars: indentChars, indentChars: indentChars,
indentThresold: indentThresold)); indentThresold: indentThresold);
jsonWriter = new JsonWriter(jsonWriterConfiguration);
_dictInstances.Add(jsonWriterConfiguration, jsonWriter);
return jsonWriter.Write(obj);
} }
#endregion Public methods #endregion Public methods

View File

@@ -11,4 +11,4 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
[assembly: Guid("28b3f937-145c-4fd4-a75b-a25ea4cc0428")] [assembly: Guid("28b3f937-145c-4fd4-a75b-a25ea4cc0428")]
[assembly: AssemblyVersion("1.1.0.*")] [assembly: AssemblyVersion("1.2.0.*")]

View File

@@ -21,6 +21,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<LangVersion>6</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release .Net 4.6.1|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release .Net 4.6.1|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@@ -30,6 +31,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<LangVersion>6</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug .Net 3.5|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug .Net 3.5|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@@ -40,6 +42,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<LangVersion>6</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release .Net 3.5|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release .Net 3.5|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@@ -49,6 +52,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<LangVersion>6</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />