From 350e0d7bb0cc6c4216ea81b57aac5dc6ff9c090e Mon Sep 17 00:00:00 2001 From: Chad Walters Date: Wed, 12 Jul 2017 18:50:58 -0700 Subject: [PATCH] [c#] Fully support uint64 in JSON when possible Use Newtonsoft's JSON.NET BigInteger support -- when available -- to handle the full range of uint64 values in the SimpleJson protocol (.NET 4.5 or greater, .NET Standard 1.6 or greater). --- CHANGELOG.md | 3 +++ appveyor.yml | 3 +++ cs/Compiler.vcxproj | 5 ++-- cs/build/internal/Common.Internal.props | 2 +- cs/build/internal/Common.Internal.targets | 1 + cs/build/internal/Portable.Internal.props | 2 +- cs/dnc/src/json/project.json | 9 ++++++-- cs/dnc/test/core.tests/project.json | 12 ++++++++-- cs/nuget/bond.csharp.test.csproj | 2 +- cs/nuget/bond.runtime.csharp.nuspec | 6 ++--- cs/nuget/packages.config | 2 +- cs/src/json/JSON.csproj | 19 +++++++++++---- .../json/expressions/json/SimpleJsonParser.cs | 23 ++++++++++++++++--- cs/test/core/Core.csproj | 20 +++++++++------- cs/test/core/SerializationTests.cs | 2 +- cs/test/core/Util.cs | 18 +++++++++++---- .../cs/core/simple_json/simple_json.csproj | 2 +- 17 files changed, 96 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25063c6b..df5c177a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,9 @@ different versioning scheme, following the Haskell community's ### C# ### * Reflection.IsBonded now recognizes custom IBonded implementations. +* Use Newtonsoft's JSON.NET BigInteger support -- when available -- to + handle the full range of uint64 values in the SimpleJson protocol (.NET + 4.5 or greater, .NET Standard 1.6 or greater). ## 6.0.0: 2017-06-29 ## * `gbc` & compiler library: 0.10.0.0 diff --git a/appveyor.yml b/appveyor.yml index 61e305d0..1aed03ec 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -298,6 +298,9 @@ nunit-console-x86 /framework:net-4.5 /labels "cs\test\core\bin\debug\net45\${env:BOND_OUTPUT}\Bond.UnitTest.dll" cs\test\internal\bin\debug\net45\Bond.InternalTest.dll if (-not $?) { throw "tests failed" } + nunit-console-x86 /framework:net-4.5 /labels "cs\test\core\bin\debug\net45-nonportable\${env:BOND_OUTPUT}\Bond.UnitTest.dll" + if (-not $?) { throw "tests failed" } + & examples\cs\grpc\pingpong\bin\Debug\pingpong.exe if (-not $?) { throw "tests failed" } diff --git a/cs/Compiler.vcxproj b/cs/Compiler.vcxproj index ddeaf3d6..fbeca512 100644 --- a/cs/Compiler.vcxproj +++ b/cs/Compiler.vcxproj @@ -33,8 +33,6 @@ call cmake $(ProjectDir)\..\compiler -Wno-dev if %errorlevel% neq 0 goto :cmEnd call cmake --build $(OutDir) --target gbc if %errorlevel% neq 0 goto :cmEnd -copy $(OutDir)\build\gbc\gbc.exe $(ProjectDir)\tools\ -if %errorlevel% neq 0 goto :cmEnd :cmEnd endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone :cmErrorLevel @@ -81,4 +79,7 @@ if %errorlevel% neq 0 goto :VCEnd + + + diff --git a/cs/build/internal/Common.Internal.props b/cs/build/internal/Common.Internal.props index 397a20ec..e07768e2 100644 --- a/cs/build/internal/Common.Internal.props +++ b/cs/build/internal/Common.Internal.props @@ -10,10 +10,10 @@ - v4.5 net45 + v4.5 bin\$(BuildType)\$(BuildFramework) obj\$(BuildType)\$(BuildFramework) 512 diff --git a/cs/build/internal/Common.Internal.targets b/cs/build/internal/Common.Internal.targets index de54c69a..8cd2cd46 100644 --- a/cs/build/internal/Common.Internal.targets +++ b/cs/build/internal/Common.Internal.targets @@ -18,5 +18,6 @@ + diff --git a/cs/build/internal/Portable.Internal.props b/cs/build/internal/Portable.Internal.props index 99134936..743f5e82 100644 --- a/cs/build/internal/Portable.Internal.props +++ b/cs/build/internal/Portable.Internal.props @@ -1,7 +1,7 @@ - + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Profile78 diff --git a/cs/dnc/src/json/project.json b/cs/dnc/src/json/project.json index d01a9b9d..d26c6657 100644 --- a/cs/dnc/src/json/project.json +++ b/cs/dnc/src/json/project.json @@ -37,10 +37,10 @@ "dependencies": { "attributes": "1.0.0-*", "core": "1.0.0-*", + "reflection": "1.0.0-*", "Newtonsoft.Json": { "version": "9.0.1" - }, - "reflection": "1.0.0-*" + } }, "frameworks": { "netstandard1.0": { @@ -49,6 +49,11 @@ } }, "netstandard1.6": { + "buildOptions": { + "define": [ + "SUPPORTS_BIGINTEGER" + ] + }, "dependencies": { "NETStandard.Library": "1.6.0" } diff --git a/cs/dnc/test/core.tests/project.json b/cs/dnc/test/core.tests/project.json index 7862fa30..b5421c3c 100644 --- a/cs/dnc/test/core.tests/project.json +++ b/cs/dnc/test/core.tests/project.json @@ -9,7 +9,7 @@ "gen/*.cs" ] }, - "debugType": "portable", + "debugType": "net45", "nowarn": [ "CS1591" ], @@ -39,7 +39,10 @@ "io": "1.0.0-*", "json": "1.0.0-*", "NUnit": "3.4.0", - "reflection": "1.0.0-*" + "reflection": "1.0.0-*", + "Newtonsoft.Json": { + "version": "10.0.1" + } }, "frameworks": { "netcoreapp1.0": { @@ -47,6 +50,11 @@ "netcoreapp1.0", "portable-net45+win8" ], + "buildOptions": { + "define": [ + "SUPPORTS_BIGINTEGER" + ] + }, "dependencies": { "Microsoft.NETCore.App": { "version": "1.0.0-*", diff --git a/cs/nuget/bond.csharp.test.csproj b/cs/nuget/bond.csharp.test.csproj index 7f4862f2..e60491b3 100644 --- a/cs/nuget/bond.csharp.test.csproj +++ b/cs/nuget/bond.csharp.test.csproj @@ -121,7 +121,7 @@ - ..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\NUnit.2.6.4\lib\nunit.framework.dll diff --git a/cs/nuget/bond.runtime.csharp.nuspec b/cs/nuget/bond.runtime.csharp.nuspec index dae4b392..1e7b5ba0 100644 --- a/cs/nuget/bond.runtime.csharp.nuspec +++ b/cs/nuget/bond.runtime.csharp.nuspec @@ -28,9 +28,9 @@ - - - + + + diff --git a/cs/nuget/packages.config b/cs/nuget/packages.config index 19654d46..7cc93a84 100644 --- a/cs/nuget/packages.config +++ b/cs/nuget/packages.config @@ -3,7 +3,7 @@ - + \ No newline at end of file diff --git a/cs/src/json/JSON.csproj b/cs/src/json/JSON.csproj index 309189e0..87a639b6 100644 --- a/cs/src/json/JSON.csproj +++ b/cs/src/json/JSON.csproj @@ -1,7 +1,12 @@  - + + net45-nonportable + $(DefineConstants);SUPPORTS_BIGINTEGER + + + {C001C79F-D289-4CF3-BB59-5F5A72F70D0E} Library @@ -9,6 +14,8 @@ Bond Bond.JSON true + bin\$(BuildType)\net45 + true @@ -20,15 +27,19 @@ + - ..\core\$(OutputPath)\Bond.dll + ..\core\$(DependentOutputPath)\Bond.dll - ..\attributes\$(OutputPath)\Bond.Attributes.dll + ..\attributes\$(DependentOutputPath)\Bond.Attributes.dll - + ..\..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + diff --git a/cs/src/json/expressions/json/SimpleJsonParser.cs b/cs/src/json/expressions/json/SimpleJsonParser.cs index 7a72f7c2..f002568a 100644 --- a/cs/src/json/expressions/json/SimpleJsonParser.cs +++ b/cs/src/json/expressions/json/SimpleJsonParser.cs @@ -9,6 +9,10 @@ namespace Bond.Expressions.Json using System.Globalization; using System.Linq.Expressions; +#if SUPPORTS_BIGINTEGER + using System.Numerics; +#endif + using Bond.Expressions.Pull; using Bond.Protocols; @@ -120,17 +124,30 @@ namespace Bond.Expressions.Json { convertedValue = Reader.Value; } - + var errorMessage = StringExpression.Format( "Invalid input, expected JSON token of type {0}, encountered {1}", Expression.Constant(scalarTokenType, typeof(object)), Expression.Convert(Reader.TokenType, typeof(object))); + Expression embeddedExpression = handler(convertedValue); + +#if SUPPORTS_BIGINTEGER + if (expectedType == BondDataType.BT_UINT64 && scalarTokenType == JsonToken.Integer) + { + embeddedExpression = + Expression.IfThenElse( + Expression.TypeIs(Reader.Value, typeof(long)), + embeddedExpression, + handler(Expression.Convert(Reader.Value, typeof(BigInteger)))); + } +#endif + var handleValue = Expression.IfThenElse( JsonTokenEquals(scalarTokenType), - handler(convertedValue), + embeddedExpression, ThrowUnexpectedInput(errorMessage)); // If a floating point value is expected also accept an integer @@ -138,7 +155,7 @@ namespace Bond.Expressions.Json { handleValue = Expression.IfThenElse( JsonTokenEquals(JsonToken.Integer), - handler(Expression.Convert(Reader.Value, typeof (long))), + handler(Expression.Convert(Reader.Value, typeof(long))), handleValue); } diff --git a/cs/test/core/Core.csproj b/cs/test/core/Core.csproj index 3be7e3c4..31c24185 100644 --- a/cs/test/core/Core.csproj +++ b/cs/test/core/Core.csproj @@ -1,9 +1,10 @@ - - net45 - + + net45-nonportable + $(DefineConstants);SUPPORTS_BIGINTEGER + {FF056B62-225A-47BC-B177-550FADDA4B41} @@ -14,14 +15,14 @@ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} False UnitTest - $(OutputPath) + $(OutputPath) + bin\$(BuildType)\net45 + true $(IntermediateOutputPath)\Properties\ $(OutputPath)\Properties\ - - $(IntermediateOutputPath)\Fields\ $(OutputPath)\Fields\ @@ -91,9 +92,12 @@ ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll - + ..\..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + @@ -109,7 +113,7 @@ ..\..\src\io\$(DependentOutputPath)\Bond.IO.dll - ..\..\src\json\$(DependentOutputPath)\Bond.JSON.dll + ..\..\src\json\$(OrigOutputPath)\Bond.JSON.dll diff --git a/cs/test/core/SerializationTests.cs b/cs/test/core/SerializationTests.cs index aaa981fd..7cae57a8 100644 --- a/cs/test/core/SerializationTests.cs +++ b/cs/test/core/SerializationTests.cs @@ -49,7 +49,7 @@ _uint8 = byte.MaxValue, _uint16 = ushort.MaxValue, _uint32 = uint.MaxValue, - // Note: not ulong.MaxValue because NewtonSoft JSON doesn't support it + // Note: not ulong.MaxValue because NewtonSoft JSON doesn't support it in portable profile _uint64 = long.MaxValue }); diff --git a/cs/test/core/Util.cs b/cs/test/core/Util.cs index 89bfd4b7..e3fcbd5e 100644 --- a/cs/test/core/Util.cs +++ b/cs/test/core/Util.cs @@ -976,6 +976,12 @@ namespace UnitTest return DeserializeTagged(reader); }); +#if SUPPORTS_BIGINTEGER + const bool hasBigInteger = true; +#else + const bool hasBigInteger = false; +#endif + // Simple doesn't support omitting fields if (typeof(From) != typeof(Nothing) && typeof(From) != typeof(GenericsWithNothing)) { @@ -999,9 +1005,10 @@ namespace UnitTest if (!AnyField(Reflection.IsBonded)) { streamTranscode(SerializeSP, TranscodeSPXml, DeserializeXml); - - // NewtonSoft JSON doesn't support uint64 - if (typeof (From) != typeof (MaxUInt64)) + + // NewtonSoft JSON doesn't fully support uint64 in portable profile, so we skip + // MaxUInt64 type where BigInteger isn't avaiable + if (hasBigInteger || (typeof(From) != typeof (MaxUInt64))) { streamTranscode(SerializeSP, TranscodeSPJson, DeserializeJson); } @@ -1024,8 +1031,9 @@ namespace UnitTest streamTranscode(SerializeCB, TranscodeCBXml, DeserializeXml); streamTranscode(SerializeFB, TranscodeFBXml, DeserializeXml); - // NewtonSoft JSON doesn't support uint64 - if (typeof (From) != typeof (MaxUInt64)) + // NewtonSoft JSON doesn't fully support uint64 in portable profile, so we skip + // MaxUInt64 type where BigInteger isn't avaiable + if (hasBigInteger || (typeof (From) != typeof (MaxUInt64))) { streamRoundtrip(SerializeJson, DeserializeJson); streamTranscode(SerializeCB, TranscodeCBJson, DeserializeJson); diff --git a/examples/cs/core/simple_json/simple_json.csproj b/examples/cs/core/simple_json/simple_json.csproj index 53f04624..90b79c69 100644 --- a/examples/cs/core/simple_json/simple_json.csproj +++ b/examples/cs/core/simple_json/simple_json.csproj @@ -58,7 +58,7 @@ $(BOND_BINARY_PATH)\net45\Bond.JSON.dll - ..\..\..\..\cs\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll + ..\..\..\..\cs\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll