├── .gitignore ├── LICENSE ├── Open.Serialization.Json.Newtonsoft ├── .editorconfig ├── CamelCaseJson.cs ├── Converters │ ├── JsonDecimalConverter.cs │ ├── JsonDecimalRoundingConverter.cs │ ├── JsonDoubleRoundingConverter.cs │ ├── JsonNullableDecimalConverter.cs │ ├── JsonNullableDecimalRoundingConverter.cs │ ├── JsonNullableDoubleConverter.cs │ ├── JsonNullableDoubleRoundingConverter.cs │ └── JsonValueConverterBase.cs ├── GlobalSuppressions.cs ├── JsonSerializerFactory.cs ├── JsonSerializerInternal.cs ├── Open.Serialization.Json.Newtonsoft.csproj ├── RelaxedJson.cs └── SerializationExtensions.cs ├── Open.Serialization.Json.System ├── .editorconfig ├── CamelCaseJson.cs ├── CaseSensitiveJson.cs ├── Converters │ ├── JsonDecimalConverter.cs │ ├── JsonDecimalRoundingConverter.cs │ ├── JsonDoubleRoundingConverter.cs │ ├── JsonNullableDecimalConverter.cs │ ├── JsonNullableDecimalRoundingConverter.cs │ ├── JsonNullableDoubleConverter.cs │ ├── JsonNullableDoubleRoundingConverter.cs │ ├── JsonValueConverterBase.cs │ └── Readme.md ├── JsonSerializerFactory.cs ├── JsonSerializerInternal.cs ├── Open.Serialization.Json.System.csproj ├── RelaxedJson.cs └── SerializationsExtensions.cs ├── Open.Serialization.Json.Utf8Json ├── .editorconfig ├── JsonSerializerFactory.cs ├── JsonSerializerInternal.cs ├── Open.Serialization.Json.Utf8Json.csproj └── SerializationExtensions.cs ├── Open.Serialization.Json ├── IJsonAsyncObjectSerializer.cs ├── IJsonAsyncSerializer.cs ├── IJsonDeserialize.cs ├── IJsonDeserializeAsync.cs ├── IJsonDeserializeObject.cs ├── IJsonDeserializeObjectAsync.cs ├── IJsonObjectSerializationFactory.cs ├── IJsonObjectSerializer.cs ├── IJsonObjectSerializerFactory.cs ├── IJsonSerializationFactory.cs ├── IJsonSerializationOptions.cs ├── IJsonSerialize.cs ├── IJsonSerializeAsync.cs ├── IJsonSerializeObject.cs ├── IJsonSerializeObjectAsync.cs ├── IJsonSerializer.cs ├── IJsonSerializerFactory.cs ├── JsonObjectSerializer.cs ├── JsonObjectSerializerBase.cs ├── JsonSerializationOptions.cs ├── JsonSerializer.cs ├── JsonSerializerBase.cs ├── Open.Serialization.Json.csproj └── Open.Serialization.Json.xml ├── Open.Serialization.Tests ├── DefaultImplementationTests.cs ├── Newtonsoft │ └── JsonExtensionTests.cs ├── Open.Serialization.Tests.csproj ├── ParityTests.cs ├── SampleModel.cs └── System │ └── JsonExtensionTests.cs ├── Open.Serialization.sln ├── Open.Serialization ├── .editorconfig ├── DefaultMethods.cs ├── Extensions │ └── SerializationExtensions.cs ├── IDeserialize.cs ├── IDeserializeAsync.cs ├── IDeserializeObject.cs ├── IDeserializeObjectAsync.cs ├── IObjectSerializer.cs ├── ISerialize.cs ├── ISerializeAsync.cs ├── ISerializeObject.cs ├── ISerializeObjectAsync.cs ├── ISerializer.cs ├── ObjectSerializer.cs ├── ObjectSerializerBase.cs ├── Open.Serialization.csproj ├── Open.Serialization.xml ├── Serializer.cs └── SerializerBase.cs ├── README.md └── logo.png /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | /.vscode 332 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 electricessence (Oren F.) All rights reserved 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CA1303: Do not pass literals as localized parameters 4 | dotnet_diagnostic.CA1303.severity = silent 5 | 6 | # CA1062: Validate arguments of public methods 7 | dotnet_diagnostic.CA1062.severity = suggestion 8 | 9 | # CA1305: Specify IFormatProvider 10 | dotnet_diagnostic.CA1305.severity = silent 11 | 12 | # CA1307: Specify StringComparison 13 | dotnet_diagnostic.CA1307.severity = suggestion 14 | 15 | # CS1591: Missing XML comment for publicly visible type or member 16 | dotnet_diagnostic.CS1591.severity = silent 17 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/CamelCaseJson.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Serialization; 3 | 4 | namespace Open.Serialization.Json.Newtonsoft; 5 | 6 | public static class CamelCaseJson 7 | { 8 | public static JsonSerializerSettings Default(bool indent = false) 9 | { 10 | var options = RelaxedJson.Options(indent); 11 | options.ContractResolver = new CamelCasePropertyNamesContractResolver() 12 | { 13 | NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false } 14 | }; 15 | return options; 16 | } 17 | 18 | public static JsonSerializerSettings Minimal(bool indent = false) 19 | { 20 | var options = Default(indent); 21 | options.NullValueHandling = NullValueHandling.Ignore; 22 | return options; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/Converters/JsonDecimalConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Diagnostics.Contracts; 4 | 5 | namespace Open.Serialization.Json.Newtonsoft.Converters; 6 | 7 | /// 8 | /// A converter for ensuring proper standardized processing of decimals. 9 | /// 10 | public class JsonDecimalConverter : JsonValueConverterBase 11 | { 12 | /// 13 | /// Constructs a . 14 | /// 15 | protected JsonDecimalConverter() 16 | { 17 | // Prevent unnecessary replication. 18 | } 19 | 20 | /// 21 | /// Shared instance of this converter. 22 | /// 23 | public static readonly JsonDecimalConverter Instance 24 | = new(); 25 | 26 | /// 27 | #if NETSTANDARD2_1 28 | [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("value")] 29 | #endif 30 | public static string? Normalize(decimal? value) 31 | => Normalize(value?.ToString()); 32 | 33 | /// 34 | /// Normalizes a decimal by removing any unnecessary trailing zeros or decimal. 35 | /// 36 | #if NETSTANDARD2_1 37 | [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("decimalString")] 38 | #endif 39 | public static string? Normalize(string? decimalString) 40 | => string.IsNullOrEmpty(decimalString) || decimalString!.IndexOf('.') == -1 41 | ? decimalString 42 | : decimalString.AsSpan().TrimEnd('0').TrimEnd('.').ToString(); 43 | 44 | /// 45 | public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer) 46 | { 47 | if (reader is null) throw new ArgumentNullException(nameof(reader)); 48 | Contract.EndContractBlock(); 49 | 50 | return ConvertToDecimal(reader.Value!); 51 | } 52 | 53 | /// 54 | public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer) 55 | { 56 | if (writer is null) throw new ArgumentNullException(nameof(writer)); 57 | Contract.EndContractBlock(); 58 | 59 | writer.WriteRawValue(Normalize(value)); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/Converters/JsonDecimalRoundingConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace Open.Serialization.Json.Newtonsoft.Converters; 5 | 6 | /// 7 | /// Converter for decimals that rounds to a maximum number of digits after the decimal. 8 | /// 9 | public class JsonDecimalRoundingConverter : JsonDecimalConverter 10 | { 11 | /// 12 | /// The maximum number of digits after the decimal. 13 | /// 14 | public int Maximum { get; } 15 | 16 | /// 17 | /// Constructs a . 18 | /// 19 | /// The maximum number of digits after the decimal. 20 | /// If the maximum is less than zero. 21 | public JsonDecimalRoundingConverter(int maximum) 22 | { 23 | if (maximum < 0) 24 | throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero."); 25 | Maximum = maximum; 26 | } 27 | 28 | /// 29 | public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer) 30 | => Math.Round(base.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer), Maximum); 31 | 32 | /// 33 | public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer) 34 | => base.WriteJson(writer, Math.Round(value, Maximum), serializer); 35 | } 36 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/Converters/JsonDoubleRoundingConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Diagnostics.Contracts; 4 | using System.Numerics; 5 | 6 | namespace Open.Serialization.Json.Newtonsoft.Converters; 7 | 8 | /// 9 | /// Converter for doubles that rounds to a maximum number of digits after the decimal. 10 | /// 11 | public class JsonDoubleRoundingConverter : JsonValueConverterBase 12 | { 13 | /// 14 | /// The maximum number of digits after the decimal. 15 | /// 16 | public int Maximum { get; } 17 | 18 | /// 19 | /// Constructs a . 20 | /// 21 | /// The maximum number of digits after the decimal. 22 | /// If the maximum is less than zero. 23 | public JsonDoubleRoundingConverter(int maximum) 24 | { 25 | if (maximum < 0) 26 | throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero."); 27 | Maximum = maximum; 28 | } 29 | 30 | public override double ReadJson(JsonReader reader, Type objectType, double existingValue, bool hasExistingValue, JsonSerializer serializer) 31 | { 32 | if (reader is null) throw new ArgumentNullException(nameof(reader)); 33 | Contract.EndContractBlock(); 34 | 35 | return reader.Value switch 36 | { 37 | double d => Math.Round(d, Maximum), 38 | decimal d => ConvertToDouble(Math.Round(d, Maximum)), 39 | sbyte i => i, 40 | byte i => i, 41 | short i => i, 42 | ushort i => i, 43 | int i => i, 44 | uint i => i, 45 | long i => i, 46 | ulong i => i, 47 | BigInteger i => (double)i, 48 | _ => Math.Round(ConvertToDouble(reader.Value!), Maximum), 49 | }; 50 | } 51 | 52 | public override void WriteJson(JsonWriter writer, double value, JsonSerializer serializer) 53 | { 54 | if (writer is null) throw new ArgumentNullException(nameof(writer)); 55 | Contract.EndContractBlock(); 56 | 57 | writer.WriteValue(Math.Round(value, Maximum)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/Converters/JsonNullableDecimalConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Diagnostics.Contracts; 4 | 5 | namespace Open.Serialization.Json.Newtonsoft.Converters; 6 | 7 | public class JsonNullableDecimalConverter : JsonValueConverterBase 8 | { 9 | protected JsonNullableDecimalConverter() 10 | { 11 | // Prevent unnecessary replication. 12 | } 13 | 14 | public static readonly JsonNullableDecimalConverter Instance 15 | = new(); 16 | 17 | /// 18 | public override decimal? ReadJson(JsonReader reader, Type objectType, decimal? existingValue, bool hasExistingValue, JsonSerializer serializer) 19 | { 20 | if (reader is null) throw new ArgumentNullException(nameof(reader)); 21 | Contract.EndContractBlock(); 22 | 23 | return reader.TokenType switch 24 | { 25 | JsonToken.Null => null, 26 | JsonToken.Undefined => null, 27 | _ => ConvertToDecimal(reader.Value!), 28 | }; 29 | } 30 | 31 | /// 32 | public override void WriteJson(JsonWriter writer, decimal? value, JsonSerializer serializer) 33 | { 34 | if (writer is null) throw new ArgumentNullException(nameof(writer)); 35 | Contract.EndContractBlock(); 36 | 37 | writer.WriteRawValue(JsonDecimalConverter.Normalize(value)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/Converters/JsonNullableDecimalRoundingConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace Open.Serialization.Json.Newtonsoft.Converters; 5 | 6 | /// 7 | /// Converter for nullable decimals that rounds to a maximum number of digits after the decimal. 8 | /// 9 | public class JsonNullableDecimalRoundingConverter : JsonNullableDecimalConverter 10 | { 11 | /// 12 | /// The maximum number of digits after the decimal. 13 | /// 14 | public int Maximum { get; } 15 | 16 | /// 17 | /// Constructs a . 18 | /// 19 | /// The maximum number of digits after the decimal. 20 | /// If the maximum is less than zero. 21 | public JsonNullableDecimalRoundingConverter(int maximum) 22 | { 23 | if (maximum < 0) 24 | throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero."); 25 | Maximum = maximum; 26 | } 27 | 28 | public override decimal? ReadJson(JsonReader reader, Type objectType, decimal? existingValue, bool hasExistingValue, JsonSerializer serializer) 29 | { 30 | var value = base.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer); 31 | return value.HasValue ? Math.Round(value.Value, Maximum) : value; 32 | } 33 | 34 | public override void WriteJson(JsonWriter writer, decimal? value, JsonSerializer serializer) 35 | { 36 | if (value.HasValue) 37 | value = Math.Round(value.Value, Maximum); 38 | 39 | base.WriteJson(writer, value, serializer); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/Converters/JsonNullableDoubleConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Diagnostics.Contracts; 4 | 5 | namespace Open.Serialization.Json.Newtonsoft.Converters; 6 | 7 | public class JsonNullableDoubleConverter : JsonValueConverterBase 8 | { 9 | protected JsonNullableDoubleConverter() 10 | { 11 | // Prevent unnecessary replication. 12 | } 13 | 14 | public static readonly JsonNullableDoubleConverter Instance 15 | = new(); 16 | 17 | public override double? ReadJson(JsonReader reader, Type objectType, double? existingValue, bool hasExistingValue, JsonSerializer serializer) 18 | { 19 | if (reader is null) throw new ArgumentNullException(nameof(reader)); 20 | Contract.EndContractBlock(); 21 | 22 | return reader.TokenType switch 23 | { 24 | JsonToken.Null => null, 25 | JsonToken.Undefined => null, 26 | _ => ConvertToDouble(reader.Value!) 27 | }; 28 | } 29 | 30 | public override void WriteJson(JsonWriter writer, double? value, JsonSerializer serializer) 31 | { 32 | if (writer is null) throw new ArgumentNullException(nameof(writer)); 33 | Contract.EndContractBlock(); 34 | 35 | if (value.HasValue) writer.WriteValue(value.Value); 36 | else writer.WriteRawValue(null); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/Converters/JsonNullableDoubleRoundingConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace Open.Serialization.Json.Newtonsoft.Converters; 5 | 6 | public class JsonNullableDoubleRoundingConverter : JsonNullableDoubleConverter 7 | { 8 | public int Maximum { get; } 9 | public JsonNullableDoubleRoundingConverter(int maximum) 10 | { 11 | if (maximum < 0) 12 | throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero."); 13 | Maximum = maximum; 14 | } 15 | 16 | public override double? ReadJson(JsonReader reader, Type objectType, double? existingValue, bool hasExistingValue, JsonSerializer serializer) 17 | { 18 | var value = base.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer); 19 | return value.HasValue ? Math.Round(value.Value, Maximum) : value; 20 | } 21 | 22 | public override void WriteJson(JsonWriter writer, double? value, JsonSerializer serializer) 23 | { 24 | if (value.HasValue) 25 | value = Math.Round(value.Value, Maximum); 26 | 27 | base.WriteJson(writer, value, serializer); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/Converters/JsonValueConverterBase.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Numerics; 4 | 5 | namespace Open.Serialization.Json.Newtonsoft.Converters; 6 | 7 | /// 8 | /// Base JsonConverter for NewtsonSoft.Json. 9 | /// 10 | public abstract class JsonValueConverterBase : JsonConverter 11 | { 12 | // Avoids stack overflow. 13 | private static readonly JsonSerializer Deserializer = JsonSerializer.Create(); 14 | 15 | /// 16 | public override T ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer) 17 | => Deserializer.Deserialize(reader)!; 18 | 19 | /// 20 | public override void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer) 21 | #pragma warning disable CA1062 // Validate arguments of public methods 22 | => writer.WriteRawValue(value?.ToString()); 23 | #pragma warning restore CA1062 // Validate arguments of public methods 24 | 25 | /// 26 | /// Special convert to decimal from object. 27 | /// 28 | protected static decimal ConvertToDecimal(object value) => value switch 29 | { 30 | decimal d => d, 31 | BigInteger i => (decimal)i, 32 | IConvertible _ => Convert.ToDecimal(value), 33 | _ => throw new ArgumentException("Unable to convert to decimal.", nameof(value)), 34 | }; 35 | 36 | /// 37 | /// Special convert to decimal from object. 38 | /// 39 | protected static double ConvertToDouble(object value) => value switch 40 | { 41 | double d => d, 42 | BigInteger i => (double)i, 43 | IConvertible _ => Convert.ToDouble(value), 44 | _ => throw new ArgumentException("Unable to convert to double.", nameof(value)), 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison for clarity", Justification = "", Scope = "member", Target = "~M:Open.Serialization.Json.Newtonsoft.Converters.JsonDecimalConverter.Normalize(System.String)~System.String")] 9 | [assembly: SuppressMessage("Usage", "CA2249:Consider using 'string.Contains' instead of 'string.IndexOf'", Justification = "", Scope = "member", Target = "~M:Open.Serialization.Json.Newtonsoft.Converters.JsonDecimalConverter.Normalize(System.String)~System.String")] 10 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/JsonSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Serialization; 3 | using System; 4 | using System.Threading; 5 | 6 | namespace Open.Serialization.Json.Newtonsoft; 7 | 8 | public class JsonSerializerFactory : IJsonSerializerFactory, IJsonObjectSerializerFactory 9 | { 10 | static readonly JsonSerializerSettings DefaultOptions = RelaxedJson.Options(); 11 | readonly JsonSerializerSettings _settings; 12 | public JsonSerializerFactory(JsonSerializerSettings? defaultOptions) 13 | { 14 | _settings = defaultOptions?.Clone() ?? DefaultOptions; 15 | } 16 | 17 | public JsonSerializerFactory() : this(null) 18 | { 19 | } 20 | 21 | JsonSerializerInternal? _defaultSerializer; 22 | internal JsonSerializerInternal DefaultSerializer 23 | => LazyInitializer.EnsureInitialized(ref _defaultSerializer, () => new JsonSerializerInternal(_settings))!; 24 | 25 | static JsonSerializerFactory? _default; 26 | public static JsonSerializerFactory Default 27 | => LazyInitializer.EnsureInitialized(ref _default)!; 28 | 29 | #if NETSTANDARD2_1 30 | [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("options")] 31 | #endif 32 | public JsonSerializerSettings? GetJsonSerializerSettings(IJsonSerializationOptions? options = null, bool caseSensitive = false) 33 | { 34 | if (caseSensitive) 35 | throw new NotSupportedException("Newtonsoft does not support case-sensitive deserialization."); 36 | 37 | if (options is null) return null; 38 | 39 | if (options.CamelCaseKeys == true && options.CamelCaseProperties != true) 40 | throw new NotSupportedException("Camel casing keys but not properties is not supported."); 41 | 42 | var o = _settings.Clone(); 43 | if (options.CamelCaseKeys == true) 44 | { 45 | o.ContractResolver = new DefaultContractResolver 46 | { 47 | NamingStrategy = new CamelCaseNamingStrategy 48 | { 49 | ProcessDictionaryKeys = true 50 | } 51 | }; 52 | } 53 | else if (options.CamelCaseProperties == true) 54 | { 55 | o.ContractResolver = new CamelCasePropertyNamesContractResolver(); 56 | } 57 | else if (options.CamelCaseProperties == false) 58 | { 59 | o.ContractResolver = new DefaultContractResolver(); 60 | } 61 | 62 | if (options.OmitNull.HasValue) 63 | o.NullValueHandling = options.OmitNull.Value ? NullValueHandling.Ignore : NullValueHandling.Include; 64 | 65 | if (options.Indent.HasValue) 66 | o.Formatting = options.Indent.Value ? Formatting.Indented : Formatting.None; 67 | 68 | return o; 69 | } 70 | 71 | internal JsonSerializerInternal GetSerializerInternal(IJsonSerializationOptions? options = null, bool caseSensitive = false) 72 | { 73 | var o = GetJsonSerializerSettings(options, caseSensitive); 74 | return o is null ? DefaultSerializer : new JsonSerializerInternal(o); 75 | } 76 | 77 | /// 78 | /// Returns an . 79 | /// 80 | public IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false) 81 | => GetSerializerInternal(options, caseSensitive); 82 | 83 | /// 84 | /// Returns an . 85 | /// 86 | public IJsonObjectSerializer GetObjectSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false) 87 | => GetSerializerInternal(options, caseSensitive); 88 | } 89 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/JsonSerializerInternal.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace Open.Serialization.Json.Newtonsoft; 5 | 6 | internal class JsonSerializerInternal : JsonObjectSerializerBase, IJsonSerializer 7 | { 8 | readonly JsonSerializerSettings _settings; 9 | internal JsonSerializerInternal(JsonSerializerSettings settings) 10 | { 11 | _settings = settings; 12 | } 13 | 14 | public override T Deserialize(string value) 15 | => JsonConvert.DeserializeObject(value, _settings)!; 16 | 17 | public override string Serialize(T item) 18 | => JsonConvert.SerializeObject(item, _settings); 19 | 20 | public override object? Deserialize(string value, Type type) 21 | => JsonConvert.DeserializeObject(value, type); 22 | 23 | public override string Serialize(object? item, Type type) 24 | => JsonConvert.SerializeObject(item, type, _settings); 25 | 26 | public new JsonSerializer Cast() 27 | => new(Deserialize, Serialize, DeserializeAsync, SerializeAsync); 28 | } 29 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/Open.Serialization.Json.Newtonsoft.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0; netstandard2.1 5 | true 6 | latest 7 | enable 8 | true 9 | electricessence 10 | 11 | Extentions and DI utilities for Newtonsoft.Json. 12 | 13 | Part of the "Open" set of libraries. 14 | 15 | serialization;json;newtonsoft 16 | © electricessence (Oren F.) All rights reserved. 17 | MIT 18 | https://github.com/Open-NET-Libraries/Open.Serialization 19 | https://github.com/Open-NET-Libraries/Open.Serialization 20 | git 21 | 3.0.0 22 | 23 | MIT 24 | true 25 | true 26 | snupkg 27 | logo.png 28 | true 29 | README.md 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | True 41 | 42 | 43 | 44 | True 45 | \ 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/RelaxedJson.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Serialization; 3 | using Open.Serialization.Json.Newtonsoft.Converters; 4 | 5 | namespace Open.Serialization.Json.Newtonsoft; 6 | 7 | /// 8 | /// Shortcut for accessing default relaxed JSON options. 9 | /// 10 | public static class RelaxedJson 11 | { 12 | internal static JsonSerializerSettings Options( 13 | bool indent = false) 14 | => new JsonSerializerSettings() 15 | { 16 | Formatting = indent ? Formatting.Indented : Formatting.None, 17 | FloatParseHandling = FloatParseHandling.Decimal, 18 | ContractResolver = new DefaultContractResolver() 19 | { 20 | NamingStrategy = new DefaultNamingStrategy() { ProcessDictionaryKeys = false } 21 | } 22 | } 23 | .AddConverter(JsonNullableDoubleConverter.Instance) 24 | .NormalizeDecimals(); 25 | 26 | /// 27 | /// Returns an .s 28 | /// 29 | public static IJsonDeserialize GetDeserializer() 30 | => DeserializerOptions.GetSerializer(); 31 | 32 | /// 33 | /// Returns an . 34 | /// 35 | public static IJsonDeserialize GetDeserializer() 36 | => DeserializerOptions.GetSerializer(); 37 | 38 | static readonly JsonSerializerSettings DeserializerOptions 39 | = Options().SetNullValueHandling(NullValueHandling.Ignore); 40 | 41 | /// 42 | public static TValue Deserialize(string value) 43 | => DeserializerOptions.Deserialize(value); 44 | } 45 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Newtonsoft/SerializationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Newtonsoft.Json; 3 | using Open.Serialization.Json.Newtonsoft.Converters; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics.Contracts; 7 | using System.Linq; 8 | 9 | namespace Open.Serialization.Json.Newtonsoft; 10 | 11 | /// 12 | /// Extensions for Newtonsof.Json serialization with Open.Serialization.Json. 13 | /// 14 | public static class SerializationExtensions 15 | { 16 | /// 17 | /// Adds a generic serializer and non-generic to the service collection. 18 | /// 19 | /// The service collection. 20 | /// The options overrides. 21 | /// The service collection. 22 | public static IServiceCollection AddJsonSerializer(this IServiceCollection services, IJsonSerializationOptions? options = null) 23 | { 24 | var factory = JsonSerializerFactory.Default; 25 | var serializer = factory.GetSerializerInternal(options); 26 | services.AddSingleton(serializer); 27 | services.AddSingleton(serializer); 28 | return services; 29 | } 30 | 31 | /// 32 | /// Adds a generic serializer to the service collection. 33 | /// 34 | /// 35 | public static IServiceCollection AddJsonSerializer(this IServiceCollection services, IJsonSerializationOptions? options = null) 36 | { 37 | var factory = JsonSerializerFactory.Default; 38 | services.AddSingleton>(factory.GetSerializerInternal(options).Cast()); 39 | return services; 40 | } 41 | 42 | public static Func GetDeserialize(this JsonSerializerSettings settings) 43 | => json => JsonConvert.DeserializeObject(json, settings)!; 44 | 45 | public static Func GetSerialize(this JsonSerializerSettings settings) 46 | => item => JsonConvert.SerializeObject(item, settings); 47 | 48 | public static Func GetSerialize(this JsonSerializerSettings settings) 49 | => item => JsonConvert.SerializeObject(item, settings); 50 | 51 | public static IJsonSerializer GetSerializer(this JsonSerializerSettings settings) 52 | => new JsonSerializerInternal(settings); 53 | 54 | public static IJsonSerializer GetSerializer(this JsonSerializerSettings settings) 55 | => new JsonSerializerInternal(settings).Cast(); 56 | 57 | public static IJsonSerializerFactory GetSerializerFactory(this JsonSerializerSettings settings) 58 | => new JsonSerializerFactory(settings); 59 | 60 | public static string Serialize(this JsonSerializerSettings settings, TValue value) 61 | => JsonConvert.SerializeObject(value, settings); 62 | 63 | public static string Serialize(this JsonSerializerSettings settings, object? value) 64 | => JsonConvert.SerializeObject(value, settings); 65 | 66 | public static TValue Deserialize(this JsonSerializerSettings settings, string value) 67 | => JsonConvert.DeserializeObject(value, settings)!; 68 | 69 | public static JsonSerializerSettings Clone(this JsonSerializerSettings settings) 70 | { 71 | if (settings is null) throw new ArgumentNullException(nameof(settings)); 72 | Contract.EndContractBlock(); 73 | 74 | var clone = new JsonSerializerSettings 75 | { 76 | StringEscapeHandling = settings.StringEscapeHandling, 77 | FloatParseHandling = settings.FloatParseHandling, 78 | FloatFormatHandling = settings.FloatFormatHandling, 79 | DateParseHandling = settings.DateParseHandling, 80 | DateTimeZoneHandling = settings.DateTimeZoneHandling, 81 | DateFormatHandling = settings.DateFormatHandling, 82 | Formatting = settings.Formatting, 83 | MaxDepth = settings.MaxDepth, 84 | DateFormatString = settings.DateFormatString, 85 | Context = settings.Context, 86 | Error = settings.Error, 87 | SerializationBinder = settings.SerializationBinder, 88 | TraceWriter = settings.TraceWriter, 89 | Culture = settings.Culture, 90 | ReferenceResolverProvider = settings.ReferenceResolverProvider, 91 | EqualityComparer = settings.EqualityComparer, 92 | ContractResolver = settings.ContractResolver, 93 | ConstructorHandling = settings.ConstructorHandling, 94 | TypeNameAssemblyFormatHandling = settings.TypeNameAssemblyFormatHandling, 95 | MetadataPropertyHandling = settings.MetadataPropertyHandling, 96 | TypeNameHandling = settings.TypeNameHandling, 97 | PreserveReferencesHandling = settings.PreserveReferencesHandling, 98 | DefaultValueHandling = settings.DefaultValueHandling, 99 | NullValueHandling = settings.NullValueHandling, 100 | ObjectCreationHandling = settings.ObjectCreationHandling, 101 | MissingMemberHandling = settings.MissingMemberHandling, 102 | ReferenceLoopHandling = settings.ReferenceLoopHandling, 103 | CheckAdditionalContent = settings.CheckAdditionalContent 104 | }; 105 | 106 | settings.Converters ??= new List(); 107 | foreach (var converter in settings.Converters) 108 | clone.Converters.Add(converter); 109 | 110 | return clone; 111 | } 112 | 113 | public static JsonSerializerSettings SetNullValueHandling(this JsonSerializerSettings settings, NullValueHandling value) 114 | { 115 | if (settings is null) throw new ArgumentNullException(nameof(settings)); 116 | Contract.EndContractBlock(); 117 | 118 | settings.NullValueHandling = value; 119 | return settings; 120 | } 121 | 122 | public static JsonSerializerSettings AddConverter(this JsonSerializerSettings settings, JsonConverter converter) 123 | { 124 | if (settings is null) throw new ArgumentNullException(nameof(settings)); 125 | Contract.EndContractBlock(); 126 | 127 | settings.Converters.Add(converter); 128 | return settings; 129 | } 130 | 131 | static JsonSerializerSettings RoundDoublesCore(this JsonSerializerSettings settings, int maxDecimals) 132 | => settings.Converters.FirstOrDefault(c => c is JsonConverter) switch 133 | { 134 | null => settings.AddConverter(new JsonDoubleRoundingConverter(maxDecimals)), 135 | JsonDoubleRoundingConverter e when e.Maximum == maxDecimals => settings, 136 | _ => throw new InvalidOperationException("A specific double converter already exists.") 137 | }; 138 | 139 | static JsonSerializerSettings RoundNullableDoublesCore(this JsonSerializerSettings settings, int maxDecimals) 140 | { 141 | JsonConverter? existing = settings.Converters.FirstOrDefault(c => c is JsonConverter); 142 | if (existing is JsonNullableDoubleConverter && existing.GetType() == typeof(JsonNullableDoubleConverter)) 143 | { 144 | settings.Converters.Remove(existing); 145 | existing = null; 146 | } 147 | 148 | return existing switch 149 | { 150 | null => settings.AddConverter(new JsonNullableDoubleRoundingConverter(maxDecimals)), 151 | JsonNullableDoubleRoundingConverter e when e.Maximum == maxDecimals => settings, 152 | _ => throw new InvalidOperationException("A specific double converter already exists.") 153 | }; 154 | } 155 | 156 | public static JsonSerializerSettings RoundDoubles(this JsonSerializerSettings settings, int maxDecimals) 157 | { 158 | if (settings is null) throw new ArgumentNullException(nameof(settings)); 159 | Contract.EndContractBlock(); 160 | 161 | return settings 162 | .RoundDoublesCore(maxDecimals) 163 | .RoundNullableDoublesCore(maxDecimals); 164 | } 165 | 166 | public static JsonSerializerSettings NormalizeDecimals(this JsonSerializerSettings settings) 167 | { 168 | if (settings is null) throw new ArgumentNullException(nameof(settings)); 169 | Contract.EndContractBlock(); 170 | 171 | JsonConverter? existing = settings.Converters.FirstOrDefault(c => c is JsonConverter); 172 | var existingNullable = settings.Converters.FirstOrDefault(c => c is JsonConverter); 173 | 174 | if (existing is JsonDecimalConverter && existingNullable is JsonNullableDecimalConverter) 175 | return settings; 176 | 177 | if (existing is null && existingNullable is null) 178 | { 179 | return settings 180 | .AddConverter(JsonDecimalConverter.Instance) 181 | .AddConverter(JsonNullableDecimalConverter.Instance); 182 | } 183 | 184 | if (existing is not null) 185 | throw new InvalidOperationException("A specific decimal converter already exists."); 186 | 187 | throw new InvalidOperationException("A specific Nullable converter already exists."); 188 | } 189 | 190 | static JsonSerializerSettings RoundDecimalsCore(this JsonSerializerSettings settings, int maxDecimals) 191 | { 192 | JsonConverter? existing = settings.Converters.FirstOrDefault(c => c is JsonConverter); 193 | if (existing is JsonDecimalConverter && existing.GetType() == typeof(JsonDecimalConverter)) 194 | { 195 | settings.Converters.Remove(existing); 196 | existing = null; 197 | } 198 | 199 | return existing switch 200 | { 201 | null => settings.AddConverter(new JsonDecimalRoundingConverter(maxDecimals)), 202 | JsonDecimalRoundingConverter e when e.Maximum == maxDecimals => settings, 203 | _ => throw new InvalidOperationException("A specific decimal converter already exists.") 204 | }; 205 | } 206 | 207 | static JsonSerializerSettings RoundNullableDecimalsCore(this JsonSerializerSettings settings, int maxDecimals) 208 | { 209 | JsonConverter? existing = settings.Converters.FirstOrDefault(c => c is JsonConverter); 210 | if (existing is JsonNullableDecimalConverter && existing.GetType() == typeof(JsonNullableDecimalConverter)) 211 | { 212 | settings.Converters.Remove(existing); 213 | existing = null; 214 | } 215 | 216 | return existing switch 217 | { 218 | null => settings.AddConverter(new JsonNullableDecimalRoundingConverter(maxDecimals)), 219 | JsonNullableDecimalRoundingConverter e when e.Maximum == maxDecimals => settings, 220 | _ => throw new InvalidOperationException("A specific Nullable converter already exists.") 221 | }; 222 | } 223 | 224 | public static JsonSerializerSettings RoundDecimals(this JsonSerializerSettings settings, int maxDecimals) 225 | { 226 | if (settings is null) throw new ArgumentNullException(nameof(settings)); 227 | Contract.EndContractBlock(); 228 | 229 | return settings 230 | .RoundDecimalsCore(maxDecimals) 231 | .RoundNullableDecimalsCore(maxDecimals); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CA1303: Do not pass literals as localized parameters 4 | dotnet_diagnostic.CA1303.severity = silent 5 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/CamelCaseJson.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Open.Serialization.Json.System; 4 | 5 | /// 6 | /// Provides default 'camel case' serialization configurations. 7 | /// 8 | public static class CamelCaseJson 9 | { 10 | /// 11 | /// Default relaxed serializations. 12 | /// 13 | public static JsonSerializerOptions Default(bool indent = false) 14 | { 15 | var options = RelaxedJson.Options(indent); 16 | options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; 17 | return options; 18 | } 19 | 20 | /// 21 | /// Default relaxed serializations but also ignores null values. 22 | /// 23 | public static JsonSerializerOptions Minimal(bool indent = false) 24 | => Default(indent).SetIgnoreNullValues(); 25 | } 26 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/CaseSensitiveJson.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Open.Serialization.Json.System; 4 | 5 | /// 6 | /// Provides default 'case sensitive' serialization configurations. 7 | /// 8 | public static class CaseSensitiveJson 9 | { 10 | /// 11 | /// Default relaxed serializations. 12 | /// 13 | public static JsonSerializerOptions Default(bool indent = false) 14 | => RelaxedJson.Options(indent, true); 15 | 16 | /// 17 | /// Default relaxed serializations but also ignores null values. 18 | /// 19 | public static JsonSerializerOptions Minimal(bool indent = false) 20 | => Default(indent).SetIgnoreNullValues(); 21 | } 22 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Converters/JsonDecimalConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Contracts; 3 | using System.Text.Json; 4 | 5 | namespace Open.Serialization.Json.System.Converters; 6 | 7 | /// 8 | /// Converter for decimals. 9 | /// 10 | public class JsonDecimalConverter : JsonValueConverterBase 11 | { 12 | /// 13 | /// Constructs a . 14 | /// 15 | protected JsonDecimalConverter() 16 | { 17 | // Prevent unnecessary replication. 18 | } 19 | 20 | /// 21 | /// The shared instance of a . 22 | /// 23 | public static readonly JsonDecimalConverter Instance 24 | = new(); 25 | 26 | /// 27 | public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 28 | => reader.GetDecimal(); 29 | 30 | /// 31 | public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) 32 | { 33 | if (writer is null) throw new ArgumentNullException(nameof(writer)); 34 | Contract.EndContractBlock(); 35 | 36 | var v = value / 1M; 37 | var truncated = decimal.Truncate(v); 38 | writer.WriteNumberValue(truncated == v ? truncated : v / 1.000000000000000000000000000000000m); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Converters/JsonDecimalRoundingConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | 4 | namespace Open.Serialization.Json.System.Converters; 5 | 6 | /// 7 | /// Converter for decimals that rounds to a maximum number of digits after the decimal. 8 | /// 9 | public class JsonDecimalRoundingConverter : JsonDecimalConverter 10 | { 11 | /// 12 | /// The maximum number of digits after the decimal. 13 | /// 14 | public int Maximum { get; } 15 | 16 | /// 17 | /// Constructs a . 18 | /// 19 | /// The maximum number of digits after the decimal. 20 | /// If the maximum is less than zero. 21 | public JsonDecimalRoundingConverter(int maximum) 22 | { 23 | if (maximum < 0) 24 | throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero."); 25 | Maximum = maximum; 26 | } 27 | 28 | /// 29 | public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 30 | => Math.Round(reader.GetDecimal(), Maximum); 31 | 32 | /// 33 | public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) 34 | => base.Write(writer, Math.Round(value, Maximum), options); 35 | } 36 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Converters/JsonDoubleRoundingConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Contracts; 3 | using System.Text.Json; 4 | 5 | namespace Open.Serialization.Json.System.Converters; 6 | 7 | /// 8 | /// Converter for doubles that rounds to a maximum number of digits after the decimal. 9 | /// 10 | public class JsonDoubleRoundingConverter : JsonValueConverterBase 11 | { 12 | /// 13 | /// The maximum number of digits after the decimal. 14 | /// 15 | public int Maximum { get; } 16 | 17 | /// 18 | /// Constructs a . 19 | /// 20 | /// The maximum number of digits after the decimal. 21 | /// If the maximum is less than zero. 22 | public JsonDoubleRoundingConverter(int maximum) 23 | { 24 | if (maximum < 0) 25 | throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero."); 26 | Maximum = maximum; 27 | } 28 | 29 | /// 30 | public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 31 | => Math.Round(reader.GetDouble(), Maximum); 32 | 33 | /// 34 | public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) 35 | { 36 | if (writer is null) throw new ArgumentNullException(nameof(writer)); 37 | Contract.EndContractBlock(); 38 | 39 | writer.WriteNumberValue(Math.Round(value, Maximum)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Converters/JsonNullableDecimalConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Contracts; 3 | using System.Text.Json; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace Open.Serialization.Json.System.Converters; 7 | 8 | /// 9 | /// Converter for nullable decimals. 10 | /// 11 | public class JsonNullableDecimalConverter : JsonConverter 12 | { 13 | /// 14 | /// Constructs a . 15 | /// 16 | protected JsonNullableDecimalConverter() 17 | { 18 | // Prevent unnecessary replication. 19 | } 20 | 21 | /// 22 | /// The shared instance of this converter. 23 | /// 24 | public static readonly JsonNullableDecimalConverter Instance 25 | = new(); 26 | 27 | /// 28 | public override bool CanConvert(Type objectType) 29 | => objectType == typeof(decimal?) || objectType == typeof(decimal); 30 | 31 | /// 32 | public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 33 | => reader.TokenType switch 34 | { 35 | JsonTokenType.Null => null, 36 | JsonTokenType.Number => reader.GetDecimal(), 37 | _ => throw new JsonException("Unexpected token type."), 38 | }; 39 | 40 | /// 41 | public override void Write(Utf8JsonWriter writer, decimal? value, JsonSerializerOptions options) 42 | { 43 | if (writer is null) throw new ArgumentNullException(nameof(writer)); 44 | Contract.EndContractBlock(); 45 | 46 | if (value.HasValue) JsonDecimalConverter.Instance.Write(writer, value.Value, options); 47 | else writer.WriteNullValue(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Converters/JsonNullableDecimalRoundingConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | 4 | namespace Open.Serialization.Json.System.Converters; 5 | 6 | /// 7 | /// Converter for nullable doubles that rounds to a maximum number of digits after the decimal. 8 | /// 9 | public class JsonNullableDecimalRoundingConverter : JsonNullableDecimalConverter 10 | { 11 | /// 12 | /// The maximum number of digits after the decimal. 13 | /// 14 | public int Maximum { get; } 15 | 16 | /// 17 | /// Constructs a . 18 | /// 19 | /// The maximum number of digits after the decimal. 20 | /// If the maximum is less than zero. 21 | public JsonNullableDecimalRoundingConverter(int maximum) 22 | { 23 | if (maximum < 0) 24 | throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero."); 25 | Maximum = maximum; 26 | } 27 | 28 | /// 29 | public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 30 | => reader.TokenType == JsonTokenType.Number 31 | ? Math.Round(reader.GetDecimal(), Maximum) 32 | : base.Read(ref reader, typeToConvert, options); 33 | 34 | /// 35 | public override void Write(Utf8JsonWriter writer, decimal? value, JsonSerializerOptions options) 36 | { 37 | if (value.HasValue) 38 | value = Math.Round(value.Value, Maximum); 39 | 40 | base.Write(writer, value, options); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Converters/JsonNullableDoubleConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Contracts; 3 | using System.Text.Json; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace Open.Serialization.Json.System.Converters; 7 | 8 | /// 9 | /// Converter for nullable doubles. 10 | /// 11 | public class JsonNullableDoubleConverter : JsonConverter 12 | { 13 | /// 14 | /// Constructs a . 15 | /// 16 | protected JsonNullableDoubleConverter() 17 | { 18 | // Prevent unnecessary replication. 19 | } 20 | 21 | /// 22 | /// The shared instance for this converter. 23 | /// 24 | public static readonly JsonNullableDoubleConverter Instance 25 | = new(); 26 | 27 | /// 28 | public override bool CanConvert(Type objectType) 29 | => objectType == typeof(double?); 30 | 31 | /// 32 | public override double? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 33 | => reader.TokenType switch 34 | { 35 | JsonTokenType.Null => null, 36 | JsonTokenType.Number => reader.GetDouble(), 37 | _ => throw new JsonException("Unexpected token type."), 38 | }; 39 | 40 | /// 41 | public override void Write(Utf8JsonWriter writer, double? value, JsonSerializerOptions options) 42 | { 43 | if (writer is null) throw new ArgumentNullException(nameof(writer)); 44 | Contract.EndContractBlock(); 45 | 46 | if (value.HasValue) writer.WriteNumberValue(value.Value); 47 | else writer.WriteNullValue(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Converters/JsonNullableDoubleRoundingConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | 4 | namespace Open.Serialization.Json.System.Converters; 5 | 6 | /// 7 | /// Converter for nullable doubles that rounds to a maximum number of digits after the decimal. 8 | /// 9 | public class JsonNullableDoubleRoundingConverter : JsonNullableDoubleConverter 10 | { 11 | /// 12 | /// The maximum number of digits after the decimal. 13 | /// 14 | public int Maximum { get; } 15 | 16 | /// 17 | /// Constructs a . 18 | /// 19 | /// The maximum number of digits after the decimal. 20 | /// If the maximum is less than zero. 21 | public JsonNullableDoubleRoundingConverter(int maximum) 22 | { 23 | if (maximum < 0) 24 | throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero."); 25 | Maximum = maximum; 26 | } 27 | 28 | /// 29 | public override double? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 30 | => reader.TokenType == JsonTokenType.Number 31 | ? Math.Round(reader.GetDouble(), Maximum) 32 | : base.Read(ref reader, typeToConvert, options); 33 | 34 | /// 35 | public override void Write(Utf8JsonWriter writer, double? value, JsonSerializerOptions options) 36 | { 37 | if (value.HasValue) 38 | value = Math.Round(value.Value, Maximum); 39 | 40 | base.Write(writer, value, options); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Converters/JsonValueConverterBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Open.Serialization.Json.System.Converters; 5 | 6 | /// Base class for other json value converters. 7 | /// 8 | public abstract class JsonValueConverterBase : JsonConverter 9 | { 10 | /// 11 | public override bool CanConvert(Type objectType) 12 | => objectType == typeof(T); 13 | } 14 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Converters/Readme.md: -------------------------------------------------------------------------------- 1 | # Nullable Types 2 | 3 | Currently nullable value types are not properly supported so these classes are provided until then. -------------------------------------------------------------------------------- /Open.Serialization.Json.System/JsonSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Threading; 3 | 4 | namespace Open.Serialization.Json.System; 5 | 6 | /// 7 | /// The default for System.Text.Json. 8 | /// 9 | public class JsonSerializerFactory : IJsonSerializerFactory 10 | { 11 | static readonly JsonSerializerOptions DefaultOptions = RelaxedJson.Options(); 12 | readonly JsonSerializerOptions _options; 13 | 14 | /// 15 | /// Constructs a 16 | /// 17 | public JsonSerializerFactory(JsonSerializerOptions? defaultOptions) 18 | { 19 | _options = defaultOptions?.Clone() ?? DefaultOptions; 20 | } 21 | 22 | /// 23 | public JsonSerializerFactory() : this(null) 24 | { 25 | } 26 | 27 | JsonSerializerInternal? _caseSensitive; 28 | JsonSerializerInternal? _ignoreCase; 29 | 30 | static JsonSerializerFactory? _default; 31 | 32 | /// 33 | /// The default instance for System.Text.Json. 34 | /// 35 | public static JsonSerializerFactory Default 36 | => LazyInitializer.EnsureInitialized(ref _default)!; 37 | 38 | JsonSerializerOptions? GetJsonSerializerSettings(IJsonSerializationOptions? options = null) 39 | { 40 | if (options is null) return null; 41 | 42 | var o = _options.Clone(); 43 | if(options.OmitNull.HasValue) o.SetIgnoreNullValues(options.OmitNull.Value); 44 | o.WriteIndented = options.Indent ?? o.WriteIndented; 45 | o.DictionaryKeyPolicy = options.CamelCaseKeys == true ? JsonNamingPolicy.CamelCase : o.DictionaryKeyPolicy; 46 | o.PropertyNamingPolicy = options.CamelCaseProperties == true ? JsonNamingPolicy.CamelCase : o.PropertyNamingPolicy; 47 | return o; 48 | } 49 | 50 | internal JsonSerializerInternal GetSerializerInternal(IJsonSerializationOptions? options = null, bool caseSensitive = false) 51 | { 52 | var o = GetJsonSerializerSettings(options); 53 | if (o is null) 54 | { 55 | #pragma warning disable CS8603 // Possible null reference return. 56 | return caseSensitive 57 | ? LazyInitializer.EnsureInitialized(ref _caseSensitive, 58 | () => new JsonSerializerInternal(_options.Clone().SetPropertyNameCaseInsensitive(false))) 59 | : LazyInitializer.EnsureInitialized(ref _ignoreCase, 60 | () => new JsonSerializerInternal(_options.Clone().SetPropertyNameCaseInsensitive(true))); 61 | #pragma warning restore CS8603 // Possible null reference return. 62 | } 63 | 64 | return new JsonSerializerInternal(o); 65 | } 66 | 67 | /// 68 | /// Returns an . 69 | /// 70 | public IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false) 71 | => GetSerializerInternal(options, caseSensitive); 72 | } 73 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/JsonSerializerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text.Json; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Open.Serialization.Json.System; 8 | 9 | internal class JsonSerializerInternal : JsonSerializerBase, IJsonSerializer 10 | { 11 | readonly JsonSerializerOptions _options; 12 | internal JsonSerializerInternal(JsonSerializerOptions options) 13 | { 14 | _options = options ?? throw new ArgumentNullException(nameof(options)); 15 | } 16 | 17 | public override T Deserialize(string value) 18 | => JsonSerializer.Deserialize(value!, _options)!; 19 | 20 | ValueTask ISerializeAsync.SerializeAsync(Stream stream, T item, CancellationToken cancellationToken) 21 | => new(JsonSerializer.SerializeAsync(stream, item, _options, cancellationToken)); 22 | 23 | public new Task SerializeAsync(Stream stream, T item, CancellationToken cancellationToken = default) 24 | => JsonSerializer.SerializeAsync(stream, item, _options, cancellationToken); 25 | 26 | public override string Serialize(T item) 27 | => JsonSerializer.Serialize(item, _options); 28 | 29 | public override ValueTask DeserializeAsync(Stream stream, CancellationToken cancellationToken = default) 30 | => JsonSerializer.DeserializeAsync(stream, _options)!; 31 | } 32 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/Open.Serialization.Json.System.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0; netstandard2.1 5 | true 6 | latest 7 | enable 8 | electricessence 9 | 10 | Extentions and DI utilities for System.Text.Json. 11 | 12 | Part of the "Open" set of libraries. 13 | 14 | serialization;json;stj;System.Text.Json 15 | © electricessence (Oren F.) All rights reserved. 16 | MIT 17 | https://github.com/Open-NET-Libraries/Open.Serialization 18 | https://github.com/Open-NET-Libraries/Open.Serialization 19 | git 20 | 3.0.0 21 | true 22 | 23 | MIT 24 | true 25 | true 26 | snupkg 27 | logo.png 28 | true 29 | README.md 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | True 41 | 42 | 43 | 44 | True 45 | \ 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/RelaxedJson.cs: -------------------------------------------------------------------------------- 1 | using Open.Serialization.Json.System.Converters; 2 | using System; 3 | using System.Text.Json; 4 | 5 | namespace Open.Serialization.Json.System; 6 | 7 | /// 8 | /// Shortcut for accessing default relaxed JSON options. 9 | /// 10 | public static class RelaxedJson 11 | { 12 | internal static JsonSerializerOptions Options( 13 | bool indent = false, 14 | bool caseSensitive = false) 15 | => new JsonSerializerOptions() 16 | { 17 | PropertyNameCaseInsensitive = !caseSensitive, 18 | AllowTrailingCommas = true, 19 | WriteIndented = indent 20 | } 21 | .AddConverter(JsonNullableDoubleConverter.Instance) 22 | .NormalizeDecimals(); 23 | 24 | static readonly JsonSerializerOptions DeserializerOptions 25 | = Options().SetIgnoreNullValues(); 26 | 27 | /// 28 | /// Returns an . 29 | /// 30 | public static IJsonDeserialize GetDeserializer() 31 | => DeserializerOptions.GetSerializer(); 32 | 33 | /// 34 | /// Returns an . 35 | /// 36 | public static IJsonDeserialize GetDeserializer() 37 | => DeserializerOptions.GetSerializer(); 38 | 39 | /// 40 | /// Deserializes to . 41 | /// 42 | public static TValue Deserialize(string value) 43 | => DeserializerOptions.Deserialize(value); 44 | 45 | /// 46 | /// Deserializes to . 47 | /// 48 | public static TValue Deserialize(ReadOnlySpan value) 49 | => DeserializerOptions.Deserialize(value); 50 | 51 | /// 52 | /// Deserializes to . 53 | /// 54 | public static TValue Deserialize(ReadOnlySpan value) 55 | => DeserializerOptions.Deserialize(value); 56 | } 57 | -------------------------------------------------------------------------------- /Open.Serialization.Json.System/SerializationsExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Open.Serialization.Json.System.Converters; 3 | using System; 4 | using System.Diagnostics.Contracts; 5 | using System.Linq; 6 | using System.Text.Encodings.Web; 7 | using System.Text.Json; 8 | using System.Text.Json.Serialization; 9 | 10 | namespace Open.Serialization.Json.System; 11 | 12 | /// 13 | /// Extensions for System.Text.Json serialization with Open.Serialization.Json. 14 | /// 15 | public static class SerializationsExtensions 16 | { 17 | /// 18 | /// Adds a generic serializer and non-generic to the service collection. 19 | /// 20 | /// The service collection. 21 | /// The options overrides. 22 | /// Indicates whether the serializer should be case sensitive. 23 | /// The service collection. 24 | public static IServiceCollection AddJsonSerializer(this IServiceCollection services, IJsonSerializationOptions? options = null, bool caseSensitive = false) 25 | { 26 | var factory = JsonSerializerFactory.Default; 27 | services.AddSingleton(factory.GetSerializer(options, caseSensitive)); 28 | return services; 29 | } 30 | 31 | /// 32 | /// Adds a generic serializer ) to the service collection. 33 | /// 34 | /// 35 | public static IServiceCollection AddJsonSerializer(this IServiceCollection services, IJsonSerializationOptions? options = null, bool caseSensitive = false) 36 | { 37 | var factory = JsonSerializerFactory.Default; 38 | services.AddSingleton>(factory.GetSerializerInternal(options, caseSensitive).Cast()); 39 | return services; 40 | } 41 | 42 | /// 43 | /// Returns a delegate that can deserialize with the given options. 44 | /// 45 | public static Func GetDeserialize(this JsonSerializerOptions options) 46 | => json => JsonSerializer.Deserialize(json, options)!; 47 | 48 | /// 49 | /// Returns a delegate that can serialze a to with the given options. 50 | /// 51 | public static Func GetSerialize(this JsonSerializerOptions options) 52 | => item => JsonSerializer.Serialize(item, options)!; 53 | 54 | /// 55 | /// Returns a delegate that can serialze a to an with the given options. 56 | /// 57 | public static Func GetSerialize(this JsonSerializerOptions options) 58 | => item => JsonSerializer.Serialize(item, options); 59 | 60 | /// 61 | /// Returns a with the given options. 62 | /// 63 | public static IJsonSerializer GetSerializer(this JsonSerializerOptions options) 64 | => new JsonSerializerInternal(options); 65 | 66 | /// 67 | /// Returns a with the given options. 68 | /// 69 | public static IJsonSerializer GetSerializer(this JsonSerializerOptions options) 70 | => new JsonSerializerInternal(options).Cast(); 71 | 72 | /// 73 | /// Returns a with the given options. 74 | /// 75 | public static IJsonSerializerFactory GetSerializerFactory(this JsonSerializerOptions options) 76 | => new JsonSerializerFactory(options); 77 | 78 | /// 79 | /// Serializes to a using the given options. 80 | /// 81 | public static string Serialize(this JsonSerializerOptions options, TValue value) 82 | => JsonSerializer.Serialize(value, options); 83 | 84 | /// 85 | /// Serializes to a using the given options. 86 | /// 87 | public static string Serialize(this JsonSerializerOptions options, object? value) 88 | => JsonSerializer.Serialize(value, options); 89 | 90 | /// 91 | /// Deserializes to a using the given options. 92 | /// 93 | public static TValue Deserialize(this JsonSerializerOptions options, string value) 94 | => JsonSerializer.Deserialize(value, options)!; 95 | 96 | /// 97 | /// Deserializes to a using the given options. 98 | /// 99 | public static TValue Deserialize(this JsonSerializerOptions options, ReadOnlySpan value) 100 | => JsonSerializer.Deserialize(value, options)!; 101 | 102 | /// 103 | /// Deserializes to a using the given options. 104 | /// 105 | public static TValue Deserialize(this JsonSerializerOptions options, ReadOnlySpan value) 106 | => JsonSerializer.Deserialize(value, options)!; 107 | 108 | /// 109 | /// Returns a shallow copy of the options. 110 | /// 111 | /// If is null. 112 | public static JsonSerializerOptions Clone(this JsonSerializerOptions options) 113 | { 114 | if (options is null) throw new ArgumentNullException(nameof(options)); 115 | Contract.EndContractBlock(); 116 | 117 | var clone = new JsonSerializerOptions 118 | { 119 | AllowTrailingCommas = options.AllowTrailingCommas, 120 | DefaultBufferSize = options.DefaultBufferSize, 121 | DictionaryKeyPolicy = options.DictionaryKeyPolicy, 122 | Encoder = options.Encoder, 123 | IgnoreReadOnlyFields = options.IgnoreReadOnlyFields, 124 | IgnoreReadOnlyProperties = options.IgnoreReadOnlyProperties, 125 | IncludeFields = options.IncludeFields, 126 | MaxDepth = options.MaxDepth, 127 | NumberHandling = options.NumberHandling, 128 | PropertyNameCaseInsensitive = options.PropertyNameCaseInsensitive, 129 | PropertyNamingPolicy = options.PropertyNamingPolicy, 130 | ReadCommentHandling = options.ReadCommentHandling, 131 | WriteIndented = options.WriteIndented, 132 | DefaultIgnoreCondition = options.DefaultIgnoreCondition, 133 | ReferenceHandler = options.ReferenceHandler 134 | }; 135 | 136 | foreach (var converter in options.Converters) 137 | clone.Converters.Add(converter); 138 | 139 | return clone; 140 | } 141 | 142 | /// 143 | /// Sets the value. 144 | /// 145 | /// If is null. 146 | public static JsonSerializerOptions SetPropertyNameCaseInsensitive(this JsonSerializerOptions options, bool value) 147 | { 148 | if (options is null) throw new ArgumentNullException(nameof(options)); 149 | Contract.EndContractBlock(); 150 | 151 | options.PropertyNameCaseInsensitive = value; 152 | return options; 153 | } 154 | 155 | /// 156 | /// Sets the value. 157 | /// A of sets the condtion to .
158 | /// A of sets the condtion to . 159 | ///
160 | /// If is null. 161 | public static JsonSerializerOptions SetIgnoreNullValues(this JsonSerializerOptions options, bool value) 162 | => SetIgnoreNullValues(options, value ? JsonIgnoreCondition.WhenWritingNull : JsonIgnoreCondition.Never); 163 | 164 | /// 165 | /// Sets the value, defaulting to ignore nulls. 166 | /// 167 | /// If is null. 168 | public static JsonSerializerOptions SetIgnoreNullValues(this JsonSerializerOptions options, JsonIgnoreCondition value = JsonIgnoreCondition.WhenWritingNull) 169 | { 170 | if (options is null) throw new ArgumentNullException(nameof(options)); 171 | Contract.EndContractBlock(); 172 | 173 | options.DefaultIgnoreCondition = value; 174 | return options; 175 | } 176 | 177 | /// 178 | /// Sets the value to . 179 | /// 180 | /// If is null. 181 | public static JsonSerializerOptions UseUnsafeEncoding(this JsonSerializerOptions options) 182 | { 183 | if (options is null) throw new ArgumentNullException(nameof(options)); 184 | Contract.EndContractBlock(); 185 | 186 | options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; 187 | return options; 188 | } 189 | 190 | /// 191 | /// Adds a converter to the options. 192 | /// 193 | /// If is null. 194 | public static JsonSerializerOptions AddConverter(this JsonSerializerOptions options, JsonConverter converter) 195 | { 196 | if (options is null) throw new ArgumentNullException(nameof(options)); 197 | Contract.EndContractBlock(); 198 | 199 | options.Converters.Add(converter); 200 | return options; 201 | } 202 | 203 | static JsonSerializerOptions RoundDoublesCore(this JsonSerializerOptions options, int maxDecimals) 204 | => options.Converters.FirstOrDefault(c => c is JsonConverter) switch 205 | { 206 | null => options.AddConverter(new JsonDoubleRoundingConverter(maxDecimals)), 207 | JsonDoubleRoundingConverter e when e.Maximum == maxDecimals => options, 208 | _ => throw new InvalidOperationException("A specific double converter already exists.") 209 | }; 210 | 211 | static JsonSerializerOptions RoundNullableDoublesCore(this JsonSerializerOptions options, int maxDecimals) 212 | { 213 | JsonConverter? existing = options.Converters.FirstOrDefault(c => c is JsonConverter); 214 | if (existing is JsonNullableDoubleConverter && existing.GetType() == typeof(JsonNullableDoubleConverter)) 215 | { 216 | options.Converters.Remove(existing); 217 | existing = null; 218 | } 219 | 220 | return existing switch 221 | { 222 | null => options.AddConverter(new JsonNullableDoubleRoundingConverter(maxDecimals)), 223 | JsonNullableDoubleRoundingConverter e when e.Maximum == maxDecimals => options, 224 | _ => throw new InvalidOperationException("A specific double converter already exists.") 225 | }; 226 | } 227 | 228 | /// 229 | /// Adds a special converter that rounds double values to the level. 230 | /// 231 | /// If is null. 232 | public static JsonSerializerOptions RoundDoubles(this JsonSerializerOptions options, int maxDecimals) 233 | { 234 | if (options is null) throw new ArgumentNullException(nameof(options)); 235 | Contract.EndContractBlock(); 236 | 237 | return options 238 | .RoundDoublesCore(maxDecimals) 239 | .RoundNullableDoublesCore(maxDecimals); 240 | } 241 | 242 | /// 243 | /// Adds a special converter that normalizes decimals so they are consistent regardless of trailing zeros. 244 | /// 245 | /// If is null. 246 | public static JsonSerializerOptions NormalizeDecimals(this JsonSerializerOptions options) 247 | { 248 | if (options is null) throw new ArgumentNullException(nameof(options)); 249 | Contract.EndContractBlock(); 250 | 251 | JsonConverter? existing = options.Converters.FirstOrDefault(c => c is JsonConverter); 252 | var existingNullable = options.Converters.FirstOrDefault(c => c is JsonConverter); 253 | 254 | if (existing is JsonDecimalConverter && existingNullable is JsonNullableDecimalConverter) 255 | return options; 256 | 257 | if (existing is null && existingNullable is null) 258 | { 259 | return options 260 | .AddConverter(JsonDecimalConverter.Instance) 261 | .AddConverter(JsonNullableDecimalConverter.Instance); 262 | } 263 | 264 | if (existing is not null) 265 | throw new InvalidOperationException("A specific decimal converter already exists."); 266 | 267 | throw new InvalidOperationException("A specific Nullable converter already exists."); 268 | } 269 | 270 | static JsonSerializerOptions RoundDecimalsCore(this JsonSerializerOptions options, int maxDecimals) 271 | { 272 | JsonConverter? existing = options.Converters.FirstOrDefault(c => c is JsonConverter); 273 | if (existing is JsonDecimalConverter && existing.GetType() == typeof(JsonDecimalConverter)) 274 | { 275 | options.Converters.Remove(existing); 276 | existing = null; 277 | } 278 | 279 | return existing switch 280 | { 281 | null => options.AddConverter(new JsonDecimalRoundingConverter(maxDecimals)), 282 | JsonDecimalRoundingConverter e when e.Maximum == maxDecimals => options, 283 | _ => throw new InvalidOperationException("A specific decimal converter already exists.") 284 | }; 285 | } 286 | 287 | static JsonSerializerOptions RoundNullableDecimalsCore(this JsonSerializerOptions options, int maxDecimals) 288 | { 289 | JsonConverter? existing = options.Converters.FirstOrDefault(c => c is JsonConverter); 290 | if (existing is JsonNullableDecimalConverter && existing.GetType() == typeof(JsonNullableDecimalConverter)) 291 | { 292 | options.Converters.Remove(existing); 293 | existing = null; 294 | } 295 | 296 | return existing switch 297 | { 298 | null => options.AddConverter(new JsonNullableDecimalRoundingConverter(maxDecimals)), 299 | JsonNullableDecimalRoundingConverter e when e.Maximum == maxDecimals => options, 300 | _ => throw new InvalidOperationException("A specific Nullable converter already exists.") 301 | }; 302 | } 303 | 304 | /// 305 | /// Adds a special converter that rounds decimal values to the level. 306 | /// 307 | /// If is null. 308 | public static JsonSerializerOptions RoundDecimals(this JsonSerializerOptions options, int maxDecimals) 309 | { 310 | if (options is null) throw new ArgumentNullException(nameof(options)); 311 | Contract.EndContractBlock(); 312 | 313 | return options 314 | .RoundDecimalsCore(maxDecimals) 315 | .RoundNullableDecimalsCore(maxDecimals); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Utf8Json/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CA1303: Do not pass literals as localized parameters 4 | dotnet_diagnostic.CA1303.severity = silent 5 | 6 | # IDE0022: Use block body for methods 7 | csharp_style_expression_bodied_methods = false:silent 8 | 9 | # IDE0060: Remove unused parameter 10 | dotnet_code_quality_unused_parameters = all:silent 11 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Utf8Json/JsonSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Utf8Json; 4 | using Utf8Json.Resolvers; 5 | 6 | namespace Open.Serialization.Json.Utf8Json; 7 | 8 | /// 9 | /// The for Utf8Json. 10 | /// 11 | public class JsonSerializerFactory : IJsonSerializerFactory 12 | { 13 | readonly IJsonFormatterResolver _resolver; 14 | readonly bool _indent; 15 | 16 | /// Snake case is not supported. 17 | /// 18 | public JsonSerializerFactory(IJsonFormatterResolver? defaultResolver, bool indent = false) 19 | { 20 | _resolver = defaultResolver ?? StandardResolver.Default; 21 | if (_resolver == StandardResolver.ExcludeNullSnakeCase || _resolver == StandardResolver.SnakeCase) 22 | throw new ArgumentOutOfRangeException(nameof(defaultResolver), "Snake case is not supported."); 23 | _indent = indent; 24 | } 25 | 26 | /// 27 | /// Constructs a . 28 | /// 29 | public JsonSerializerFactory() : this(null) 30 | { 31 | } 32 | 33 | JsonSerializerInternal? _defaultSerializer; 34 | JsonSerializerInternal DefaultSerializer 35 | => LazyInitializer.EnsureInitialized(ref _defaultSerializer, () => new JsonSerializerInternal(_resolver, _indent))!; 36 | 37 | static JsonSerializerFactory? _default; 38 | 39 | /// 40 | /// The default instance (with default settings) of this . 41 | /// 42 | public static JsonSerializerFactory Default 43 | => LazyInitializer.EnsureInitialized(ref _default)!; 44 | 45 | internal JsonSerializerInternal GetSerializerInternal(IJsonSerializationOptions? options = null, bool caseSensitive = false) 46 | { 47 | if (caseSensitive) 48 | throw new NotSupportedException("Utf8Json does not support case-sensitive deserialization."); 49 | 50 | if (options is null) 51 | return DefaultSerializer; 52 | 53 | if (options.CamelCaseKeys == true) 54 | throw new NotSupportedException("Utf8Json does not support camel casing keys."); 55 | 56 | var omitNull = options.OmitNull == true || _resolver == StandardResolver.ExcludeNull || _resolver == StandardResolver.ExcludeNullCamelCase; 57 | var camelCase = options.CamelCaseProperties == true || _resolver == StandardResolver.CamelCase || _resolver == StandardResolver.ExcludeNullCamelCase; 58 | 59 | return camelCase 60 | ? new JsonSerializerInternal(omitNull ? StandardResolver.ExcludeNullCamelCase : StandardResolver.CamelCase, options.Indent ?? _indent) 61 | : new JsonSerializerInternal(omitNull ? StandardResolver.ExcludeNull : StandardResolver.Default, options.Indent ?? _indent); 62 | } 63 | 64 | /// 65 | /// Returns an . 66 | /// 67 | public IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false) 68 | => GetSerializerInternal(options, caseSensitive); 69 | 70 | /// 71 | /// Returns an . 72 | /// 73 | public IJsonObjectSerializer GetObjectSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false) 74 | => GetSerializerInternal(options, caseSensitive); 75 | } 76 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Utf8Json/JsonSerializerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Utf8Json; 6 | 7 | namespace Open.Serialization.Json.Utf8Json; 8 | 9 | internal class JsonSerializerInternal : JsonObjectSerializerBase, IJsonSerializer 10 | { 11 | readonly IJsonFormatterResolver _resolver; 12 | readonly bool _indent; 13 | internal JsonSerializerInternal(IJsonFormatterResolver resolver, bool indent) 14 | { 15 | _resolver = resolver; 16 | _indent = indent; 17 | } 18 | 19 | public override T Deserialize(string value) 20 | => JsonSerializer.Deserialize(value, _resolver); 21 | 22 | public new Task DeserializeAsync(Stream stream, CancellationToken cancellationToken = default) 23 | { 24 | cancellationToken.ThrowIfCancellationRequested(); 25 | return JsonSerializer.DeserializeAsync(stream, _resolver); 26 | } 27 | 28 | ValueTask IDeserializeAsync.DeserializeAsync(Stream stream, CancellationToken cancellationToken) 29 | => new(DeserializeAsync(stream, cancellationToken)); 30 | 31 | public override string Serialize(T item) 32 | { 33 | var json = JsonSerializer.ToJsonString(item, _resolver); 34 | return _indent ? JsonSerializer.PrettyPrint(json) : json; 35 | } 36 | 37 | public new Task SerializeAsync(Stream stream, T item, CancellationToken cancellationToken = default) 38 | { 39 | cancellationToken.ThrowIfCancellationRequested(); 40 | if (!_indent) return JsonSerializer.SerializeAsync(stream, item); 41 | 42 | var result = JsonSerializer.PrettyPrintByteArray(JsonSerializer.Serialize(item)); 43 | return stream.WriteAsync(result, 0, result.Length, cancellationToken); 44 | } 45 | 46 | ValueTask ISerializeAsync.SerializeAsync(Stream stream, T item, CancellationToken cancellationToken) 47 | => new(SerializeAsync(stream, item, cancellationToken)); 48 | 49 | public override object? Deserialize(string value, Type type) 50 | => JsonSerializer.NonGeneric.Deserialize(type, value, _resolver); 51 | 52 | public override string Serialize(object? item, Type type) 53 | { 54 | var json = JsonSerializer.NonGeneric.ToJsonString(type, item, _resolver); 55 | return _indent ? JsonSerializer.PrettyPrint(json) : json; 56 | } 57 | 58 | public override ValueTask DeserializeAsync(Stream source, Type type, CancellationToken cancellationToken = default) 59 | => new(JsonSerializer.NonGeneric.DeserializeAsync(type, source, _resolver)); 60 | 61 | public override ValueTask SerializeAsync(Stream target, object? item, Type type, CancellationToken cancellationToken = default) 62 | => _indent 63 | ? base.SerializeAsync(target, item, type, cancellationToken) 64 | : new ValueTask(JsonSerializer.NonGeneric.SerializeAsync(type, target, item, _resolver)); 65 | 66 | public new JsonSerializer Cast() 67 | => new(Deserialize, Serialize, ((IDeserializeAsync)this).DeserializeAsync, ((ISerializeAsync)this).SerializeAsync); 68 | } 69 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Utf8Json/Open.Serialization.Json.Utf8Json.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0; netstandard2.1 5 | true 6 | latest 7 | enable 8 | true 9 | electricessence 10 | 11 | Extentions and DI utilities for Utf8Json. 12 | 13 | Part of the "Open" set of libraries. 14 | 15 | serialization;json;utf8json 16 | © electricessence (Oren F.) All rights reserved. 17 | MIT 18 | https://github.com/Open-NET-Libraries/Open.Serialization 19 | https://github.com/Open-NET-Libraries/Open.Serialization 20 | git 21 | 3.0.0 22 | 23 | MIT 24 | true 25 | true 26 | snupkg 27 | logo.png 28 | true 29 | README.md 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | True 41 | 42 | 43 | 44 | True 45 | \ 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Open.Serialization.Json.Utf8Json/SerializationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using Utf8Json; 4 | 5 | namespace Open.Serialization.Json.Utf8Json; 6 | 7 | /// 8 | /// Extensions for Utf8Json serialization with Open.Serialization.Json. 9 | /// 10 | public static class SerializationExtensions 11 | { 12 | /// 13 | /// Adds a generic serializer and non-generic to the service collection. 14 | /// 15 | /// The service collection. 16 | /// The options overrides. 17 | /// The service collection. 18 | public static IServiceCollection AddJsonSerializer(this IServiceCollection services, IJsonSerializationOptions? options = null) 19 | { 20 | var factory = JsonSerializerFactory.Default; 21 | var serializer = factory.GetSerializerInternal(options); 22 | services.AddSingleton(serializer); 23 | services.AddSingleton(serializer); 24 | return services; 25 | } 26 | 27 | /// 28 | /// Adds a generic serializer to the service collection. 29 | /// 30 | /// 31 | public static IServiceCollection AddJsonSerializer(this IServiceCollection services, IJsonSerializationOptions? options = null) 32 | { 33 | var factory = JsonSerializerFactory.Default; 34 | services.AddSingleton>(factory.GetSerializerInternal(options).Cast()); 35 | return services; 36 | } 37 | 38 | /// 39 | /// Returns a delegate for deserializing a to . 40 | /// 41 | public static Func GetDeserialize(this IJsonFormatterResolver options) 42 | => json => JsonSerializer.Deserialize(json, options); 43 | 44 | /// 45 | /// Returns a delegate for serializing to a . 46 | /// 47 | public static Func GetSerialize(this IJsonFormatterResolver options, bool indent = false) 48 | => item => 49 | { 50 | var result = JsonSerializer.ToJsonString(item, options); 51 | return indent ? JsonSerializer.PrettyPrint(result) : result; 52 | }; 53 | 54 | /// 55 | /// Returns a delegate for serializing to . 56 | /// 57 | public static Func GetSerialize(this IJsonFormatterResolver options, bool indent = false) 58 | => item => 59 | { 60 | var result = JsonSerializer.ToJsonString(item, options); 61 | return indent ? JsonSerializer.PrettyPrint(result) : result; 62 | }; 63 | 64 | /// 65 | /// Returns an with the providied options. 66 | /// 67 | public static IJsonSerializer GetSerializer(this IJsonFormatterResolver options, bool indent = false) 68 | => new JsonSerializerInternal(options, indent); 69 | 70 | /// 71 | /// Returns an with the providied options. 72 | /// 73 | public static IJsonSerializer GetSerializer(this IJsonFormatterResolver options, bool indent = false) 74 | => new JsonSerializerInternal(options, indent).Cast(); 75 | 76 | /// 77 | /// Returns an with the providied options. 78 | /// 79 | public static IJsonSerializerFactory GetSerializerFactory(this IJsonFormatterResolver options) 80 | => new JsonSerializerFactory(options); 81 | 82 | /// 83 | /// Serializes a to a using the provided options. 84 | /// 85 | public static string Serialize(this IJsonFormatterResolver options, TValue value) 86 | => JsonSerializer.ToJsonString(value, options); 87 | 88 | /// 89 | /// Serializes an to a using the provided options. 90 | /// 91 | public static string Serialize(this IJsonFormatterResolver options, object? value) 92 | => JsonSerializer.ToJsonString(value, options); 93 | 94 | /// 95 | /// Deserializes a to using the provided options. 96 | /// 97 | public static TValue Deserialize(this IJsonFormatterResolver options, string value) 98 | => JsonSerializer.Deserialize(value, options); 99 | } 100 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonAsyncObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonAsyncObjectSerializer : IAsyncObjectSerializer 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonAsyncSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonAsyncSerializer : IAsyncSerializer 5 | { 6 | } 7 | 8 | /// 9 | public interface IJsonAsyncSerializer : IAsyncSerializer 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonDeserialize.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonDeserialize : IDeserialize 5 | { 6 | } 7 | 8 | /// 9 | public interface IJsonDeserialize : IDeserialize 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonDeserializeAsync.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonDeserializeAsync : IDeserializeAsync 5 | { 6 | } 7 | 8 | /// 9 | public interface IJsonDeserializeAsync : IDeserializeAsync 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonDeserializeObject.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonDeserializeObject : IDeserializeObject 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonDeserializeObjectAsync.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonDeserializeObjectAsync : IDeserializeObjectAsync 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonObjectSerializationFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | /// Factory for generating JSON serializers and deserializers. 5 | /// 6 | public interface IJsonObjectSerializationFactory 7 | { 8 | /// 9 | /// Returns the requested deserializer 10 | /// 11 | IJsonDeserializeObject GetDeserializer(bool caseSensitive = false); 12 | 13 | /// 14 | /// Returns the requested deserializer 15 | /// 16 | IJsonDeserializeObjectAsync GetAsyncDeserializer(bool caseSensitive = false); 17 | 18 | /// 19 | /// Returns the requested serializer 20 | /// 21 | IJsonSerializeObject GetSerializer(IJsonSerializationOptions? options = null); 22 | 23 | /// 24 | /// Returns the requested serializer 25 | /// 26 | IJsonSerializeObjectAsync GetAsyncSerializer(IJsonSerializationOptions? options = null); 27 | } 28 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | // Provided as a means of specificity when setting up DI. 4 | 5 | /// 6 | public interface IJsonObjectSerializer : IObjectSerializer, IJsonSerializeObject, IJsonDeserializeObject, IJsonDeserializeObjectAsync, IJsonSerializeObjectAsync 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonObjectSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | /// Factory for generating a JSON serializer/deserializer. 5 | /// 6 | public interface IJsonObjectSerializerFactory 7 | { 8 | /// 9 | /// Returns the requested serializer 10 | /// 11 | IJsonObjectSerializer GetObjectSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false); 12 | } 13 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonSerializationFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | /// Factory for generating JSON generic serializers and deserializers. 5 | /// 6 | public interface IJsonSerializationFactory 7 | { 8 | /// 9 | /// Returns the requested deserializer 10 | /// 11 | IJsonDeserialize GetDeserializer(bool caseSensitive = false); 12 | 13 | /// 14 | /// Returns the requested deserializer 15 | /// 16 | IJsonDeserializeAsync GetAsyncDeserializer(bool caseSensitive = false); 17 | 18 | /// 19 | /// Returns the requested serializer 20 | /// 21 | IJsonSerialize GetSerializer(IJsonSerializationOptions? options = null); 22 | 23 | /// 24 | /// Returns the requested serializer 25 | /// 26 | IJsonSerializeAsync GetAsyncSerializer(IJsonSerializationOptions? options = null); 27 | } 28 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonSerializationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | /// Basic properties needed for modifying how JSON is written. 5 | /// 6 | public interface IJsonSerializationOptions 7 | { 8 | /// 9 | /// If true the serializer will format the JSON properties camelCase. 10 | /// 11 | bool? CamelCaseProperties { get; } 12 | 13 | /// 14 | /// If true the serializer will format the JSON map keys camelCase. 15 | /// 16 | bool? CamelCaseKeys { get; } 17 | 18 | /// 19 | /// If true the serializer will omit map entries with null values. 20 | /// 21 | bool? OmitNull { get; } 22 | 23 | /// 24 | /// If true the serializer will format the JSON multi-line indented. 25 | /// 26 | bool? Indent { get; } 27 | } 28 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonSerialize.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonSerialize : ISerialize 5 | { 6 | } 7 | 8 | /// 9 | public interface IJsonSerialize : ISerialize 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonSerializeAsync.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonSerializeAsync : ISerializeAsync 5 | { 6 | } 7 | 8 | /// 9 | public interface IJsonSerializeAsync : ISerializeAsync 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonSerializeObject.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonSerializeObject : ISerializeObject 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonSerializeObjectAsync.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public interface IJsonSerializeObjectAsync : ISerializeObjectAsync 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | // Provided as a means of specificity when setting up DI. 4 | 5 | /// 6 | public interface IJsonSerializer : ISerializer, IJsonSerialize, IJsonDeserialize, IJsonDeserializeAsync, IJsonSerializeAsync 7 | { 8 | } 9 | 10 | /// 11 | public interface IJsonSerializer : ISerializer, IJsonSerialize, IJsonDeserialize, IJsonDeserializeAsync, IJsonSerializeAsync 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /Open.Serialization.Json/IJsonSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | /// Factory for generating a JSON generic serializer/deserializer. 5 | /// 6 | public interface IJsonSerializerFactory 7 | { 8 | /// 9 | /// Returns the requested serializer 10 | /// 11 | IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false); 12 | } 13 | -------------------------------------------------------------------------------- /Open.Serialization.Json/JsonObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Open.Serialization.Json; 7 | 8 | /// 9 | public class JsonObjectSerializer : ObjectSerializer, IJsonObjectSerializer, IJsonAsyncObjectSerializer 10 | { 11 | /// 12 | public JsonObjectSerializer( 13 | Func? deserializer, 14 | Func? serializer = null, 15 | Func>? deserializerAsync = null, 16 | Func? serializerAsync = null) 17 | : base(deserializer, serializer, deserializerAsync, serializerAsync) 18 | { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Open.Serialization.Json/JsonObjectSerializerBase.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public abstract class JsonObjectSerializerBase : ObjectSerializerBase, IJsonObjectSerializer, IJsonAsyncObjectSerializer 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Open.Serialization.Json/JsonSerializationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public class JsonSerializationOptions : IJsonSerializationOptions 5 | { 6 | /// 7 | public bool? CamelCaseProperties { get; set; } 8 | 9 | /// 10 | public bool? CamelCaseKeys { get; set; } 11 | 12 | /// 13 | public bool? OmitNull { get; set; } 14 | 15 | /// 16 | public bool? Indent { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /Open.Serialization.Json/JsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Open.Serialization.Json; 7 | 8 | /// 9 | public class JsonSerializer : Serializer, IJsonSerializer, IJsonAsyncSerializer 10 | { 11 | /// 12 | public JsonSerializer( 13 | Func? deserializer, 14 | Func? serializer = null, 15 | Func>? deserializerAsync = null, 16 | Func? serializerAsync = null) 17 | : base(deserializer, serializer, deserializerAsync, serializerAsync) 18 | { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Open.Serialization.Json/JsonSerializerBase.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization.Json; 2 | 3 | /// 4 | public abstract class JsonSerializerBase : SerializerBase, IJsonSerializer, IJsonAsyncSerializer 5 | { 6 | /// 7 | public new JsonSerializer Cast() 8 | => new(Deserialize, Serialize, DeserializeAsync, SerializeAsync); 9 | } 10 | 11 | /// 12 | public abstract class JsonSerializerBase : SerializerBase, IJsonSerializer, IJsonAsyncSerializer 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /Open.Serialization.Json/Open.Serialization.Json.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0; netstandard2.1 5 | true 6 | latest 7 | enable 8 | true 9 | electricessence 10 | 11 | DI/IoC agnostic interfaces for injecting any JSON serialization library. 12 | 13 | Part of the "Open" set of libraries. 14 | 15 | serialization;json 16 | © electricessence (Oren F.) All rights reserved. 17 | MIT 18 | https://github.com/Open-NET-Libraries/Open.Serialization 19 | https://github.com/Open-NET-Libraries/Open.Serialization 20 | git 21 | 3.0.0 22 | 23 | MIT 24 | true 25 | true 26 | snupkg 27 | logo.png 28 | true 29 | README.md 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | True 39 | 40 | 41 | 42 | True 43 | \ 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Open.Serialization.Json/Open.Serialization.Json.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Open.Serialization.Json 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Factory for generating JSON serializers and deserializers. 37 | 38 | 39 | 40 | 41 | Returns the requested deserializer 42 | 43 | 44 | 45 | 46 | Returns the requested deserializer 47 | 48 | 49 | 50 | 51 | Returns the requested serializer 52 | 53 | 54 | 55 | 56 | Returns the requested serializer 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | Factory for generating a JSON serializer/deserializer. 65 | 66 | 67 | 68 | 69 | Returns the requested serializer 70 | 71 | 72 | 73 | 74 | Factory for generating JSON generic serializers and deserializers. 75 | 76 | 77 | 78 | 79 | Returns the requested deserializer 80 | 81 | 82 | 83 | 84 | Returns the requested deserializer 85 | 86 | 87 | 88 | 89 | Returns the requested serializer 90 | 91 | 92 | 93 | 94 | Returns the requested serializer 95 | 96 | 97 | 98 | 99 | Basic properties needed for modifying how JSON is written. 100 | 101 | 102 | 103 | 104 | If true the serializer will format the JSON properties camelCase. 105 | 106 | 107 | 108 | 109 | If true the serializer will format the JSON map keys camelCase. 110 | 111 | 112 | 113 | 114 | If true the serializer will omit map entries with null values. 115 | 116 | 117 | 118 | 119 | If true the serializer will format the JSON multi-line indented. 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | Factory for generating a JSON generic serializer/deserializer. 149 | 150 | 151 | 152 | 153 | Returns the requested serializer 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /Open.Serialization.Tests/DefaultImplementationTests.cs: -------------------------------------------------------------------------------- 1 | using Open.Serialization.Extensions; 2 | using System; 3 | using Xunit; 4 | 5 | namespace Open.Serialization.Tests; 6 | 7 | public static class DefaultImplementationTests 8 | { 9 | [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")] 10 | [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Roslynator", "RCS1163:Unused parameter.")] 11 | [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter")] 12 | class A : IDeserializeObject 13 | { 14 | public object Deserialize(string value, Type type) => 1; 15 | 16 | public object Deserialize(ReadOnlySpan value, Type type) => 1; 17 | } 18 | 19 | [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")] 20 | class B : A 21 | { 22 | public T Deserialize(string _) => default; 23 | } 24 | 25 | class C : B, IDeserializeObject 26 | { 27 | } 28 | 29 | [Fact] 30 | public static void DefaultImplementation() 31 | { 32 | IDeserializeObject a1 = new A(); 33 | Assert.Equal(1, a1.Deserialize("0")); 34 | 35 | var b1 = new B(); 36 | Assert.Equal(0, b1.Deserialize("0")); 37 | 38 | IDeserializeObject b2 = new B(); 39 | Assert.Equal(1, b2.Deserialize("0")); 40 | 41 | var c1 = new C(); 42 | Assert.Equal(0, c1.Deserialize("0")); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Open.Serialization.Tests/Newtonsoft/JsonExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Open.Serialization.Json.Newtonsoft; 3 | using System; 4 | using Xunit; 5 | using FluentAssertions; 6 | 7 | namespace Open.Serialization.Tests.Newtonsoft; 8 | 9 | public static class JsonExtensionTests 10 | { 11 | [Fact] 12 | public static void ValidateJsonExtensions() 13 | { 14 | { 15 | var basic = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }; 16 | var serializer = basic 17 | .NormalizeDecimals() 18 | .RoundDoubles(2) 19 | .RoundDecimals(2) 20 | .GetSerializer(); 21 | 22 | var json = serializer.Serialize(SampleModel.Instance); 23 | var obj = serializer.Deserialize(json); 24 | Assert.NotNull(obj); 25 | } 26 | 27 | { 28 | var basic = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }; 29 | var serializer = basic 30 | .RoundDoubles(2) 31 | .RoundDecimals(2) 32 | .GetSerializer(); 33 | 34 | var json = serializer.Serialize(SampleModel.Instance); 35 | var obj = serializer.Deserialize(json); 36 | Assert.NotNull(obj); 37 | } 38 | 39 | { 40 | var basic = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }; 41 | var serializer = basic 42 | .RoundDecimals(2) 43 | .RoundDoubles(2) 44 | .GetSerializer(); 45 | 46 | var json = serializer.Serialize(SampleModel.Instance); 47 | var obj = serializer.Deserialize(json); 48 | Assert.NotNull(obj); 49 | } 50 | } 51 | 52 | [Fact] 53 | public static void ValidateDecimals() 54 | { 55 | const decimal sample = 0.234567890123456789012345m; 56 | { 57 | var serializer = CamelCaseJson 58 | .Default() 59 | .NormalizeDecimals() 60 | .GetSerializer(); 61 | 62 | { 63 | var model = new SampleModel 64 | { 65 | DecimalValue1 = sample 66 | }; 67 | var json = serializer.Serialize(model); 68 | Assert.Equal(sample, serializer.Deserialize(json).DecimalValue1); 69 | } 70 | 71 | { 72 | var model = new SampleModel 73 | { 74 | NullableDecimalValue = sample 75 | }; 76 | var json = serializer.Serialize(model); 77 | Assert.Equal(sample, serializer.Deserialize(json).NullableDecimalValue); 78 | } 79 | } 80 | 81 | { 82 | var serializer = CamelCaseJson 83 | .Default() 84 | .RoundDecimals(2) 85 | .GetSerializer(); 86 | 87 | var sample2 = Math.Round(sample, 2); 88 | 89 | { 90 | var model = new SampleModel 91 | { 92 | DecimalValue1 = sample 93 | }; 94 | var json = serializer.Serialize(model); 95 | Assert.Equal(sample2, serializer.Deserialize(json).DecimalValue1); 96 | } 97 | 98 | { 99 | var model = new SampleModel 100 | { 101 | NullableDecimalValue = sample 102 | }; 103 | var json = serializer.Serialize(model); 104 | Assert.Equal(sample2, serializer.Deserialize(json).NullableDecimalValue); 105 | } 106 | } 107 | 108 | { 109 | var serializer = CamelCaseJson 110 | .Default() 111 | .GetSerializer(); 112 | 113 | const int sample3 = 1; 114 | 115 | { 116 | var model = new SampleModel 117 | { 118 | DecimalValue1 = sample3 119 | }; 120 | var json = serializer.Serialize(model); 121 | Assert.Equal(sample3, serializer.Deserialize(json).DecimalValue1); 122 | } 123 | 124 | { 125 | var model = new SampleModel 126 | { 127 | NullableDecimalValue = sample3 128 | }; 129 | var json = serializer.Serialize(model); 130 | Assert.Equal(sample3, serializer.Deserialize(json).NullableDecimalValue); 131 | } 132 | } 133 | } 134 | 135 | [Fact] 136 | public static void ValidateNulls() 137 | { 138 | Assert.Throws(()=>RelaxedJson.Deserialize(null)); 139 | RelaxedJson.Deserialize("null").Should().BeNull(); 140 | Assert.Throws(()=>RelaxedJson.Deserialize("null")); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Open.Serialization.Tests/Open.Serialization.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Open.Serialization.Tests/ParityTests.cs: -------------------------------------------------------------------------------- 1 | using Open.Serialization.Json; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | using Xunit; 6 | 7 | namespace Open.Serialization.Tests; 8 | 9 | public static class ParityTests 10 | { 11 | [Fact] 12 | public static async Task EnsureBasicParity() 13 | { 14 | { 15 | var newtonsoftFactory = new Json.Newtonsoft.JsonSerializerFactory(Json.Newtonsoft.CamelCaseJson.Minimal(true)); 16 | var systemFactory = new Json.System.JsonSerializerFactory(Json.System.CamelCaseJson.Minimal(true)); 17 | 18 | await CompareFactories(newtonsoftFactory, systemFactory); 19 | } 20 | 21 | { 22 | var newtonsoftFactory = new Json.Newtonsoft.JsonSerializerFactory(); 23 | var systemFactory = new Json.System.JsonSerializerFactory(); 24 | 25 | await CompareFactories(newtonsoftFactory, systemFactory); 26 | } 27 | 28 | { 29 | var newtonsoftFactory = new Json.Newtonsoft.JsonSerializerFactory(Json.Newtonsoft.CamelCaseJson.Minimal(true)); 30 | var utf8JsonFactory = new Json.Utf8Json.JsonSerializerFactory(Utf8Json.Resolvers.StandardResolver.ExcludeNullCamelCase, true); 31 | 32 | await CompareFactories(newtonsoftFactory, utf8JsonFactory); 33 | 34 | var ns = newtonsoftFactory.GetObjectSerializer(); 35 | var utf8 = utf8JsonFactory.GetObjectSerializer(); 36 | 37 | var instance = SampleModel.Instance; 38 | var type = SampleModel.Instance.GetType(); 39 | var expected = ns.Serialize(instance, type); 40 | var actual = utf8.Serialize(instance, type); 41 | if (Debugger.IsAttached) Debug.Assert(expected == actual); 42 | Assert.Equal(expected, actual); 43 | } 44 | } 45 | 46 | static async ValueTask CompareFactories(IJsonSerializerFactory expectedFactory, IJsonSerializerFactory actualFactory) 47 | { 48 | var expectedSerializer = expectedFactory.GetSerializer(); 49 | var actualSerializer = actualFactory.GetSerializer(); 50 | 51 | await CompareSerializer(SampleModel.Instance); 52 | await CompareSerializer(SampleModel.DecimalList); 53 | await CompareSerializer(SampleModel.DecimalLookup); 54 | await CompareSerializer(SampleModel.DoubleLookup); 55 | expectedFactory.GetSerializer().Deserialize(actualSerializer.Serialize(SampleModel.Instance)); 56 | 57 | async ValueTask CompareSerializer(T instance) 58 | { 59 | var expected = expectedSerializer.Serialize(instance); 60 | var actual = actualSerializer.Serialize(instance); 61 | if (Debugger.IsAttached) Debug.Assert(expected == actual); 62 | Assert.Equal(expected, actual); 63 | 64 | using var stream = new MemoryStream(); 65 | await actualSerializer.SerializeAsync(stream, instance); 66 | 67 | stream.Position = 0; 68 | var result = await actualSerializer.DeserializeAsync(stream); 69 | var actualAsync = actualSerializer.Serialize(result); 70 | if (Debugger.IsAttached) Debug.Assert(expected == actualAsync); 71 | Assert.Equal(expected, actual); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Open.Serialization.Tests/SampleModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Open.Serialization.Tests; 4 | 5 | public class SampleModel 6 | { 7 | public static readonly IReadOnlyList DecimalList = new decimal?[] { 3.45678m, 45.6789m, null }; 8 | 9 | public static IDictionary DecimalLookup { get; set; } = new SortedDictionary { { "NumberOne", 1.2345m }, { "NumberTwo", 2345.6m } }; 10 | 11 | public static IDictionary DoubleLookup { get; set; } = new SortedDictionary { { "NumberOne", 1.2345 }, { "NumberTwo", 2345.6 } }; 12 | 13 | public int IntegerValue { get; set; } = 11; 14 | 15 | public double DoubleValue { get; set; } = 1.2345678901234567; 16 | public decimal DecimalValue1 { get; set; } = 1.2345678901234567m; 17 | 18 | public decimal DecimalValue2 { get; set; } = 1.2345000m; 19 | public decimal DecimalValue3 { get; set; } = 11.000m; 20 | 21 | public string StringValue { get; set; } = "Hello"; 22 | public string StringValueNull { get; set; } = null; 23 | 24 | public int? NullableIntegerValue { get; set; } = 11; 25 | public int? NullableIntegerNull { get; set; } = null; 26 | 27 | public double? NullableDoubleNull { get; set; } = null; 28 | 29 | public double? NullableDoubleValue { get; set; } = 1.2345678901234567; 30 | public decimal? NullableDecimalValue { get; set; } = 1.2345678901234567m; 31 | public decimal? NullableDecimalNull { get; set; } = null; 32 | 33 | public SampleModel Child { get; set; } 34 | 35 | public static readonly SampleModel Instance = new() { Child = new SampleModel() }; 36 | } 37 | -------------------------------------------------------------------------------- /Open.Serialization.Tests/System/JsonExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Open.Serialization.Json.System; 3 | using Open.Serialization.Json.System.Converters; 4 | using System; 5 | using System.Text.Json; 6 | using System.Text.Json.Serialization; 7 | using Xunit; 8 | 9 | namespace Open.Serialization.Tests.System; 10 | 11 | public static class JsonExtensionTests 12 | { 13 | [Fact] 14 | public static void ValidateJsonExtensions() 15 | { 16 | var basic = new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; 17 | var converter = basic.GetConverter(typeof(decimal)); 18 | Assert.NotNull(converter); 19 | 20 | converter = basic.GetConverter(typeof(decimal?)); 21 | Assert.NotNull(converter); // Was null in previous versions. 22 | 23 | basic.Converters.Add(JsonDecimalConverter.Instance); 24 | converter = basic.GetConverter(typeof(decimal)); 25 | Assert.True(converter is JsonDecimalConverter); 26 | 27 | basic.RoundDecimals(2); 28 | converter = basic.GetConverter(typeof(decimal)); 29 | Assert.True(converter is JsonDecimalRoundingConverter); 30 | 31 | basic 32 | .NormalizeDecimals() 33 | .RoundDoubles(2) 34 | .RoundDecimals(2); 35 | 36 | var serializer = basic.GetSerializer(); 37 | var json = serializer.Serialize(SampleModel.Instance); 38 | var obj = serializer.Deserialize(json); 39 | Assert.NotNull(obj); 40 | } 41 | 42 | [Fact] 43 | public static void ValidateDecimals() 44 | { 45 | const decimal sample = 1.234567890123456789012345m; 46 | var basic = new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; 47 | var serializer = basic 48 | .NormalizeDecimals() 49 | .GetSerializer(); 50 | 51 | { 52 | var model = new SampleModel 53 | { 54 | DecimalValue1 = sample 55 | }; 56 | var json = serializer.Serialize(model); 57 | Assert.Equal(sample, serializer.Deserialize(json).DecimalValue1); 58 | } 59 | 60 | { 61 | var model = new SampleModel 62 | { 63 | NullableDecimalValue = sample 64 | }; 65 | var json = serializer.Serialize(model); 66 | Assert.Equal(sample, serializer.Deserialize(json).NullableDecimalValue); 67 | } 68 | } 69 | 70 | [Fact] 71 | public static void ValidateNulls() 72 | { 73 | Assert.Throws(() => RelaxedJson.Deserialize(default(string)!)); 74 | RelaxedJson.Deserialize("null").Should().BeNull(); 75 | Assert.Throws(() => RelaxedJson.Deserialize("null")); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Open.Serialization.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29411.108 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Serialization", "Open.Serialization\Open.Serialization.csproj", "{AD459933-8F06-4609-A731-C679E1477E91}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Serialization.Json", "Open.Serialization.Json\Open.Serialization.Json.csproj", "{D4B99E89-AF5C-4ADB-BC36-A22A58849B53}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Serialization.Json.System", "Open.Serialization.Json.System\Open.Serialization.Json.System.csproj", "{11C4DC58-266D-4C5B-8A15-1C2ADFB15C29}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Serialization.Json.Newtonsoft", "Open.Serialization.Json.Newtonsoft\Open.Serialization.Json.Newtonsoft.csproj", "{30179FC8-4270-4E4F-86CE-BEA03E9DC358}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Serialization.Json.Utf8Json", "Open.Serialization.Json.Utf8Json\Open.Serialization.Json.Utf8Json.csproj", "{78695C58-3C3E-42DC-B588-9B9F2E9E400B}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Serialization.Tests", "Open.Serialization.Tests\Open.Serialization.Tests.csproj", "{A3278929-4835-4860-94C1-BA442CDEA3FA}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {AD459933-8F06-4609-A731-C679E1477E91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {AD459933-8F06-4609-A731-C679E1477E91}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {AD459933-8F06-4609-A731-C679E1477E91}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {AD459933-8F06-4609-A731-C679E1477E91}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {D4B99E89-AF5C-4ADB-BC36-A22A58849B53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {D4B99E89-AF5C-4ADB-BC36-A22A58849B53}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {D4B99E89-AF5C-4ADB-BC36-A22A58849B53}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {D4B99E89-AF5C-4ADB-BC36-A22A58849B53}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {11C4DC58-266D-4C5B-8A15-1C2ADFB15C29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {11C4DC58-266D-4C5B-8A15-1C2ADFB15C29}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {11C4DC58-266D-4C5B-8A15-1C2ADFB15C29}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {11C4DC58-266D-4C5B-8A15-1C2ADFB15C29}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {30179FC8-4270-4E4F-86CE-BEA03E9DC358}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {30179FC8-4270-4E4F-86CE-BEA03E9DC358}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {30179FC8-4270-4E4F-86CE-BEA03E9DC358}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {30179FC8-4270-4E4F-86CE-BEA03E9DC358}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {78695C58-3C3E-42DC-B588-9B9F2E9E400B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {78695C58-3C3E-42DC-B588-9B9F2E9E400B}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {78695C58-3C3E-42DC-B588-9B9F2E9E400B}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {78695C58-3C3E-42DC-B588-9B9F2E9E400B}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {A3278929-4835-4860-94C1-BA442CDEA3FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {A3278929-4835-4860-94C1-BA442CDEA3FA}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {A3278929-4835-4860-94C1-BA442CDEA3FA}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {A3278929-4835-4860-94C1-BA442CDEA3FA}.Release|Any CPU.Build.0 = Release|Any CPU 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {0AB837D3-B1BE-471B-B860-A2D16EEB71F5} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /Open.Serialization/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CA1303: Do not pass literals as localized parameters 4 | dotnet_diagnostic.CA1303.severity = silent 5 | -------------------------------------------------------------------------------- /Open.Serialization/DefaultMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | 5 | namespace Open.Serialization; 6 | 7 | /// 8 | /// Default utiltiy methods for use with implementing interfaces/classes. 9 | /// 10 | // Note: not implemented as extensions as it would cause ambiguity collisions 11 | // But retained public for use externally. 12 | public static class DefaultMethods 13 | { 14 | /// 15 | /// Deserializes a stream to the specified type. 16 | /// 17 | public static async ValueTask DeserializeAsync(IDeserializeObject deserializer, Stream source, Type type) 18 | { 19 | if (deserializer is null) throw new ArgumentNullException(nameof(deserializer)); 20 | if (source is null) throw new ArgumentNullException(nameof(source)); 21 | if (type is null) throw new ArgumentNullException(nameof(type)); 22 | string text; 23 | using (var reader = new StreamReader(source)) 24 | text = await reader.ReadToEndAsync().ConfigureAwait(false); 25 | return deserializer.Deserialize(text, type); 26 | } 27 | 28 | /// 29 | /// Serializes the provided item to a stream. 30 | /// 31 | public static async ValueTask SerializeAsync(ISerializeObject serializer, Stream target, object? item, Type type) 32 | { 33 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 34 | if (target is null) throw new ArgumentNullException(nameof(target)); 35 | if (type is null) throw new ArgumentNullException(nameof(type)); 36 | var text = serializer.Serialize(item, type); 37 | using var writer = new StreamWriter(target); 38 | await writer.WriteAsync(text).ConfigureAwait(false); 39 | } 40 | 41 | /// 42 | /// Deserializes a stream to the specified type. 43 | /// 44 | public static async ValueTask DeserializeAsync(IDeserialize deserializer, Stream source) 45 | { 46 | if (deserializer is null) throw new ArgumentNullException(nameof(deserializer)); 47 | if (source is null) throw new ArgumentNullException(nameof(source)); 48 | string text; 49 | using (var reader = new StreamReader(source)) 50 | text = await reader.ReadToEndAsync().ConfigureAwait(false); 51 | return deserializer.Deserialize(text)!; 52 | } 53 | 54 | /// 55 | /// Serializes the provided item to a stream. 56 | /// 57 | public static async ValueTask SerializeAsync(ISerialize serializer, Stream target, T item) 58 | { 59 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 60 | if (target is null) throw new ArgumentNullException(nameof(target)); 61 | 62 | var text = serializer.Serialize(item); 63 | using var writer = new StreamWriter(target); 64 | await writer.WriteAsync(text).ConfigureAwait(false); 65 | } 66 | 67 | /// 68 | /// Deserializes a stream to the specified type. 69 | /// 70 | public static async ValueTask DeserializeAsync(IDeserialize deserializer, Stream source) 71 | { 72 | if (deserializer is null) throw new ArgumentNullException(nameof(deserializer)); 73 | if (source is null) throw new ArgumentNullException(nameof(source)); 74 | string text; 75 | using (var reader = new StreamReader(source)) 76 | text = await reader.ReadToEndAsync().ConfigureAwait(false); 77 | return deserializer.Deserialize(text)!; 78 | } 79 | 80 | /// 81 | /// Serializes the provided item to a stream. 82 | /// 83 | public static async ValueTask SerializeAsync(ISerialize serializer, Stream target, T item) 84 | { 85 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 86 | if (target is null) throw new ArgumentNullException(nameof(target)); 87 | 88 | var text = serializer.Serialize(item); 89 | using var writer = new StreamWriter(target); 90 | await writer.WriteAsync(text).ConfigureAwait(false); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Open.Serialization/Extensions/SerializationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Contracts; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Open.Serialization.Extensions; 8 | 9 | /// 10 | /// Default extensions for pure serialization interfaces. 11 | /// 12 | public static class SerializationExtensions 13 | { 14 | /// 15 | public static T Deserialize(this IDeserializeObject deserializer, string value) 16 | { 17 | if (deserializer is null) throw new ArgumentNullException(nameof(deserializer)); 18 | Contract.EndContractBlock(); 19 | 20 | return deserializer is IDeserialize d 21 | ? d.Deserialize(value) 22 | : (T)deserializer.Deserialize(value, typeof(T))!; 23 | } 24 | 25 | /// 26 | public static string Serialize(this ISerializeObject serializer, T item) 27 | { 28 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 29 | Contract.EndContractBlock(); 30 | 31 | return serializer is ISerialize s 32 | ? s.Serialize(item) 33 | : serializer.Serialize(item, typeof(T)); 34 | } 35 | 36 | /// 37 | public static async ValueTask DeserializeAsync(this IDeserializeObject deserializer, Stream source, CancellationToken cancellationToken = default) 38 | { 39 | if (deserializer is null) throw new ArgumentNullException(nameof(deserializer)); 40 | Contract.EndContractBlock(); 41 | 42 | return deserializer is IDeserializeAsync d 43 | ? await d.DeserializeAsync(source, cancellationToken).ConfigureAwait(false) 44 | : (T)(await DeserializeAsync(deserializer, source, typeof(T), cancellationToken).ConfigureAwait(false))!; 45 | } 46 | 47 | /// 48 | public static ValueTask DeserializeAsync(this IDeserializeObject deserializer, Stream source, Type type, CancellationToken cancellationToken = default) 49 | { 50 | if (deserializer is null) throw new ArgumentNullException(nameof(deserializer)); 51 | Contract.EndContractBlock(); 52 | 53 | return deserializer is IDeserializeObjectAsync d 54 | ? d.DeserializeAsync(source, type, cancellationToken) 55 | : DefaultMethods.DeserializeAsync(deserializer, source, type); 56 | } 57 | 58 | /// 59 | public static async ValueTask DeserializeAsync(this IDeserializeObjectAsync deserializer, Stream source, CancellationToken cancellationToken = default) 60 | { 61 | if (deserializer is null) throw new ArgumentNullException(nameof(deserializer)); 62 | Contract.EndContractBlock(); 63 | 64 | return deserializer is IDeserializeAsync d 65 | ? await d.DeserializeAsync(source, cancellationToken).ConfigureAwait(false) 66 | : (T)(await deserializer.DeserializeAsync(source, typeof(T), cancellationToken).ConfigureAwait(false))!; 67 | } 68 | 69 | /// 70 | public static ValueTask SerializeAsync(this ISerialize serializer, Stream target, T item, CancellationToken cancellationToken = default) 71 | { 72 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 73 | Contract.EndContractBlock(); 74 | 75 | return serializer is ISerializeAsync s 76 | ? s.SerializeAsync(target, item, cancellationToken) 77 | : DefaultMethods.SerializeAsync(serializer, target, item); 78 | } 79 | 80 | /// 81 | public static ValueTask SerializeAsync(this ISerializeObject serializer, Stream target, object? item, Type type, CancellationToken cancellationToken = default) 82 | { 83 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 84 | Contract.EndContractBlock(); 85 | 86 | return serializer is ISerializeObjectAsync s 87 | ? s.SerializeAsync(target, item, type, cancellationToken) 88 | : DefaultMethods.SerializeAsync(serializer, target, item, type); 89 | } 90 | 91 | /// 92 | public static ValueTask SerializeAsync(this ISerializeObject serializer, Stream target, T item, CancellationToken cancellationToken = default) 93 | { 94 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 95 | Contract.EndContractBlock(); 96 | 97 | return serializer is ISerializeAsync s 98 | ? s.SerializeAsync(target, item, cancellationToken) 99 | : SerializeAsync(serializer, target, item, typeof(T), cancellationToken); 100 | } 101 | 102 | /// 103 | public static ValueTask DeserializeAsync(this IDeserialize deserializer, Stream source, CancellationToken cancellationToken = default) 104 | { 105 | if (deserializer is null) throw new ArgumentNullException(nameof(deserializer)); 106 | Contract.EndContractBlock(); 107 | 108 | return deserializer is IDeserializeAsync d 109 | ? d.DeserializeAsync(source, cancellationToken) 110 | : DefaultMethods.DeserializeAsync(deserializer, source); 111 | } 112 | 113 | /// 114 | public static ValueTask DeserializeAsync(this IDeserialize deserializer, Stream source, CancellationToken cancellationToken = default) 115 | { 116 | if (deserializer is null) throw new ArgumentNullException(nameof(deserializer)); 117 | Contract.EndContractBlock(); 118 | 119 | return deserializer is IDeserializeAsync d 120 | ? d.DeserializeAsync(source, cancellationToken) 121 | : DefaultMethods.DeserializeAsync(deserializer, source); 122 | } 123 | 124 | /// 125 | public static ValueTask SerializeAsync(this ISerialize serializer, Stream target, T item, CancellationToken cancellationToken = default) 126 | { 127 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 128 | Contract.EndContractBlock(); 129 | 130 | return serializer is ISerializeAsync s 131 | ? s.SerializeAsync(target, item, cancellationToken) 132 | : DefaultMethods.SerializeAsync(serializer, target, item); 133 | } 134 | 135 | /// 136 | public static ValueTask SerializeAsync(this ISerializeObjectAsync serializer, Stream target, T item, CancellationToken cancellationToken) 137 | { 138 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 139 | Contract.EndContractBlock(); 140 | 141 | return serializer is ISerializeAsync s 142 | ? s.SerializeAsync(target, item, cancellationToken) 143 | : serializer.SerializeAsync(target, item, typeof(T), cancellationToken); 144 | } 145 | 146 | /// 147 | /// Creates a type specific serializer using this as the underlying serializer. 148 | /// 149 | /// A type specific serializer. 150 | public static Serializer Cast(this ISerializer serializer) 151 | { 152 | if (serializer is null) throw new ArgumentNullException(nameof(serializer)); 153 | Contract.EndContractBlock(); 154 | 155 | return serializer is SerializerBase sb 156 | ? sb.Cast() 157 | : serializer is IAsyncSerializer a 158 | ? new Serializer( 159 | serializer.Deserialize, serializer.Serialize, 160 | a.DeserializeAsync, a.SerializeAsync) 161 | : new Serializer( 162 | serializer.Deserialize, serializer.Serialize); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /Open.Serialization/IDeserialize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Open.Serialization; 4 | 5 | /// 6 | /// Interface for deserializing any given generic type. 7 | /// 8 | public interface IDeserialize 9 | { 10 | /// 11 | /// Deserializes a string value to the specified type. 12 | /// 13 | /// The string to deserialize. 14 | /// The deserialized result. 15 | T Deserialize(string value); 16 | } 17 | 18 | /// 19 | /// Interface for deserializing a predefined specific generic type. 20 | /// 21 | public interface IDeserialize 22 | { 23 | /// 24 | /// Deserializes a string value to the specified type. 25 | /// 26 | /// The string to deserialize. 27 | /// The deserialized result. 28 | T Deserialize(string value); 29 | } -------------------------------------------------------------------------------- /Open.Serialization/IDeserializeAsync.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Open.Serialization; 6 | 7 | /// 8 | /// Interface for asynchronously deserializing any given generic type. 9 | /// 10 | public interface IDeserializeAsync 11 | { 12 | /// 13 | /// Deserializes a stream to the specified type. 14 | /// 15 | /// The stream to deserialize. 16 | /// An optional cancellation token. 17 | /// The deserialized result. 18 | ValueTask DeserializeAsync(Stream source, CancellationToken cancellationToken = default); 19 | } 20 | 21 | /// 22 | /// Interface for asynchronously deserializing a predefined specific generic type. 23 | /// 24 | public interface IDeserializeAsync 25 | { 26 | /// 27 | /// Deserializes a stream to the specified type. 28 | /// 29 | /// The stream to deserialize. 30 | /// An optional cancellation token. 31 | /// The deserialized result. 32 | ValueTask DeserializeAsync(Stream source, CancellationToken cancellationToken = default); 33 | } 34 | -------------------------------------------------------------------------------- /Open.Serialization/IDeserializeObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Open.Serialization; 4 | 5 | /// 6 | /// Interface for deserializing any to an when given a type. 7 | /// 8 | public interface IDeserializeObject 9 | { 10 | /// 11 | /// Deserializes a string value to the specified type. 12 | /// 13 | /// The string to deserialize. 14 | /// The expected type. 15 | /// The deserialized result. 16 | object? Deserialize(string value, Type type); 17 | } -------------------------------------------------------------------------------- /Open.Serialization/IDeserializeObjectAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Open.Serialization; 7 | 8 | /// 9 | /// Interface for asynchronously deserializing any string to an object when given a type. 10 | /// 11 | public interface IDeserializeObjectAsync 12 | { 13 | /// 14 | /// Deserializes a stream to the specified type. 15 | /// 16 | /// The stream to deserialize. 17 | /// The expected type. 18 | /// An optional cancellation token. 19 | /// The deserialized result. 20 | ValueTask DeserializeAsync(Stream source, Type type, CancellationToken cancellationToken = default); 21 | } 22 | -------------------------------------------------------------------------------- /Open.Serialization/IObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization; 2 | 3 | /// 4 | /// Interface for defining an object serializer. 5 | /// 6 | public interface IObjectSerializer : ISerializeObject, IDeserializeObject 7 | { 8 | } 9 | 10 | /// 11 | /// Interface for defining an asynchronous object serializer. 12 | /// 13 | public interface IAsyncObjectSerializer : ISerializeObjectAsync, IDeserializeObjectAsync 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /Open.Serialization/ISerialize.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization; 2 | 3 | /// 4 | /// Interface for serializing a given generic type. 5 | /// 6 | public interface ISerialize 7 | { 8 | /// 9 | /// Serializes the provided item to a string. 10 | /// 11 | /// The item to serialze. 12 | /// The serialized string. 13 | string Serialize(T item); 14 | } 15 | 16 | /// 17 | /// Interface for serializing a predefined specific generic type. 18 | /// 19 | public interface ISerialize 20 | { 21 | /// 22 | /// Serializes the provided item to a string. 23 | /// 24 | /// The item to serialze. 25 | /// The serialized string. 26 | string Serialize(T item); 27 | } 28 | -------------------------------------------------------------------------------- /Open.Serialization/ISerializeAsync.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Open.Serialization; 6 | 7 | /// 8 | /// Interface for asynchronously serializing a given generic type. 9 | /// 10 | public interface ISerializeAsync 11 | { 12 | /// 13 | /// Serializes the provided item to a stream. 14 | /// 15 | /// The destination stream. 16 | /// The item to serialize. 17 | /// An optional cancellation token. 18 | ValueTask SerializeAsync(Stream target, T item, CancellationToken cancellationToken = default); 19 | } 20 | 21 | /// 22 | /// Interface for asynchronously serializing a predefined specific generic type. 23 | /// 24 | public interface ISerializeAsync 25 | { 26 | /// 27 | /// Serializes the provided item to a stream. 28 | /// 29 | /// The destination stream. 30 | /// The item to serialize. 31 | /// An optional cancellation token. 32 | ValueTask SerializeAsync(Stream target, T item, CancellationToken cancellationToken = default); 33 | } 34 | -------------------------------------------------------------------------------- /Open.Serialization/ISerializeObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Open.Serialization; 4 | 5 | /// 6 | /// Interface for serializing an object to a string when given a type. 7 | /// 8 | public interface ISerializeObject 9 | { 10 | /// 11 | /// Serializes the provided item to a string. 12 | /// 13 | /// The item to deserialze. 14 | /// The expected type. 15 | /// The serialized string. 16 | string Serialize(object? item, Type type); 17 | } 18 | -------------------------------------------------------------------------------- /Open.Serialization/ISerializeObjectAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Open.Serialization; 7 | 8 | /// 9 | /// Interface for asynchronously serializing an object to a string when given a type. 10 | /// 11 | public interface ISerializeObjectAsync 12 | { 13 | /// 14 | /// Serializes the provided item to a stream. 15 | /// 16 | /// The destination stream. 17 | /// The item to serialize. 18 | /// The expected type. 19 | /// An optional cancellation token. 20 | ValueTask SerializeAsync(Stream target, object? item, Type type, CancellationToken cancellationToken = default); 21 | } 22 | -------------------------------------------------------------------------------- /Open.Serialization/ISerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Open.Serialization; 2 | 3 | /// 4 | /// Interface for serializing any object. 5 | /// 6 | public interface ISerializer : ISerialize, IDeserialize 7 | { 8 | } 9 | 10 | /// 11 | /// Interface for serializing a specific type. 12 | /// 13 | /// The type to serialize 14 | public interface ISerializer : ISerialize, IDeserialize 15 | { 16 | } 17 | 18 | /// 19 | /// Interface for serializing any object asyncronously. 20 | /// 21 | public interface IAsyncSerializer : ISerializeAsync, IDeserializeAsync 22 | { 23 | } 24 | 25 | /// 26 | /// Interface for serializing a specific type asyncronously. 27 | /// 28 | /// The type to serialize 29 | public interface IAsyncSerializer : ISerializeAsync, IDeserializeAsync 30 | { 31 | } 32 | -------------------------------------------------------------------------------- /Open.Serialization/ObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Contracts; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Open.Serialization; 8 | 9 | /// 10 | /// A class for serializing and deserializing objects. 11 | /// 12 | public abstract class ObjectSerializer : ObjectSerializerBase 13 | { 14 | private readonly Func? _deserializer; 15 | private readonly Func? _serializer; 16 | private readonly Func> _deserializerAsync; 17 | private readonly Func _serializerAsync; 18 | 19 | /// 20 | /// Constructs a serializer/deserializer using the provided serialization functions. 21 | /// 22 | protected ObjectSerializer( 23 | Func? deserializer, 24 | Func? serializer = null, 25 | Func>? deserializerAsync = null, 26 | Func? serializerAsync = null) 27 | { 28 | if (deserializer is null && serializer is null && deserializerAsync is null && serializerAsync is null) 29 | throw new ArgumentNullException(nameof(deserializer), "At least one of the serialization or deserialization functions must not be null."); 30 | Contract.EndContractBlock(); 31 | 32 | _deserializer = deserializer; 33 | _serializer = serializer; 34 | _deserializerAsync = deserializerAsync ?? base.DeserializeAsync; 35 | _serializerAsync = serializerAsync ?? base.SerializeAsync; 36 | } 37 | 38 | /// 39 | public override object? Deserialize(string value, Type type) 40 | => _deserializer is null 41 | ? throw new NullReferenceException("No deserializer function was supplied.") 42 | : _deserializer(value, type); 43 | 44 | /// 45 | public override ValueTask DeserializeAsync(Stream stream, Type type, CancellationToken cancellationToken = default) 46 | => _deserializerAsync(stream, type, cancellationToken); 47 | 48 | /// 49 | public override string Serialize(object? item, Type type) 50 | => _serializer is null 51 | ? throw new NullReferenceException("No serializer function was supplied.") 52 | : _serializer(item, type); 53 | 54 | /// 55 | public override ValueTask SerializeAsync(Stream stream, object? item, Type type, CancellationToken cancellationToken = default) 56 | => _serializerAsync(stream, item, type, cancellationToken); 57 | } 58 | -------------------------------------------------------------------------------- /Open.Serialization/ObjectSerializerBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Open.Serialization; 7 | 8 | /// 9 | /// Base class for serializing and deserializing non-generic objects. 10 | /// 11 | public abstract class ObjectSerializerBase : SerializerBase, IObjectSerializer, IAsyncObjectSerializer 12 | { 13 | /// 14 | public abstract string Serialize(object? item, Type type); 15 | 16 | /// 17 | public abstract object? Deserialize(string value, Type type); 18 | 19 | /// 20 | public virtual ValueTask SerializeAsync(Stream target, object? item, Type type, CancellationToken cancellationToken = default) 21 | => DefaultMethods.SerializeAsync(this, target, item, type); 22 | 23 | /// 24 | public virtual ValueTask DeserializeAsync(Stream source, Type type, CancellationToken cancellationToken = default) 25 | => DefaultMethods.DeserializeAsync(this, source, type); 26 | 27 | /// 28 | public override string Serialize(T item) 29 | => Serialize(item, typeof(T)); 30 | 31 | /// 32 | public override T Deserialize(string value) 33 | => (T)Deserialize(value, typeof(T))!; 34 | 35 | /// 36 | public override ValueTask SerializeAsync(Stream target, T item, CancellationToken cancellationToken = default) 37 | => SerializeAsync(target, item, typeof(T), cancellationToken); 38 | 39 | /// 40 | public override async ValueTask DeserializeAsync(Stream source, CancellationToken cancellationToken = default) 41 | => (T)(await DeserializeAsync(source, typeof(T), cancellationToken).ConfigureAwait(false))!; 42 | } 43 | -------------------------------------------------------------------------------- /Open.Serialization/Open.Serialization.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0; netstandard2.1 5 | true 6 | latest 7 | enable 8 | true 9 | electricessence 10 | 11 | DI/IoC agnostic interfaces for injecting any serialization library. 12 | 13 | Part of the "Open" set of libraries. 14 | 15 | serialization 16 | © electricessence (Oren F.) All rights reserved. 17 | MIT 18 | https://github.com/Open-NET-Libraries/Open.Serialization 19 | https://github.com/Open-NET-Libraries/Open.Serialization 20 | git 21 | 3.0.0 22 | 23 | MIT 24 | true 25 | true 26 | snupkg 27 | logo.png 28 | true 29 | README.md 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | True 39 | 40 | 41 | 42 | True 43 | \ 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Open.Serialization/Open.Serialization.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Open.Serialization 5 | 6 | 7 | 8 | 9 | Default utiltiy methods for use with implementing interfaces/classes. 10 | 11 | 12 | 13 | 14 | Deserializes a stream to the specified type. 15 | 16 | 17 | 18 | 19 | Serializes the provided item to a stream. 20 | 21 | 22 | 23 | 24 | Deserializes a stream to the specified type. 25 | 26 | 27 | 28 | 29 | Serializes the provided item to a stream. 30 | 31 | 32 | 33 | 34 | Deserializes a stream to the specified type. 35 | 36 | 37 | 38 | 39 | Serializes the provided item to a stream. 40 | 41 | 42 | 43 | 44 | Default extensions for pure serialization interfaces. 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | Creates a type specific serializer using this as the underlying serializer. 86 | 87 | A type specific serializer. 88 | 89 | 90 | 91 | Interface for deserializing any given generic type. 92 | 93 | 94 | 95 | 96 | Deserializes a string value to the specified type. 97 | 98 | The string to deserialize. 99 | The deserialized result. 100 | 101 | 102 | 103 | Interface for deserializing a predefined specific generic type. 104 | 105 | 106 | 107 | 108 | Deserializes a string value to the specified type. 109 | 110 | The string to deserialize. 111 | The deserialized result. 112 | 113 | 114 | 115 | Interface for asynchronously deserializing any given generic type. 116 | 117 | 118 | 119 | 120 | Deserializes a stream to the specified type. 121 | 122 | The stream to deserialize. 123 | An optional cancellation token. 124 | The deserialized result. 125 | 126 | 127 | 128 | Interface for asynchronously deserializing a predefined specific generic type. 129 | 130 | 131 | 132 | 133 | Deserializes a stream to the specified type. 134 | 135 | The stream to deserialize. 136 | An optional cancellation token. 137 | The deserialized result. 138 | 139 | 140 | 141 | Interface for deserializing any to an when given a type. 142 | 143 | 144 | 145 | 146 | Deserializes a string value to the specified type. 147 | 148 | The string to deserialize. 149 | The expected type. 150 | The deserialized result. 151 | 152 | 153 | 154 | Interface for asynchronously deserializing any string to an object when given a type. 155 | 156 | 157 | 158 | 159 | Deserializes a stream to the specified type. 160 | 161 | The stream to deserialize. 162 | The expected type. 163 | An optional cancellation token. 164 | The deserialized result. 165 | 166 | 167 | 168 | Interface for defining an object serializer. 169 | 170 | 171 | 172 | 173 | Interface for defining an asynchronous object serializer. 174 | 175 | 176 | 177 | 178 | Interface for serializing a given generic type. 179 | 180 | 181 | 182 | 183 | Serializes the provided item to a string. 184 | 185 | The item to serialze. 186 | The serialized string. 187 | 188 | 189 | 190 | Interface for serializing a predefined specific generic type. 191 | 192 | 193 | 194 | 195 | Serializes the provided item to a string. 196 | 197 | The item to serialze. 198 | The serialized string. 199 | 200 | 201 | 202 | Interface for asynchronously serializing a given generic type. 203 | 204 | 205 | 206 | 207 | Serializes the provided item to a stream. 208 | 209 | The destination stream. 210 | The item to serialize. 211 | An optional cancellation token. 212 | 213 | 214 | 215 | Interface for asynchronously serializing a predefined specific generic type. 216 | 217 | 218 | 219 | 220 | Serializes the provided item to a stream. 221 | 222 | The destination stream. 223 | The item to serialize. 224 | An optional cancellation token. 225 | 226 | 227 | 228 | Interface for serializing an object to a string when given a type. 229 | 230 | 231 | 232 | 233 | Serializes the provided item to a string. 234 | 235 | The item to deserialze. 236 | The expected type. 237 | The serialized string. 238 | 239 | 240 | 241 | Interface for asynchronously serializing an object to a string when given a type. 242 | 243 | 244 | 245 | 246 | Serializes the provided item to a stream. 247 | 248 | The destination stream. 249 | The item to serialize. 250 | The expected type. 251 | An optional cancellation token. 252 | 253 | 254 | 255 | Interface for serializing any object. 256 | 257 | 258 | 259 | 260 | Interface for serializing a specific type. 261 | 262 | The type to serialize 263 | 264 | 265 | 266 | Interface for serializing any object asyncronously. 267 | 268 | 269 | 270 | 271 | Interface for serializing a specific type asyncronously. 272 | 273 | The type to serialize 274 | 275 | 276 | 277 | A class for serializing and deserializing objects. 278 | 279 | 280 | 281 | 282 | Constructs a serializer/deserializer using the provided serialization functions. 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | Base class for serializing and deserializing non-generic objects. 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | A class for serializing and deserializing a predefined generic type. 329 | 330 | 331 | 332 | 333 | Constructs a serializer/deserializer using the provided serialization functions. 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | Base class for implementing a serializer/deserializer. 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | Creates a type specific serializer using this as the underlying serializer. 368 | 369 | A type specific serializer. 370 | 371 | 372 | 373 | Base calss for implementing a spedific generic type serializer/deserializer. 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | -------------------------------------------------------------------------------- /Open.Serialization/Serializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Contracts; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Open.Serialization; 8 | 9 | /// 10 | /// A class for serializing and deserializing a predefined generic type. 11 | /// 12 | public class Serializer : SerializerBase 13 | { 14 | private readonly Func? _deserializer; 15 | private readonly Func? _serializer; 16 | private readonly Func> _deserializerAsync; 17 | private readonly Func _serializerAsync; 18 | 19 | /// 20 | /// Constructs a serializer/deserializer using the provided serialization functions. 21 | /// 22 | public Serializer( 23 | Func? deserializer, 24 | Func? serializer = null, 25 | Func>? deserializerAsync = null, 26 | Func? serializerAsync = null) 27 | { 28 | if (deserializer is null && serializer is null && deserializerAsync is null && serializerAsync is null) 29 | throw new ArgumentNullException(nameof(deserializer), "At least one of the serialization or deserialization functions must not be null."); 30 | Contract.EndContractBlock(); 31 | 32 | _deserializer = deserializer; 33 | _serializer = serializer; 34 | _deserializerAsync = deserializerAsync ?? base.DeserializeAsync; 35 | _serializerAsync = serializerAsync ?? base.SerializeAsync; 36 | } 37 | 38 | /// 39 | public override T Deserialize(string value) 40 | => _deserializer is null 41 | ? throw new NullReferenceException("No deserializer function was supplied.") 42 | : _deserializer(value); 43 | 44 | /// 45 | public override ValueTask DeserializeAsync(Stream source, CancellationToken cancellationToken = default) 46 | => _deserializerAsync(source, cancellationToken); 47 | 48 | /// 49 | public override string Serialize(T item) 50 | => _serializer is null 51 | ? throw new NullReferenceException("No serializer function was supplied.") 52 | : _serializer(item); 53 | 54 | /// 55 | public override ValueTask SerializeAsync(Stream target, T item, CancellationToken cancellationToken = default) 56 | => _serializerAsync(target, item, cancellationToken); 57 | } 58 | -------------------------------------------------------------------------------- /Open.Serialization/SerializerBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Open.Serialization; 8 | 9 | /// 10 | /// Base class for implementing a serializer/deserializer. 11 | /// 12 | public abstract class SerializerBase : ISerializer, IAsyncSerializer 13 | { 14 | /// 15 | public abstract T Deserialize(string value); 16 | 17 | /// 18 | public virtual ValueTask DeserializeAsync(Stream source, CancellationToken cancellationToken = default) 19 | => DefaultMethods.DeserializeAsync(this, source); 20 | 21 | /// 22 | public abstract string Serialize(T item); 23 | 24 | /// 25 | public virtual ValueTask SerializeAsync(Stream target, T item, CancellationToken cancellationToken = default) 26 | => DefaultMethods.SerializeAsync(this, target, item); 27 | 28 | /// 29 | /// Creates a type specific serializer using this as the underlying serializer. 30 | /// 31 | /// A type specific serializer. 32 | public virtual Serializer Cast() 33 | => new(Deserialize, Serialize, DeserializeAsync, SerializeAsync); 34 | } 35 | 36 | /// 37 | /// Base calss for implementing a spedific generic type serializer/deserializer. 38 | /// 39 | public abstract class SerializerBase : ISerializer, IAsyncSerializer 40 | { 41 | /// 42 | public abstract T Deserialize(string value); 43 | 44 | /// 45 | public virtual T Deserialize(ReadOnlySpan value) 46 | => Deserialize(value.ToString()); 47 | 48 | /// 49 | public virtual ValueTask DeserializeAsync(Stream source, CancellationToken cancellationToken = default) 50 | => DefaultMethods.DeserializeAsync(this, source); 51 | 52 | /// 53 | public abstract string Serialize(T item); 54 | 55 | /// 56 | public virtual ValueTask SerializeAsync(Stream target, T item, CancellationToken cancellationToken = default) 57 | => DefaultMethods.SerializeAsync(this, target, item); 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open.Serialization 2 | 3 | DI/IoC agnostic interfaces for injecting any serialization library. 4 | 5 | ## Implementation 6 | 7 | With the following libraries, you can build other libraries that sever their dependency from any serializer and allow you to inject whichever you want. You can also easily integrate another serializer and still retain the DI/IoC happiness. 8 | 9 | ### Core Interfaces & Extensions 10 | 11 | [https://www.nuget.org/packages/Open.Serialization](https://www.nuget.org/packages/Open.Serialization/) 12 | [![NuGet](https://img.shields.io/nuget/v/Open.Serialization.svg)](https://www.nuget.org/packages/Open.Serialization/) Core package for serializing anything. 13 | 14 | [https://www.nuget.org/packages/Open.Serialization.Json](https://www.nuget.org/packages/Open.Serialization.Json/) 15 | [![NuGet](https://img.shields.io/nuget/v/Open.Serialization.Json.svg)](https://www.nuget.org/packages/Open.Serialization.Json/) Core package specific to JSON. 16 | 17 | ### Library/Vendor Specific Implementations 18 | 19 | ```cs 20 | services.AddJsonSerializer(); 21 | ``` 22 | 23 | The following libs contain support for `Microsoft.Extensions.DependencyInjection`. 24 | Import any of these and you can use the above extension to inject default serializers. 25 | 26 | [https://www.nuget.org/packages/Open.Serialization.Json.Newtonsoft](https://www.nuget.org/packages/Open.Serialization.Json.Newtonsoft/) 27 | [![NuGet](https://img.shields.io/nuget/v/Open.Serialization.Json.Newtonsoft.svg)](https://www.nuget.org/packages/Open.Serialization.Json.Newtonsoft/) Extensions and DI for **Newtonsoft.Json**. 28 | 29 | [https://www.nuget.org/packages/Open.Serialization.Json.System](https://www.nuget.org/packages/Open.Serialization.Json.System/) 30 | [![NuGet](https://img.shields.io/nuget/v/Open.Serialization.Json.System.svg)](https://www.nuget.org/packages/Open.Serialization.Json.System/) Extensions and DI for **System.Text.Json**. *Note: There is no `IJsonObjectSerializer` option for `System.Text.Json`.* 31 | 32 | [https://www.nuget.org/packages/Open.Serialization.Json.Utf8Json](https://www.nuget.org/packages/Open.Serialization.Json.Utf8Json/) 33 | [![NuGet](https://img.shields.io/nuget/v/Open.Serialization.Json.Utf8Json.svg)](https://www.nuget.org/packages/Open.Serialization.Json.Utf8Json/) Extensions and DI for **Utf8Json**. 34 | 35 | ## Interface & Methods Exposed 36 | 37 | ```cs 38 | T IDeserialize.Deserialize(string value); 39 | T IDeserialize.Deserialize(string value); 40 | object IDeserializeObject.Deserialize(string value, Type type); 41 | 42 | ValueTask IDeserializeAsync.DeserializeAsync(Stream source, CancellationToken cancellationToken = default); 43 | ValueTask IDeserializeAsync.DeserializeAsync(Stream source, CancellationToken cancellationToken = default); 44 | ValueTask IDeserializeObjectAsync.DeserializeAsync(Stream source, Type type, CancellationToken cancellationToken = default); 45 | 46 | string ISerialize.Serialize(T item); 47 | string ISerialize.Serialize(T item); 48 | string ISerializeObject.Serialize(object item, Type type); 49 | 50 | ValueTask ISerializeAsync.SerializeAsync(Stream target, T item, CancellationToken cancellationToken = default); 51 | ValueTask ISerializeAsync.SerializeAsync(Stream target, T item, CancellationToken cancellationToken = default); 52 | ValueTask ISerializeObjectAsync.SerializeAsync(Stream target, object item, Type type, CancellationToken cancellationToken = default); 53 | ``` 54 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Open-NET-Libraries/Open.Serialization/0b08141467d15898eea779d3d44d442676bb89ed/logo.png --------------------------------------------------------------------------------