Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: Open-NET-Libraries/Open.Serialization
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.2.4
Choose a base ref
...
head repository: Open-NET-Libraries/Open.Serialization
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Feb 6, 2020

  1. Fix issue with BigInteger and potentially other numerical values.

    Oren (electricessence) committed Feb 6, 2020
    Copy the full SHA
    2b03ab9 View commit details

Commits on Apr 7, 2020

  1. Update README.md

    electricessence authored Apr 7, 2020
    Copy the full SHA
    050ed0f View commit details

Commits on May 5, 2020

  1. Copy the full SHA
    ea6f8fd View commit details

Commits on Jul 17, 2020

  1. Updated packaging.

    Prep for release of Open.Serialization.
    Oren (electricessence) committed Jul 17, 2020
    Copy the full SHA
    71aebc8 View commit details
  2. Update README.md

    Oren (electricessence) committed Jul 17, 2020
    Copy the full SHA
    d101aee View commit details
  3. Updated references.

    Oren (electricessence) committed Jul 17, 2020
    Copy the full SHA
    da57144 View commit details
  4. Updated fxcop.

    Oren (electricessence) committed Jul 17, 2020
    Copy the full SHA
    b55fbaf View commit details
  5. Updated version.

    Oren (electricessence) committed Jul 17, 2020
    Copy the full SHA
    0b434bf View commit details
  6. Updated references.

    Oren (electricessence) committed Jul 17, 2020
    Copy the full SHA
    33aaed0 View commit details

Commits on Sep 21, 2021

  1. Copy the full SHA
    0f7d1a9 View commit details

Commits on Sep 25, 2021

  1. Merge pull request #2 from mehyaa/patch-1

    JsonSerializerOptions.Clone fixed
    electricessence authored Sep 25, 2021
    Copy the full SHA
    0291751 View commit details
  2. Updates.

    electricessence committed Sep 25, 2021
    Copy the full SHA
    dae9501 View commit details

Commits on Sep 28, 2021

  1. Copy the full SHA
    6b054af View commit details

Commits on May 17, 2022

  1. Update SerializationsExtensions.cs

    Added missing properties in the Clone method
    tstrausbaugh-dev committed May 17, 2022
    Copy the full SHA
    edd47f1 View commit details

Commits on May 19, 2022

  1. Merge pull request #4 from tstrausbaugh-dev/master

    Update SerializationsExtensions.cs
    electricessence authored May 19, 2022
    Copy the full SHA
    fd64c1d View commit details
  2. Updated patch.

    electricessence committed May 19, 2022
    Copy the full SHA
    5e0cf08 View commit details

Commits on May 21, 2022

  1. Phase 1 code cleanup.

    electricessence committed May 21, 2022
    Copy the full SHA
    3c7f5b0 View commit details
  2. Copy the full SHA
    fc9822c View commit details
  3. Copy the full SHA
    0eb3781 View commit details
  4. Copy the full SHA
    8393a3d View commit details
  5. V3 prep.

    electricessence committed May 21, 2022
    Copy the full SHA
    57cc7f3 View commit details
  6. Added ReadOnlySpan option for System.Text.Json. Standardized serializ…

    …ation to always return non-null string.
    electricessence committed May 21, 2022
    Copy the full SHA
    fc11ef6 View commit details

Commits on May 22, 2022

  1. V3 Ready.

    electricessence committed May 22, 2022
    Copy the full SHA
    cf8e882 View commit details

Commits on May 23, 2022

  1. Cleanup for V3

    electricessence committed May 23, 2022
    Copy the full SHA
    e5ae9f9 View commit details
  2. Docs cleanup.

    electricessence committed May 23, 2022
    Copy the full SHA
    0fa20b6 View commit details

Commits on Oct 14, 2024

  1. Bump System.Text.Json in /Open.Serialization.Json.System

    Bumps [System.Text.Json](https://github.com/dotnet/runtime) from 6.0.4 to 6.0.10.
    - [Release notes](https://github.com/dotnet/runtime/releases)
    - [Commits](dotnet/runtime@v6.0.4...v6.0.10)
    
    ---
    updated-dependencies:
    - dependency-name: System.Text.Json
      dependency-type: direct:production
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored Oct 14, 2024
    Copy the full SHA
    741196f View commit details
  2. Merge pull request #8 from Open-NET-Libraries/dependabot/nuget/Open.S…

    …erialization.Json.System/System.Text.Json-6.0.10
    
    Bump System.Text.Json from 6.0.4 to 6.0.10 in /Open.Serialization.Json.System
    electricessence authored Oct 14, 2024
    Copy the full SHA
    0b08141 View commit details
Showing with 2,677 additions and 2,200 deletions.
  1. +6 −6 LICENSE
  2. +3 −0 Open.Serialization.Json.Newtonsoft/.editorconfig
  3. +15 −16 Open.Serialization.Json.Newtonsoft/CamelCaseJson.cs
  4. +49 −29 Open.Serialization.Json.Newtonsoft/Converters/JsonDecimalConverter.cs
  5. +28 −15 Open.Serialization.Json.Newtonsoft/Converters/JsonDecimalRoundingConverter.cs
  6. +48 −24 Open.Serialization.Json.Newtonsoft/Converters/JsonDoubleRoundingConverter.cs
  7. +26 −25 Open.Serialization.Json.Newtonsoft/Converters/JsonNullableDecimalConverter.cs
  8. +32 −21 Open.Serialization.Json.Newtonsoft/Converters/JsonNullableDecimalRoundingConverter.cs
  9. +24 −26 Open.Serialization.Json.Newtonsoft/Converters/JsonNullableDoubleConverter.cs
  10. +20 −21 Open.Serialization.Json.Newtonsoft/Converters/JsonNullableDoubleRoundingConverter.cs
  11. +40 −11 Open.Serialization.Json.Newtonsoft/Converters/JsonValueConverterBase.cs
  12. +9 −0 Open.Serialization.Json.Newtonsoft/GlobalSuppressions.cs
  13. +63 −58 Open.Serialization.Json.Newtonsoft/JsonSerializerFactory.cs
  14. +17 −19 Open.Serialization.Json.Newtonsoft/JsonSerializerInternal.cs
  15. +32 −13 Open.Serialization.Json.Newtonsoft/Open.Serialization.Json.Newtonsoft.csproj
  16. +33 −23 Open.Serialization.Json.Newtonsoft/RelaxedJson.cs
  17. +184 −176 Open.Serialization.Json.Newtonsoft/SerializationExtensions.cs
  18. +19 −15 Open.Serialization.Json.System/CamelCaseJson.cs
  19. +16 −12 Open.Serialization.Json.System/CaseSensitiveJson.cs
  20. +28 −18 Open.Serialization.Json.System/Converters/JsonDecimalConverter.cs
  21. +28 −15 Open.Serialization.Json.System/Converters/JsonDecimalRoundingConverter.cs
  22. +31 −18 Open.Serialization.Json.System/Converters/JsonDoubleRoundingConverter.cs
  23. +35 −24 Open.Serialization.Json.System/Converters/JsonNullableDecimalConverter.cs
  24. +33 −23 Open.Serialization.Json.System/Converters/JsonNullableDecimalRoundingConverter.cs
  25. +35 −24 Open.Serialization.Json.System/Converters/JsonNullableDoubleConverter.cs
  26. +33 −23 Open.Serialization.Json.System/Converters/JsonNullableDoubleRoundingConverter.cs
  27. +8 −6 Open.Serialization.Json.System/Converters/JsonValueConverterBase.cs
  28. +55 −41 Open.Serialization.Json.System/JsonSerializerFactory.cs
  29. +17 −19 Open.Serialization.Json.System/JsonSerializerInternal.cs
  30. +39 −20 Open.Serialization.Json.System/Open.Serialization.Json.System.csproj
  31. +50 −27 Open.Serialization.Json.System/RelaxedJson.cs
  32. +266 −175 Open.Serialization.Json.System/SerializationsExtensions.cs
  33. +66 −48 Open.Serialization.Json.Utf8Json/JsonSerializerFactory.cs
  34. +47 −49 Open.Serialization.Json.Utf8Json/JsonSerializerInternal.cs
  35. +34 −15 Open.Serialization.Json.Utf8Json/Open.Serialization.Json.Utf8Json.csproj
  36. +83 −55 Open.Serialization.Json.Utf8Json/SerializationExtensions.cs
  37. +4 −5 Open.Serialization.Json/IJsonAsyncObjectSerializer.cs
  38. +8 −9 Open.Serialization.Json/IJsonAsyncSerializer.cs
  39. +8 −9 Open.Serialization.Json/IJsonDeserialize.cs
  40. +8 −9 Open.Serialization.Json/IJsonDeserializeAsync.cs
  41. +4 −5 Open.Serialization.Json/IJsonDeserializeObject.cs
  42. +4 −5 Open.Serialization.Json/IJsonDeserializeObjectAsync.cs
  43. +20 −21 Open.Serialization.Json/IJsonObjectSerializationFactory.cs
  44. +6 −7 Open.Serialization.Json/IJsonObjectSerializer.cs
  45. +8 −9 Open.Serialization.Json/IJsonObjectSerializerFactory.cs
  46. +20 −21 Open.Serialization.Json/IJsonSerializationFactory.cs
  47. +20 −21 Open.Serialization.Json/IJsonSerializationOptions.cs
  48. +8 −9 Open.Serialization.Json/IJsonSerialize.cs
  49. +8 −9 Open.Serialization.Json/IJsonSerializeAsync.cs
  50. +4 −5 Open.Serialization.Json/IJsonSerializeObject.cs
  51. +4 −5 Open.Serialization.Json/IJsonSerializeObjectAsync.cs
  52. +10 −11 Open.Serialization.Json/IJsonSerializer.cs
  53. +8 −9 Open.Serialization.Json/IJsonSerializerFactory.cs
  54. +10 −11 Open.Serialization.Json/JsonObjectSerializer.cs
  55. +4 −5 Open.Serialization.Json/JsonObjectSerializerBase.cs
  56. +11 −12 Open.Serialization.Json/JsonSerializationOptions.cs
  57. +10 −11 Open.Serialization.Json/JsonSerializer.cs
  58. +11 −12 Open.Serialization.Json/JsonSerializerBase.cs
  59. +30 −15 Open.Serialization.Json/Open.Serialization.Json.csproj
  60. +36 −38 Open.Serialization.Tests/DefaultImplementationTests.cs
  61. +106 −100 Open.Serialization.Tests/Newtonsoft/JsonExtensionTests.cs
  62. +6 −4 Open.Serialization.Tests/Open.Serialization.Tests.csproj
  63. +49 −50 Open.Serialization.Tests/ParityTests.cs
  64. +21 −22 Open.Serialization.Tests/SampleModel.cs
  65. +57 −47 Open.Serialization.Tests/System/JsonExtensionTests.cs
  66. +75 −77 Open.Serialization/DefaultMethods.cs
  67. +153 −131 Open.Serialization/Extensions/SerializationExtensions.cs
  68. +23 −22 Open.Serialization/IDeserialize.cs
  69. +22 −23 Open.Serialization/IDeserializeAsync.cs
  70. +12 −13 Open.Serialization/IDeserializeObject.cs
  71. +12 −13 Open.Serialization/IDeserializeObjectAsync.cs
  72. +12 −13 Open.Serialization/IObjectSerializer.cs
  73. +20 −22 Open.Serialization/ISerialize.cs
  74. +22 −23 Open.Serialization/ISerializeAsync.cs
  75. +11 −12 Open.Serialization/ISerializeObject.cs
  76. +12 −13 Open.Serialization/ISerializeObjectAsync.cs
  77. +27 −27 Open.Serialization/ISerializer.cs
  78. +45 −47 Open.Serialization/ObjectSerializer.cs
  79. +35 −36 Open.Serialization/ObjectSerializerBase.cs
  80. +33 −21 Open.Serialization/Open.Serialization.csproj
  81. +7 −4 Open.Serialization/Open.Serialization.xml
  82. +45 −47 Open.Serialization/Serializer.cs
  83. +50 −45 Open.Serialization/SerializerBase.cs
  84. +6 −5 README.md
  85. BIN logo.png
12 changes: 6 additions & 6 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License
The MIT License (MIT)

Copyright (c) 2019 electricessence
Copyright (c) 2020 electricessence (Oren F.) All rights reserved

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
3 changes: 3 additions & 0 deletions Open.Serialization.Json.Newtonsoft/.editorconfig
Original file line number Diff line number Diff line change
@@ -11,3 +11,6 @@ dotnet_diagnostic.CA1305.severity = silent

# CA1307: Specify StringComparison
dotnet_diagnostic.CA1307.severity = suggestion

# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = silent
31 changes: 15 additions & 16 deletions Open.Serialization.Json.Newtonsoft/CamelCaseJson.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace Open.Serialization.Json.Newtonsoft
namespace Open.Serialization.Json.Newtonsoft;

public static class CamelCaseJson
{
public static class CamelCaseJson
public static JsonSerializerSettings Default(bool indent = false)
{
public static JsonSerializerSettings Default(bool indent = false)
var options = RelaxedJson.Options(indent);
options.ContractResolver = new CamelCasePropertyNamesContractResolver()
{
var options = RelaxedJson.Options(indent);
options.ContractResolver = new CamelCasePropertyNamesContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false }
};
return options;
}
NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false }
};
return options;
}

public static JsonSerializerSettings Minimal(bool indent = false)
{
var options = Default(indent);
options.NullValueHandling = NullValueHandling.Ignore;
return options;
}
public static JsonSerializerSettings Minimal(bool indent = false)
{
var options = Default(indent);
options.NullValueHandling = NullValueHandling.Ignore;
return options;
}
}
Original file line number Diff line number Diff line change
@@ -2,40 +2,60 @@
using System;
using System.Diagnostics.Contracts;

namespace Open.Serialization.Json.Newtonsoft.Converters
namespace Open.Serialization.Json.Newtonsoft.Converters;

/// <summary>
/// A converter for ensuring proper standardized processing of decimals.
/// </summary>
public class JsonDecimalConverter : JsonValueConverterBase<decimal>
{
public class JsonDecimalConverter : JsonValueConverterBase<decimal>
/// <summary>
/// Constructs a <see cref="JsonDecimalConverter"/>.
/// </summary>
protected JsonDecimalConverter()
{
protected JsonDecimalConverter()
{
// Prevent unnecessary replication.
}

public static readonly JsonDecimalConverter Instance
= new JsonDecimalConverter();

public static string? Normalize(decimal? value)
=> Normalize(value?.ToString());

public static string? Normalize(string? decimalString)
=> decimalString == null || decimalString.IndexOf('.') == -1
? decimalString
: decimalString?.TrimEnd('0').TrimEnd('.');
// Prevent unnecessary replication.
}

public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader is null) throw new ArgumentNullException(nameof(reader));
Contract.EndContractBlock();
/// <summary>
/// Shared instance of this converter.
/// </summary>
public static readonly JsonDecimalConverter Instance
= new();

/// <inheritdoc cref="Normalize(string?)"/>
#if NETSTANDARD2_1
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("value")]
#endif
public static string? Normalize(decimal? value)
=> Normalize(value?.ToString());

/// <summary>
/// Normalizes a decimal by removing any unnecessary trailing zeros or decimal.
/// </summary>
#if NETSTANDARD2_1
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("decimalString")]
#endif
public static string? Normalize(string? decimalString)
=> string.IsNullOrEmpty(decimalString) || decimalString!.IndexOf('.') == -1
? decimalString
: decimalString.AsSpan().TrimEnd('0').TrimEnd('.').ToString();

/// <inheritdoc />
public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader is null) throw new ArgumentNullException(nameof(reader));
Contract.EndContractBlock();

return Convert.ToDecimal(reader.Value);
}
return ConvertToDecimal(reader.Value!);
}

public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();

writer.WriteRawValue(Normalize(value));
}
writer.WriteRawValue(Normalize(value));
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
using Newtonsoft.Json;
using System;

namespace Open.Serialization.Json.Newtonsoft.Converters
namespace Open.Serialization.Json.Newtonsoft.Converters;

/// <summary>
/// Converter for decimals that rounds to a maximum number of digits after the decimal.
/// </summary>
public class JsonDecimalRoundingConverter : JsonDecimalConverter
{
public class JsonDecimalRoundingConverter : JsonDecimalConverter
/// <summary>
/// The maximum number of digits after the decimal.
/// </summary>
public int Maximum { get; }

/// <summary>
/// Constructs a <see cref="JsonDecimalRoundingConverter"/>.
/// </summary>
/// <param name="maximum">The maximum number of digits after the decimal.</param>
/// <exception cref="ArgumentOutOfRangeException">If the maximum is less than zero.</exception>
public JsonDecimalRoundingConverter(int maximum)
{
public int Maximum { get; }
public JsonDecimalRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}

public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
=> Math.Round(base.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer), Maximum);
/// <inheritdoc />
public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
=> Math.Round(base.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer), Maximum);

public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
=> base.WriteJson(writer, Math.Round(value, Maximum), serializer);
}
}
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
=> base.WriteJson(writer, Math.Round(value, Maximum), serializer);
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,59 @@
using Newtonsoft.Json;
using System;
using System.Diagnostics.Contracts;
using System.Numerics;

namespace Open.Serialization.Json.Newtonsoft.Converters
namespace Open.Serialization.Json.Newtonsoft.Converters;

/// <summary>
/// Converter for doubles that rounds to a maximum number of digits after the decimal.
/// </summary>
public class JsonDoubleRoundingConverter : JsonValueConverterBase<double>
{
public class JsonDoubleRoundingConverter : JsonValueConverterBase<double>
{
public int Maximum { get; }
public JsonDoubleRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}
/// <summary>
/// The maximum number of digits after the decimal.
/// </summary>
public int Maximum { get; }

public override double ReadJson(JsonReader reader, Type objectType, double existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader is null) throw new ArgumentNullException(nameof(reader));
Contract.EndContractBlock();
/// <summary>
/// Constructs a <see cref="JsonDoubleRoundingConverter"/>.
/// </summary>
/// <param name="maximum">The maximum number of digits after the decimal.</param>
/// <exception cref="ArgumentOutOfRangeException">If the maximum is less than zero.</exception>
public JsonDoubleRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}

return reader.Value is decimal d
? Convert.ToDouble(Math.Round(d, Maximum))
: Math.Round(Convert.ToDouble(reader.Value), Maximum);
}
public override double ReadJson(JsonReader reader, Type objectType, double existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader is null) throw new ArgumentNullException(nameof(reader));
Contract.EndContractBlock();

public override void WriteJson(JsonWriter writer, double value, JsonSerializer serializer)
return reader.Value switch
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();
double d => Math.Round(d, Maximum),
decimal d => ConvertToDouble(Math.Round(d, Maximum)),
sbyte i => i,
byte i => i,
short i => i,
ushort i => i,
int i => i,
uint i => i,
long i => i,
ulong i => i,
BigInteger i => (double)i,
_ => Math.Round(ConvertToDouble(reader.Value!), Maximum),
};
}

public override void WriteJson(JsonWriter writer, double value, JsonSerializer serializer)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();

writer.WriteValue(Math.Round(value, Maximum));
}
writer.WriteValue(Math.Round(value, Maximum));
}
}
}
Original file line number Diff line number Diff line change
@@ -2,37 +2,38 @@
using System;
using System.Diagnostics.Contracts;

namespace Open.Serialization.Json.Newtonsoft.Converters
namespace Open.Serialization.Json.Newtonsoft.Converters;

public class JsonNullableDecimalConverter : JsonValueConverterBase<decimal?>
{
public class JsonNullableDecimalConverter : JsonValueConverterBase<decimal?>
protected JsonNullableDecimalConverter()
{
protected JsonNullableDecimalConverter()
{
// Prevent unnecessary replication.
}

public static readonly JsonNullableDecimalConverter Instance
= new JsonNullableDecimalConverter();
// Prevent unnecessary replication.
}

public override decimal? ReadJson(JsonReader reader, Type objectType, decimal? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader is null) throw new ArgumentNullException(nameof(reader));
Contract.EndContractBlock();
public static readonly JsonNullableDecimalConverter Instance
= new();

return reader.TokenType switch
{
JsonToken.Null => default,
JsonToken.Undefined => default,
_ => Convert.ToDecimal(reader.Value),
};
}
/// <inheritdoc />
public override decimal? ReadJson(JsonReader reader, Type objectType, decimal? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader is null) throw new ArgumentNullException(nameof(reader));
Contract.EndContractBlock();

public override void WriteJson(JsonWriter writer, decimal? value, JsonSerializer serializer)
return reader.TokenType switch
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();
JsonToken.Null => null,
JsonToken.Undefined => null,
_ => ConvertToDecimal(reader.Value!),
};
}

/// <inheritdoc />
public override void WriteJson(JsonWriter writer, decimal? value, JsonSerializer serializer)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();

writer.WriteRawValue(JsonDecimalConverter.Normalize(value));
}
writer.WriteRawValue(JsonDecimalConverter.Normalize(value));
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,41 @@
using Newtonsoft.Json;
using System;

namespace Open.Serialization.Json.Newtonsoft.Converters
namespace Open.Serialization.Json.Newtonsoft.Converters;

/// <summary>
/// Converter for nullable decimals that rounds to a maximum number of digits after the decimal.
/// </summary>
public class JsonNullableDecimalRoundingConverter : JsonNullableDecimalConverter
{
public class JsonNullableDecimalRoundingConverter : JsonNullableDecimalConverter
/// <summary>
/// The maximum number of digits after the decimal.
/// </summary>
public int Maximum { get; }

/// <summary>
/// Constructs a <see cref="JsonNullableDecimalRoundingConverter"/>.
/// </summary>
/// <param name="maximum">The maximum number of digits after the decimal.</param>
/// <exception cref="ArgumentOutOfRangeException">If the maximum is less than zero.</exception>
public JsonNullableDecimalRoundingConverter(int maximum)
{
public int Maximum { get; }
public JsonNullableDecimalRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}

public override decimal? ReadJson(JsonReader reader, Type objectType, decimal? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var value = base.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer);
return value.HasValue ? Math.Round(value.Value, Maximum) : value;
}
public override decimal? ReadJson(JsonReader reader, Type objectType, decimal? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var value = base.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer);
return value.HasValue ? Math.Round(value.Value, Maximum) : value;
}

public override void WriteJson(JsonWriter writer, decimal? value, JsonSerializer serializer)
{
if (value.HasValue)
value = Math.Round(value.Value, Maximum);
public override void WriteJson(JsonWriter writer, decimal? value, JsonSerializer serializer)
{
if (value.HasValue)
value = Math.Round(value.Value, Maximum);

base.WriteJson(writer, value, serializer);
}
base.WriteJson(writer, value, serializer);
}
}
}
Original file line number Diff line number Diff line change
@@ -2,39 +2,37 @@
using System;
using System.Diagnostics.Contracts;

namespace Open.Serialization.Json.Newtonsoft.Converters
namespace Open.Serialization.Json.Newtonsoft.Converters;

public class JsonNullableDoubleConverter : JsonValueConverterBase<double?>
{
public class JsonNullableDoubleConverter : JsonValueConverterBase<double?>
protected JsonNullableDoubleConverter()
{
protected JsonNullableDoubleConverter()
{
// Prevent unnecessary replication.
}
// Prevent unnecessary replication.
}

public static readonly JsonNullableDoubleConverter Instance
= new JsonNullableDoubleConverter();
public static readonly JsonNullableDoubleConverter Instance
= new();

public override double? ReadJson(JsonReader reader, Type objectType, double? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader is null) throw new ArgumentNullException(nameof(reader));
Contract.EndContractBlock();

public override double? ReadJson(JsonReader reader, Type objectType, double? existingValue, bool hasExistingValue, JsonSerializer serializer)
return reader.TokenType switch
{
if (reader is null) throw new ArgumentNullException(nameof(reader));
Contract.EndContractBlock();

return reader.TokenType switch
{
JsonToken.Null => default,
JsonToken.Undefined => default,
_ => Convert.ToDouble(reader.Value)
};
}
JsonToken.Null => null,
JsonToken.Undefined => null,
_ => ConvertToDouble(reader.Value!)
};
}

public override void WriteJson(JsonWriter writer, double? value, JsonSerializer serializer)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();
public override void WriteJson(JsonWriter writer, double? value, JsonSerializer serializer)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();

if (value.HasValue) writer.WriteValue(value.Value);
else writer.WriteRawValue(null);
}
if (value.HasValue) writer.WriteValue(value.Value);
else writer.WriteRawValue(null);
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
using Newtonsoft.Json;
using System;

namespace Open.Serialization.Json.Newtonsoft.Converters
namespace Open.Serialization.Json.Newtonsoft.Converters;

public class JsonNullableDoubleRoundingConverter : JsonNullableDoubleConverter
{
public class JsonNullableDoubleRoundingConverter : JsonNullableDoubleConverter
public int Maximum { get; }
public JsonNullableDoubleRoundingConverter(int maximum)
{
public int Maximum { get; }
public JsonNullableDoubleRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}

public override double? ReadJson(JsonReader reader, Type objectType, double? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var value = base.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer);
return value.HasValue ? Math.Round(value.Value, Maximum) : value;
}
public override double? ReadJson(JsonReader reader, Type objectType, double? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var value = base.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer);
return value.HasValue ? Math.Round(value.Value, Maximum) : value;
}

public override void WriteJson(JsonWriter writer, double? value, JsonSerializer serializer)
{
if (value.HasValue)
value = Math.Round(value.Value, Maximum);
public override void WriteJson(JsonWriter writer, double? value, JsonSerializer serializer)
{
if (value.HasValue)
value = Math.Round(value.Value, Maximum);

base.WriteJson(writer, value, serializer);
}
base.WriteJson(writer, value, serializer);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,46 @@
using Newtonsoft.Json;
using System;
using System.Numerics;

namespace Open.Serialization.Json.Newtonsoft.Converters
namespace Open.Serialization.Json.Newtonsoft.Converters;

/// <summary>
/// Base JsonConverter for NewtsonSoft.Json.
/// </summary>
public abstract class JsonValueConverterBase<T> : JsonConverter<T>
{
public abstract class JsonValueConverterBase<T> : JsonConverter<T>
{
// Avoids stack overflow.
private static readonly JsonSerializer Deserializer = JsonSerializer.Create();
// Avoids stack overflow.
private static readonly JsonSerializer Deserializer = JsonSerializer.Create();

/// <inheritdoc />
public override T ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer)
=> Deserializer.Deserialize<T>(reader)!;

public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer)
=> Deserializer.Deserialize<T>(reader);
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer)
#pragma warning disable CA1062 // Validate arguments of public methods
=> writer.WriteRawValue(value?.ToString());
#pragma warning restore CA1062 // Validate arguments of public methods

public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
=> writer.WriteRawValue(value?.ToString());
}
}
/// <summary>
/// Special convert to decimal from object.
/// </summary>
protected static decimal ConvertToDecimal(object value) => value switch
{
decimal d => d,
BigInteger i => (decimal)i,
IConvertible _ => Convert.ToDecimal(value),
_ => throw new ArgumentException("Unable to convert to decimal.", nameof(value)),
};

/// <summary>
/// Special convert to decimal from object.
/// </summary>
protected static double ConvertToDouble(object value) => value switch
{
double d => d,
BigInteger i => (double)i,
IConvertible _ => Convert.ToDouble(value),
_ => throw new ArgumentException("Unable to convert to double.", nameof(value)),
};
}
9 changes: 9 additions & 0 deletions Open.Serialization.Json.Newtonsoft/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.

using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison for clarity", Justification = "<Pending>", Scope = "member", Target = "~M:Open.Serialization.Json.Newtonsoft.Converters.JsonDecimalConverter.Normalize(System.String)~System.String")]
[assembly: SuppressMessage("Usage", "CA2249:Consider using 'string.Contains' instead of 'string.IndexOf'", Justification = "<Pending>", Scope = "member", Target = "~M:Open.Serialization.Json.Newtonsoft.Converters.JsonDecimalConverter.Normalize(System.String)~System.String")]
121 changes: 63 additions & 58 deletions Open.Serialization.Json.Newtonsoft/JsonSerializerFactory.cs
Original file line number Diff line number Diff line change
@@ -3,81 +3,86 @@
using System;
using System.Threading;

namespace Open.Serialization.Json.Newtonsoft
namespace Open.Serialization.Json.Newtonsoft;

public class JsonSerializerFactory : IJsonSerializerFactory, IJsonObjectSerializerFactory
{
public class JsonSerializerFactory : IJsonSerializerFactory, IJsonObjectSerializerFactory
static readonly JsonSerializerSettings DefaultOptions = RelaxedJson.Options();
readonly JsonSerializerSettings _settings;
public JsonSerializerFactory(JsonSerializerSettings? defaultOptions)
{
static readonly JsonSerializerSettings DefaultOptions = RelaxedJson.Options();
readonly JsonSerializerSettings _settings;
public JsonSerializerFactory(JsonSerializerSettings? defaultOptions)
{
_settings = defaultOptions?.Clone() ?? DefaultOptions;
}
_settings = defaultOptions?.Clone() ?? DefaultOptions;
}

public JsonSerializerFactory() : this(null)
{
}
public JsonSerializerFactory() : this(null)
{
}

JsonSerializerInternal? _defaultSerializer;
internal JsonSerializerInternal DefaultSerializer
=> LazyInitializer.EnsureInitialized(ref _defaultSerializer, () => new JsonSerializerInternal(_settings))!;
JsonSerializerInternal? _defaultSerializer;
internal JsonSerializerInternal DefaultSerializer
=> LazyInitializer.EnsureInitialized(ref _defaultSerializer, () => new JsonSerializerInternal(_settings))!;

static JsonSerializerFactory? _default;
public static JsonSerializerFactory Default
=> LazyInitializer.EnsureInitialized(ref _default)!;
static JsonSerializerFactory? _default;
public static JsonSerializerFactory Default
=> LazyInitializer.EnsureInitialized(ref _default)!;

#if NETSTANDARD2_1
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("options")]
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("options")]
#endif
public JsonSerializerSettings? GetJsonSerializerSettings(IJsonSerializationOptions? options = null, bool caseSensitive = false)
{
if (caseSensitive)
throw new NotSupportedException("Newtonsoft does not support case-sensitive deserialization.");
public JsonSerializerSettings? GetJsonSerializerSettings(IJsonSerializationOptions? options = null, bool caseSensitive = false)
{
if (caseSensitive)
throw new NotSupportedException("Newtonsoft does not support case-sensitive deserialization.");

if (options == null) return null;
if (options is null) return null;

if (options.CamelCaseKeys == true && options.CamelCaseProperties != true)
throw new NotSupportedException("Camel casing keys but not properties is not supported.");
if (options.CamelCaseKeys == true && options.CamelCaseProperties != true)
throw new NotSupportedException("Camel casing keys but not properties is not supported.");

var o = _settings.Clone();
if (options.CamelCaseKeys == true)
var o = _settings.Clone();
if (options.CamelCaseKeys == true)
{
o.ContractResolver = new DefaultContractResolver
{
o.ContractResolver = new DefaultContractResolver
NamingStrategy = new CamelCaseNamingStrategy
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true
}
};
}
else if (options.CamelCaseProperties == true)
{
o.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
else if (options.CamelCaseProperties == false)
{
o.ContractResolver = new DefaultContractResolver();
}

if (options.OmitNull.HasValue)
o.NullValueHandling = options.OmitNull.Value ? NullValueHandling.Ignore : NullValueHandling.Include;

if (options.Indent.HasValue)
o.Formatting = options.Indent.Value ? Formatting.Indented : Formatting.None;

return o;
ProcessDictionaryKeys = true
}
};
}

internal JsonSerializerInternal GetSerializerInternal(IJsonSerializationOptions? options = null, bool caseSensitive = false)
else if (options.CamelCaseProperties == true)
{
o.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
else if (options.CamelCaseProperties == false)
{
var o = GetJsonSerializerSettings(options, caseSensitive);
return o == null ? DefaultSerializer : new JsonSerializerInternal(o);
o.ContractResolver = new DefaultContractResolver();
}

public IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);
if (options.OmitNull.HasValue)
o.NullValueHandling = options.OmitNull.Value ? NullValueHandling.Ignore : NullValueHandling.Include;

public IJsonObjectSerializer GetObjectSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);
if (options.Indent.HasValue)
o.Formatting = options.Indent.Value ? Formatting.Indented : Formatting.None;

return o;
}

internal JsonSerializerInternal GetSerializerInternal(IJsonSerializationOptions? options = null, bool caseSensitive = false)
{
var o = GetJsonSerializerSettings(options, caseSensitive);
return o is null ? DefaultSerializer : new JsonSerializerInternal(o);
}

/// <summary>
/// Returns an <see cref="IJsonSerializer"/>.
/// </summary>
public IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);

/// <summary>
/// Returns an <see cref="IJsonObjectSerializer"/>.
/// </summary>
public IJsonObjectSerializer GetObjectSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);
}
36 changes: 17 additions & 19 deletions Open.Serialization.Json.Newtonsoft/JsonSerializerInternal.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
using Newtonsoft.Json;
using System;

namespace Open.Serialization.Json.Newtonsoft
namespace Open.Serialization.Json.Newtonsoft;

internal class JsonSerializerInternal : JsonObjectSerializerBase, IJsonSerializer
{
internal class JsonSerializerInternal : JsonObjectSerializerBase, IJsonSerializer
readonly JsonSerializerSettings _settings;
internal JsonSerializerInternal(JsonSerializerSettings settings)
{
readonly JsonSerializerSettings _settings;
internal JsonSerializerInternal(JsonSerializerSettings settings)
{
_settings = settings;
}

public override T Deserialize<T>(string? value)
=> JsonConvert.DeserializeObject<T>(value, _settings);
_settings = settings;
}

public override string? Serialize<T>(T item)
=> JsonConvert.SerializeObject(item, _settings);
public override T Deserialize<T>(string value)
=> JsonConvert.DeserializeObject<T>(value, _settings)!;

public override object? Deserialize(string? value, Type type)
=> JsonConvert.DeserializeObject(value, type);
public override string Serialize<T>(T item)
=> JsonConvert.SerializeObject(item, _settings);

public override string? Serialize(object? item, Type type)
=> JsonConvert.SerializeObject(item, type, _settings);
public override object? Deserialize(string value, Type type)
=> JsonConvert.DeserializeObject(value, type);

public new JsonSerializer<T> Cast<T>()
=> new JsonSerializer<T>(Deserialize<T>, Serialize, DeserializeAsync<T>, SerializeAsync);
public override string Serialize(object? item, Type type)
=> JsonConvert.SerializeObject(item, type, _settings);

}
public new JsonSerializer<T> Cast<T>()
=> new(Deserialize<T>, Serialize, DeserializeAsync<T>, SerializeAsync);
}
Original file line number Diff line number Diff line change
@@ -4,31 +4,50 @@
<TargetFrameworks>netstandard2.0; netstandard2.1</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<Authors>electricessence</Authors>
<Company>electricessence</Company>
<Description>
Extentions and DI utilities for Newtonsoft.Json.

Part of the "Open" set of libraries.
</Description>
<Copyright>https://github.com/electricessence/Open.Serialization/blob/master/LICENSE</Copyright>
<PackageTags>serialization;json;newtonsoft</PackageTags>
<Copyright>© electricessence (Oren F.) All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/electricessence/Open.Serialization</PackageProjectUrl>
<RepositoryUrl>https://github.com/electricessence/Open.Serialization</RepositoryUrl>
<PackageProjectUrl>https://github.com/Open-NET-Libraries/Open.Serialization</PackageProjectUrl>
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Serialization</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>serialization json newtonsoft</PackageTags>
<Version>2.2.4</Version>
<Nullable>enable</Nullable>
<Version>3.0.0</Version>
<PackageReleaseNotes></PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageIcon>logo.png</PackageIcon>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
<PackageReference Include="Open.Serialization.Json" Version="2.2.2" />
</ItemGroup>

<ItemGroup>
<None Include="..\logo.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Open.Serialization.Json\Open.Serialization.Json.csproj" />
</ItemGroup>

</Project>
56 changes: 33 additions & 23 deletions Open.Serialization.Json.Newtonsoft/RelaxedJson.cs
Original file line number Diff line number Diff line change
@@ -2,33 +2,43 @@
using Newtonsoft.Json.Serialization;
using Open.Serialization.Json.Newtonsoft.Converters;

namespace Open.Serialization.Json.Newtonsoft
namespace Open.Serialization.Json.Newtonsoft;

/// <summary>
/// Shortcut for accessing default relaxed JSON options.
/// </summary>
public static class RelaxedJson
{
public static class RelaxedJson
{
internal static JsonSerializerSettings Options(
bool indent = false)
=> new JsonSerializerSettings()
internal static JsonSerializerSettings Options(
bool indent = false)
=> new JsonSerializerSettings()
{
Formatting = indent ? Formatting.Indented : Formatting.None,
FloatParseHandling = FloatParseHandling.Decimal,
ContractResolver = new DefaultContractResolver()
{
Formatting = indent ? Formatting.Indented : Formatting.None,
FloatParseHandling = FloatParseHandling.Decimal,
ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new DefaultNamingStrategy() { ProcessDictionaryKeys = false }
}
NamingStrategy = new DefaultNamingStrategy() { ProcessDictionaryKeys = false }
}
.AddConverter(JsonNullableDoubleConverter.Instance)
.NormalizeDecimals();
}
.AddConverter(JsonNullableDoubleConverter.Instance)
.NormalizeDecimals();

/// <summary>
/// Returns an <see cref="IJsonDeserialize"/>.s
/// </summary>
public static IJsonDeserialize<TValue> GetDeserializer<TValue>()
=> DeserializerOptions.GetSerializer<TValue>();

public static IJsonDeserialize<TValue> GetDeserializer<TValue>()
=> DeserializerOptions.GetSerializer<TValue>();
public static IJsonDeserialize GetDeserializer()
=> DeserializerOptions.GetSerializer();
/// <summary>
/// Returns an <see cref="IJsonDeserialize"/>.
/// </summary>
public static IJsonDeserialize GetDeserializer()
=> DeserializerOptions.GetSerializer();

static readonly JsonSerializerSettings DeserializerOptions
= Options().SetNullValueHandling(NullValueHandling.Ignore);
static readonly JsonSerializerSettings DeserializerOptions
= Options().SetNullValueHandling(NullValueHandling.Ignore);

public static TValue Deserialize<TValue>(string value)
=> DeserializerOptions.Deserialize<TValue>(value);
}
/// <inheritdoc cref="IDeserialize.Deserialize{T}(string)"/>
public static TValue Deserialize<TValue>(string value)
=> DeserializerOptions.Deserialize<TValue>(value);
}
360 changes: 184 additions & 176 deletions Open.Serialization.Json.Newtonsoft/SerializationExtensions.cs

Large diffs are not rendered by default.

34 changes: 19 additions & 15 deletions Open.Serialization.Json.System/CamelCaseJson.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
using System.Text.Json;

namespace Open.Serialization.Json.System
namespace Open.Serialization.Json.System;

/// <summary>
/// Provides default 'camel case' serialization configurations.
/// </summary>
public static class CamelCaseJson
{
public static class CamelCaseJson
/// <summary>
/// Default relaxed serializations.
/// </summary>
public static JsonSerializerOptions Default(bool indent = false)
{
public static JsonSerializerOptions Default(bool indent = false)
{
var options = RelaxedJson.Options(indent);
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
return options;
}

public static JsonSerializerOptions Minimal(bool indent = false)
{
var options = Default(indent);
options.IgnoreNullValues = true;
return options;
}
var options = RelaxedJson.Options(indent);
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
return options;
}

/// <summary>
/// Default relaxed serializations but also ignores null values.
/// </summary>
public static JsonSerializerOptions Minimal(bool indent = false)
=> Default(indent).SetIgnoreNullValues();
}
28 changes: 16 additions & 12 deletions Open.Serialization.Json.System/CaseSensitiveJson.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
using System.Text.Json;

namespace Open.Serialization.Json.System
namespace Open.Serialization.Json.System;

/// <summary>
/// Provides default 'case sensitive' serialization configurations.
/// </summary>
public static class CaseSensitiveJson
{
public static class CaseSensitiveJson
{
public static JsonSerializerOptions Default(bool indent = false)
=> RelaxedJson.Options(indent, true);
/// <summary>
/// Default relaxed serializations.
/// </summary>
public static JsonSerializerOptions Default(bool indent = false)
=> RelaxedJson.Options(indent, true);

public static JsonSerializerOptions Minimal(bool indent = false)
{
var options = Default(indent);
options.IgnoreNullValues = true;
return options;
}
}
/// <summary>
/// Default relaxed serializations but also ignores null values.
/// </summary>
public static JsonSerializerOptions Minimal(bool indent = false)
=> Default(indent).SetIgnoreNullValues();
}
46 changes: 28 additions & 18 deletions Open.Serialization.Json.System/Converters/JsonDecimalConverter.cs
Original file line number Diff line number Diff line change
@@ -2,29 +2,39 @@
using System.Diagnostics.Contracts;
using System.Text.Json;

namespace Open.Serialization.Json.System.Converters
namespace Open.Serialization.Json.System.Converters;

/// <summary>
/// Converter for decimals.
/// </summary>
public class JsonDecimalConverter : JsonValueConverterBase<decimal>
{
public class JsonDecimalConverter : JsonValueConverterBase<decimal>
/// <summary>
/// Constructs a <see cref="JsonDecimalConverter"/>.
/// </summary>
protected JsonDecimalConverter()
{
protected JsonDecimalConverter()
{
// Prevent unnecessary replication.
}
// Prevent unnecessary replication.
}

public static readonly JsonDecimalConverter Instance
= new JsonDecimalConverter();
/// <summary>
/// The shared instance of a <see cref="JsonDecimalConverter"/>.
/// </summary>
public static readonly JsonDecimalConverter Instance
= new();

public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.GetDecimal();
/// <inheritdoc />
public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.GetDecimal();

public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();

var v = value / 1M;
var truncated = decimal.Truncate(v);
writer.WriteNumberValue(truncated == v ? truncated : v / 1.000000000000000000000000000000000m);
}
var v = value / 1M;
var truncated = decimal.Truncate(v);
writer.WriteNumberValue(truncated == v ? truncated : v / 1.000000000000000000000000000000000m);
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
using System;
using System.Text.Json;

namespace Open.Serialization.Json.System.Converters
namespace Open.Serialization.Json.System.Converters;

/// <summary>
/// Converter for decimals that rounds to a maximum number of digits after the decimal.
/// </summary>
public class JsonDecimalRoundingConverter : JsonDecimalConverter
{
public class JsonDecimalRoundingConverter : JsonDecimalConverter
/// <summary>
/// The maximum number of digits after the decimal.
/// </summary>
public int Maximum { get; }

/// <summary>
/// Constructs a <see cref="JsonDecimalRoundingConverter"/>.
/// </summary>
/// <param name="maximum">The maximum number of digits after the decimal.</param>
/// <exception cref="ArgumentOutOfRangeException">If the maximum is less than zero.</exception>
public JsonDecimalRoundingConverter(int maximum)
{
public int Maximum { get; }
public JsonDecimalRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}

public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> Math.Round(reader.GetDecimal(), Maximum);
/// <inheritdoc />
public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> Math.Round(reader.GetDecimal(), Maximum);

public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options)
=> base.Write(writer, Math.Round(value, Maximum), options);
}
}
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options)
=> base.Write(writer, Math.Round(value, Maximum), options);
}
Original file line number Diff line number Diff line change
@@ -2,27 +2,40 @@
using System.Diagnostics.Contracts;
using System.Text.Json;

namespace Open.Serialization.Json.System.Converters
namespace Open.Serialization.Json.System.Converters;

/// <summary>
/// Converter for doubles that rounds to a maximum number of digits after the decimal.
/// </summary>
public class JsonDoubleRoundingConverter : JsonValueConverterBase<double>
{
public class JsonDoubleRoundingConverter : JsonValueConverterBase<double>
/// <summary>
/// The maximum number of digits after the decimal.
/// </summary>
public int Maximum { get; }

/// <summary>
/// Constructs a <see cref="JsonDoubleRoundingConverter"/>.
/// </summary>
/// <param name="maximum">The maximum number of digits after the decimal.</param>
/// <exception cref="ArgumentOutOfRangeException">If the maximum is less than zero.</exception>
public JsonDoubleRoundingConverter(int maximum)
{
public int Maximum { get; }
public JsonDoubleRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}

public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> Math.Round(reader.GetDouble(), Maximum);
/// <inheritdoc />
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> Math.Round(reader.GetDouble(), Maximum);

public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();

writer.WriteNumberValue(Math.Round(value, Maximum));
}
writer.WriteNumberValue(Math.Round(value, Maximum));
}
}
}
Original file line number Diff line number Diff line change
@@ -3,36 +3,47 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Open.Serialization.Json.System.Converters
namespace Open.Serialization.Json.System.Converters;

/// <summary>
/// Converter for nullable decimals.
/// </summary>
public class JsonNullableDecimalConverter : JsonConverter<decimal?>
{
public class JsonNullableDecimalConverter : JsonConverter<decimal?>
/// <summary>
/// Constructs a <see cref="JsonNullableDecimalConverter"/>.
/// </summary>
protected JsonNullableDecimalConverter()
{
protected JsonNullableDecimalConverter()
{
// Prevent unnecessary replication.
}

public static readonly JsonNullableDecimalConverter Instance
= new JsonNullableDecimalConverter();
// Prevent unnecessary replication.
}

public override bool CanConvert(Type objectType)
=> objectType == typeof(decimal?) || objectType == typeof(decimal);
/// <summary>
/// The shared instance of this converter.
/// </summary>
public static readonly JsonNullableDecimalConverter Instance
= new();

public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType switch
{
JsonTokenType.Null => default,
JsonTokenType.Number => reader.GetDecimal(),
_ => throw new JsonException("Unexpected token type."),
};
/// <inheritdoc />
public override bool CanConvert(Type objectType)
=> objectType == typeof(decimal?) || objectType == typeof(decimal);

public override void Write(Utf8JsonWriter writer, decimal? value, JsonSerializerOptions options)
/// <inheritdoc />
public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType switch
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();
JsonTokenType.Null => null,
JsonTokenType.Number => reader.GetDecimal(),
_ => throw new JsonException("Unexpected token type."),
};

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, decimal? value, JsonSerializerOptions options)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();

if (value.HasValue) JsonDecimalConverter.Instance.Write(writer, value.Value, options);
else writer.WriteNullValue();
}
if (value.HasValue) JsonDecimalConverter.Instance.Write(writer, value.Value, options);
else writer.WriteNullValue();
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
using System;
using System.Text.Json;

namespace Open.Serialization.Json.System.Converters
namespace Open.Serialization.Json.System.Converters;

/// <summary>
/// Converter for nullable doubles that rounds to a maximum number of digits after the decimal.
/// </summary>
public class JsonNullableDecimalRoundingConverter : JsonNullableDecimalConverter
{
public class JsonNullableDecimalRoundingConverter : JsonNullableDecimalConverter
{
public int Maximum { get; }
public JsonNullableDecimalRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}
/// <summary>
/// The maximum number of digits after the decimal.
/// </summary>
public int Maximum { get; }

public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
return Math.Round(reader.GetDecimal(), Maximum);
/// <summary>
/// Constructs a <see cref="JsonNullableDecimalRoundingConverter"/>.
/// </summary>
/// <param name="maximum">The maximum number of digits after the decimal.</param>
/// <exception cref="ArgumentOutOfRangeException">If the maximum is less than zero.</exception>
public JsonNullableDecimalRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}

return base.Read(ref reader, typeToConvert, options);
}
/// <inheritdoc />
public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType == JsonTokenType.Number
? Math.Round(reader.GetDecimal(), Maximum)
: base.Read(ref reader, typeToConvert, options);

public override void Write(Utf8JsonWriter writer, decimal? value, JsonSerializerOptions options)
{
if (value.HasValue)
value = Math.Round(value.Value, Maximum);
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, decimal? value, JsonSerializerOptions options)
{
if (value.HasValue)
value = Math.Round(value.Value, Maximum);

base.Write(writer, value, options);
}
base.Write(writer, value, options);
}
}
}
Original file line number Diff line number Diff line change
@@ -3,36 +3,47 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Open.Serialization.Json.System.Converters
namespace Open.Serialization.Json.System.Converters;

/// <summary>
/// Converter for nullable doubles.
/// </summary>
public class JsonNullableDoubleConverter : JsonConverter<double?>
{
public class JsonNullableDoubleConverter : JsonConverter<double?>
/// <summary>
/// Constructs a <see cref="JsonNullableDoubleConverter"/>.
/// </summary>
protected JsonNullableDoubleConverter()
{
protected JsonNullableDoubleConverter()
{
// Prevent unnecessary replication.
}

public static readonly JsonNullableDoubleConverter Instance
= new JsonNullableDoubleConverter();
// Prevent unnecessary replication.
}

public override bool CanConvert(Type objectType)
=> objectType == typeof(double?);
/// <summary>
/// The shared instance for this converter.
/// </summary>
public static readonly JsonNullableDoubleConverter Instance
= new();

public override double? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType switch
{
JsonTokenType.Null => default,
JsonTokenType.Number => reader.GetDouble(),
_ => throw new JsonException("Unexpected token type."),
};
/// <inheritdoc />
public override bool CanConvert(Type objectType)
=> objectType == typeof(double?);

public override void Write(Utf8JsonWriter writer, double? value, JsonSerializerOptions options)
/// <inheritdoc />
public override double? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType switch
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();
JsonTokenType.Null => null,
JsonTokenType.Number => reader.GetDouble(),
_ => throw new JsonException("Unexpected token type."),
};

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, double? value, JsonSerializerOptions options)
{
if (writer is null) throw new ArgumentNullException(nameof(writer));
Contract.EndContractBlock();

if (value.HasValue) writer.WriteNumberValue(value.Value);
else writer.WriteNullValue();
}
if (value.HasValue) writer.WriteNumberValue(value.Value);
else writer.WriteNullValue();
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
using System;
using System.Text.Json;

namespace Open.Serialization.Json.System.Converters
namespace Open.Serialization.Json.System.Converters;

/// <summary>
/// Converter for nullable doubles that rounds to a maximum number of digits after the decimal.
/// </summary>
public class JsonNullableDoubleRoundingConverter : JsonNullableDoubleConverter
{
public class JsonNullableDoubleRoundingConverter : JsonNullableDoubleConverter
{
public int Maximum { get; }
public JsonNullableDoubleRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}
/// <summary>
/// The maximum number of digits after the decimal.
/// </summary>
public int Maximum { get; }

public override double? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
return Math.Round(reader.GetDouble(), Maximum);
/// <summary>
/// Constructs a <see cref="JsonNullableDoubleRoundingConverter"/>.
/// </summary>
/// <param name="maximum">The maximum number of digits after the decimal.</param>
/// <exception cref="ArgumentOutOfRangeException">If the maximum is less than zero.</exception>
public JsonNullableDoubleRoundingConverter(int maximum)
{
if (maximum < 0)
throw new ArgumentOutOfRangeException(nameof(maximum), maximum, "Must be at least zero.");
Maximum = maximum;
}

return base.Read(ref reader, typeToConvert, options);
}
/// <inheritdoc />
public override double? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType == JsonTokenType.Number
? Math.Round(reader.GetDouble(), Maximum)
: base.Read(ref reader, typeToConvert, options);

public override void Write(Utf8JsonWriter writer, double? value, JsonSerializerOptions options)
{
if (value.HasValue)
value = Math.Round(value.Value, Maximum);
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, double? value, JsonSerializerOptions options)
{
if (value.HasValue)
value = Math.Round(value.Value, Maximum);

base.Write(writer, value, options);
}
base.Write(writer, value, options);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.Text.Json.Serialization;

namespace Open.Serialization.Json.System.Converters
namespace Open.Serialization.Json.System.Converters;

/// <summary>Base class for other json value converters.</summary>
/// <inheritdoc />
public abstract class JsonValueConverterBase<T> : JsonConverter<T>
{
public abstract class JsonValueConverterBase<T> : JsonConverter<T>
{
public override bool CanConvert(Type objectType)
=> objectType == typeof(T);
}
/// <inheritdoc />
public override bool CanConvert(Type objectType)
=> objectType == typeof(T);
}
96 changes: 55 additions & 41 deletions Open.Serialization.Json.System/JsonSerializerFactory.cs
Original file line number Diff line number Diff line change
@@ -1,58 +1,72 @@
using System.Text.Json;
using System.Threading;

namespace Open.Serialization.Json.System
namespace Open.Serialization.Json.System;

/// <summary>
/// The default <see cref="IJsonSerializerFactory"/> for System.Text.Json.
/// </summary>
public class JsonSerializerFactory : IJsonSerializerFactory
{
public class JsonSerializerFactory : IJsonSerializerFactory
static readonly JsonSerializerOptions DefaultOptions = RelaxedJson.Options();
readonly JsonSerializerOptions _options;

/// <summary>
/// Constructs a <see cref="JsonSerializerFactory"/>
/// </summary>
public JsonSerializerFactory(JsonSerializerOptions? defaultOptions)
{
static readonly JsonSerializerOptions DefaultOptions = RelaxedJson.Options();
readonly JsonSerializerOptions _options;
public JsonSerializerFactory(JsonSerializerOptions? defaultOptions)
{
_options = defaultOptions?.Clone() ?? DefaultOptions;
}
_options = defaultOptions?.Clone() ?? DefaultOptions;
}

public JsonSerializerFactory() : this(null)
{
}
/// <inheritdoc cref="JsonSerializerFactory(JsonSerializerOptions?)"/>
public JsonSerializerFactory() : this(null)
{
}

JsonSerializerInternal? _caseSensitive;
JsonSerializerInternal? _ignoreCase;
JsonSerializerInternal? _caseSensitive;
JsonSerializerInternal? _ignoreCase;

static JsonSerializerFactory? _default;
public static JsonSerializerFactory Default
=> LazyInitializer.EnsureInitialized(ref _default)!;
static JsonSerializerFactory? _default;

JsonSerializerOptions? GetJsonSerializerSettings(IJsonSerializationOptions? options = null)
{
if (options == null) return null;

var o = _options.Clone();
o.IgnoreNullValues = options.OmitNull ?? o.IgnoreNullValues;
o.WriteIndented = options.Indent ?? o.WriteIndented;
o.DictionaryKeyPolicy = options.CamelCaseKeys == true ? JsonNamingPolicy.CamelCase : o.DictionaryKeyPolicy;
o.PropertyNamingPolicy = options.CamelCaseProperties == true ? JsonNamingPolicy.CamelCase : o.PropertyNamingPolicy;
return o;
}
/// <summary>
/// The default <see cref="IJsonSerializerFactory"/> instance for System.Text.Json.
/// </summary>
public static JsonSerializerFactory Default
=> LazyInitializer.EnsureInitialized(ref _default)!;

internal JsonSerializerInternal GetSerializerInternal(IJsonSerializationOptions? options = null, bool caseSensitive = false)
JsonSerializerOptions? GetJsonSerializerSettings(IJsonSerializationOptions? options = null)
{
if (options is null) return null;

var o = _options.Clone();
if(options.OmitNull.HasValue) o.SetIgnoreNullValues(options.OmitNull.Value);
o.WriteIndented = options.Indent ?? o.WriteIndented;
o.DictionaryKeyPolicy = options.CamelCaseKeys == true ? JsonNamingPolicy.CamelCase : o.DictionaryKeyPolicy;
o.PropertyNamingPolicy = options.CamelCaseProperties == true ? JsonNamingPolicy.CamelCase : o.PropertyNamingPolicy;
return o;
}

internal JsonSerializerInternal GetSerializerInternal(IJsonSerializationOptions? options = null, bool caseSensitive = false)
{
var o = GetJsonSerializerSettings(options);
if (o is null)
{
var o = GetJsonSerializerSettings(options);
if (o == null)
{
#pragma warning disable CS8603 // Possible null reference return.
return caseSensitive
? LazyInitializer.EnsureInitialized(ref _caseSensitive,
() => new JsonSerializerInternal(_options.Clone().SetPropertyNameCaseInsensitive(false)))
: LazyInitializer.EnsureInitialized(ref _ignoreCase,
() => new JsonSerializerInternal(_options.Clone().SetPropertyNameCaseInsensitive(true)));
return caseSensitive
? LazyInitializer.EnsureInitialized(ref _caseSensitive,
() => new JsonSerializerInternal(_options.Clone().SetPropertyNameCaseInsensitive(false)))
: LazyInitializer.EnsureInitialized(ref _ignoreCase,
() => new JsonSerializerInternal(_options.Clone().SetPropertyNameCaseInsensitive(true)));
#pragma warning restore CS8603 // Possible null reference return.
}

return new JsonSerializerInternal(o);
}

public IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);
return new JsonSerializerInternal(o);
}

/// <summary>
/// Returns an <see cref="IJsonSerializer"/>.
/// </summary>
public IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);
}
36 changes: 17 additions & 19 deletions Open.Serialization.Json.System/JsonSerializerInternal.cs
Original file line number Diff line number Diff line change
@@ -4,30 +4,28 @@
using System.Threading;
using System.Threading.Tasks;

namespace Open.Serialization.Json.System
namespace Open.Serialization.Json.System;

internal class JsonSerializerInternal : JsonSerializerBase, IJsonSerializer
{
internal class JsonSerializerInternal : JsonSerializerBase, IJsonSerializer
readonly JsonSerializerOptions _options;
internal JsonSerializerInternal(JsonSerializerOptions options)
{
readonly JsonSerializerOptions _options;
internal JsonSerializerInternal(JsonSerializerOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}

public override T Deserialize<T>(string? value)
=> JsonSerializer.Deserialize<T>(value, _options);
_options = options ?? throw new ArgumentNullException(nameof(options));
}

ValueTask ISerializeAsync.SerializeAsync<T>(Stream stream, T item, CancellationToken cancellationToken)
=> new ValueTask(JsonSerializer.SerializeAsync(stream, item, _options, cancellationToken));
public override T Deserialize<T>(string value)
=> JsonSerializer.Deserialize<T>(value!, _options)!;

public new Task SerializeAsync<T>(Stream stream, T item, CancellationToken cancellationToken = default)
=> JsonSerializer.SerializeAsync(stream, item, _options, cancellationToken);
ValueTask ISerializeAsync.SerializeAsync<T>(Stream stream, T item, CancellationToken cancellationToken)
=> new(JsonSerializer.SerializeAsync(stream, item, _options, cancellationToken));

public override string? Serialize<T>(T item)
=> JsonSerializer.Serialize(item, _options);
public new Task SerializeAsync<T>(Stream stream, T item, CancellationToken cancellationToken = default)
=> JsonSerializer.SerializeAsync(stream, item, _options, cancellationToken);

public override ValueTask<T> DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken = default)
=> JsonSerializer.DeserializeAsync<T>(stream, _options);
public override string Serialize<T>(T item)
=> JsonSerializer.Serialize(item, _options);

}
public override ValueTask<T> DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken = default)
=> JsonSerializer.DeserializeAsync<T>(stream, _options)!;
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,53 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0; netstandard2.1</TargetFrameworks>
<PropertyGroup>
<TargetFrameworks>netstandard2.0; netstandard2.1</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Authors>electricessence</Authors>
<Company>electricessence</Company>
<Description>
Extentions and DI utilities for System.Text.Json.
Extentions and DI utilities for System.Text.Json.

Part of the "Open" set of libraries.
Part of the "Open" set of libraries.
</Description>
<Copyright>https://github.com/electricessence/Open.Serialization/blob/master/LICENSE</Copyright>
<PackageTags>serialization;json;stj;System.Text.Json</PackageTags>
<Copyright>© electricessence (Oren F.) All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/electricessence/Open.Serialization</PackageProjectUrl>
<RepositoryUrl>https://github.com/electricessence/Open.Serialization</RepositoryUrl>
<PackageProjectUrl>https://github.com/Open-NET-Libraries/Open.Serialization</PackageProjectUrl>
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Serialization</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>serialization json stj</PackageTags>
<Version>2.2.4</Version>
<Nullable>enable</Nullable>
<Version>3.0.0</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageReleaseNotes></PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageIcon>logo.png</PackageIcon>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
<PackageReference Include="Open.Serialization.Json" Version="2.2.2" />
<PackageReference Include="System.Text.Json" Version="4.7.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="System.Text.Json" Version="6.0.10" />
</ItemGroup>

<ItemGroup>
<None Include="..\logo.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Open.Serialization.Json\Open.Serialization.Json.csproj" />
</ItemGroup>

</Project>
77 changes: 50 additions & 27 deletions Open.Serialization.Json.System/RelaxedJson.cs
Original file line number Diff line number Diff line change
@@ -2,32 +2,55 @@
using System;
using System.Text.Json;

namespace Open.Serialization.Json.System
namespace Open.Serialization.Json.System;

/// <summary>
/// Shortcut for accessing default relaxed JSON options.
/// </summary>
public static class RelaxedJson
{
public static class RelaxedJson
{
internal static JsonSerializerOptions Options(
bool indent = false,
bool caseSensitive = false)
=> new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = !caseSensitive,
AllowTrailingCommas = true,
WriteIndented = indent
}
.AddConverter(JsonNullableDoubleConverter.Instance)
.NormalizeDecimals();

static readonly JsonSerializerOptions DeserializerOptions
= Options().SetIgnoreNullValues();

public static IJsonDeserialize<TValue> GetDeserializer<TValue>()
=> DeserializerOptions.GetSerializer<TValue>();
public static IJsonDeserialize GetDeserializer()
=> DeserializerOptions.GetSerializer();
public static TValue Deserialize<TValue>(string? value)
=> DeserializerOptions.Deserialize<TValue>(value);
public static TValue Deserialize<TValue>(ReadOnlySpan<byte> value)
=> DeserializerOptions.Deserialize<TValue>(value);
}
internal static JsonSerializerOptions Options(
bool indent = false,
bool caseSensitive = false)
=> new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = !caseSensitive,
AllowTrailingCommas = true,
WriteIndented = indent
}
.AddConverter(JsonNullableDoubleConverter.Instance)
.NormalizeDecimals();

static readonly JsonSerializerOptions DeserializerOptions
= Options().SetIgnoreNullValues();

/// <summary>
/// Returns an <see cref="IJsonDeserialize{TValue}"/>.
/// </summary>
public static IJsonDeserialize<TValue> GetDeserializer<TValue>()
=> DeserializerOptions.GetSerializer<TValue>();

/// <summary>
/// Returns an <see cref="IJsonDeserialize"/>.
/// </summary>
public static IJsonDeserialize GetDeserializer()
=> DeserializerOptions.GetSerializer();

/// <summary>
/// Deserializes <paramref name="value"/> to <typeparamref name="TValue"/>.
/// </summary>
public static TValue Deserialize<TValue>(string value)
=> DeserializerOptions.Deserialize<TValue>(value);

/// <summary>
/// Deserializes <paramref name="value"/> to <typeparamref name="TValue"/>.
/// </summary>
public static TValue Deserialize<TValue>(ReadOnlySpan<char> value)
=> DeserializerOptions.Deserialize<TValue>(value);

/// <summary>
/// Deserializes <paramref name="value"/> to <typeparamref name="TValue"/>.
/// </summary>
public static TValue Deserialize<TValue>(ReadOnlySpan<byte> value)
=> DeserializerOptions.Deserialize<TValue>(value);
}
441 changes: 266 additions & 175 deletions Open.Serialization.Json.System/SerializationsExtensions.cs

Large diffs are not rendered by default.

114 changes: 66 additions & 48 deletions Open.Serialization.Json.Utf8Json/JsonSerializerFactory.cs
Original file line number Diff line number Diff line change
@@ -3,55 +3,73 @@
using Utf8Json;
using Utf8Json.Resolvers;

namespace Open.Serialization.Json.Utf8Json
namespace Open.Serialization.Json.Utf8Json;

/// <summary>
/// The <see cref="IJsonSerializerFactory"/> for Utf8Json.
/// </summary>
public class JsonSerializerFactory : IJsonSerializerFactory
{
public class JsonSerializerFactory : IJsonSerializerFactory
readonly IJsonFormatterResolver _resolver;
readonly bool _indent;

/// <exception cref="ArgumentOutOfRangeException">Snake case is not supported.</exception>
/// <inheritdoc cref="JsonSerializerFactory()"/>
public JsonSerializerFactory(IJsonFormatterResolver? defaultResolver, bool indent = false)
{
_resolver = defaultResolver ?? StandardResolver.Default;
if (_resolver == StandardResolver.ExcludeNullSnakeCase || _resolver == StandardResolver.SnakeCase)
throw new ArgumentOutOfRangeException(nameof(defaultResolver), "Snake case is not supported.");
_indent = indent;
}

/// <summary>
/// Constructs a <see cref="JsonSerializerFactory"/>.
/// </summary>
public JsonSerializerFactory() : this(null)
{
readonly IJsonFormatterResolver _resolver;
readonly bool _indent;
public JsonSerializerFactory(IJsonFormatterResolver? defaultResolver, bool indent = false)
{
_resolver = defaultResolver ?? StandardResolver.Default;
if (_resolver == StandardResolver.ExcludeNullSnakeCase || _resolver == StandardResolver.SnakeCase)
throw new ArgumentOutOfRangeException(nameof(defaultResolver), "Snake case is not supported.");
_indent = indent;
}

public JsonSerializerFactory() : this(null)
{
}

JsonSerializerInternal? _defaultSerializer;
JsonSerializerInternal DefaultSerializer
=> LazyInitializer.EnsureInitialized(ref _defaultSerializer, () => new JsonSerializerInternal(_resolver, _indent))!;

static JsonSerializerFactory? _default;
public static JsonSerializerFactory Default
=> LazyInitializer.EnsureInitialized(ref _default)!;

internal JsonSerializerInternal GetSerializerInternal(IJsonSerializationOptions? options = null, bool caseSensitive = false)
{
if (caseSensitive)
throw new NotSupportedException("Utf8Json does not support case-sensitive deserialization.");

if (options == null)
return DefaultSerializer;

if (options.CamelCaseKeys == true)
throw new NotSupportedException("Utf8Json does not support camel casing keys.");

var omitNull = options.OmitNull == true || _resolver == StandardResolver.ExcludeNull || _resolver == StandardResolver.ExcludeNullCamelCase;
var camelCase = options.CamelCaseProperties == true || _resolver == StandardResolver.CamelCase || _resolver == StandardResolver.ExcludeNullCamelCase;

return camelCase
? new JsonSerializerInternal(omitNull ? StandardResolver.ExcludeNullCamelCase : StandardResolver.CamelCase, options.Indent ?? _indent)
: new JsonSerializerInternal(omitNull ? StandardResolver.ExcludeNull : StandardResolver.Default, options.Indent ?? _indent);
}

public IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);

public IJsonObjectSerializer GetObjectSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);
}

JsonSerializerInternal? _defaultSerializer;
JsonSerializerInternal DefaultSerializer
=> LazyInitializer.EnsureInitialized(ref _defaultSerializer, () => new JsonSerializerInternal(_resolver, _indent))!;

static JsonSerializerFactory? _default;

/// <summary>
/// The default instance (with default settings) of this <see cref="JsonSerializerFactory"/>.
/// </summary>
public static JsonSerializerFactory Default
=> LazyInitializer.EnsureInitialized(ref _default)!;

internal JsonSerializerInternal GetSerializerInternal(IJsonSerializationOptions? options = null, bool caseSensitive = false)
{
if (caseSensitive)
throw new NotSupportedException("Utf8Json does not support case-sensitive deserialization.");

if (options is null)
return DefaultSerializer;

if (options.CamelCaseKeys == true)
throw new NotSupportedException("Utf8Json does not support camel casing keys.");

var omitNull = options.OmitNull == true || _resolver == StandardResolver.ExcludeNull || _resolver == StandardResolver.ExcludeNullCamelCase;
var camelCase = options.CamelCaseProperties == true || _resolver == StandardResolver.CamelCase || _resolver == StandardResolver.ExcludeNullCamelCase;

return camelCase
? new JsonSerializerInternal(omitNull ? StandardResolver.ExcludeNullCamelCase : StandardResolver.CamelCase, options.Indent ?? _indent)
: new JsonSerializerInternal(omitNull ? StandardResolver.ExcludeNull : StandardResolver.Default, options.Indent ?? _indent);
}

/// <summary>
/// Returns an <see cref="IJsonSerializer"/>.
/// </summary>
public IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);

/// <summary>
/// Returns an <see cref="IJsonObjectSerializer"/>.
/// </summary>
public IJsonObjectSerializer GetObjectSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false)
=> GetSerializerInternal(options, caseSensitive);
}
96 changes: 47 additions & 49 deletions Open.Serialization.Json.Utf8Json/JsonSerializerInternal.cs
Original file line number Diff line number Diff line change
@@ -4,67 +4,65 @@
using System.Threading.Tasks;
using Utf8Json;

namespace Open.Serialization.Json.Utf8Json
namespace Open.Serialization.Json.Utf8Json;

internal class JsonSerializerInternal : JsonObjectSerializerBase, IJsonSerializer
{
internal class JsonSerializerInternal : JsonObjectSerializerBase, IJsonSerializer
readonly IJsonFormatterResolver _resolver;
readonly bool _indent;
internal JsonSerializerInternal(IJsonFormatterResolver resolver, bool indent)
{
readonly IJsonFormatterResolver _resolver;
readonly bool _indent;
internal JsonSerializerInternal(IJsonFormatterResolver resolver, bool indent)
{
_resolver = resolver;
_indent = indent;
}

public override T Deserialize<T>(string? value)
=> JsonSerializer.Deserialize<T>(value, _resolver);
_resolver = resolver;
_indent = indent;
}

public new Task<T> DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return JsonSerializer.DeserializeAsync<T>(stream, _resolver);
}
public override T Deserialize<T>(string value)
=> JsonSerializer.Deserialize<T>(value, _resolver);

ValueTask<T> IDeserializeAsync.DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken)
=> new ValueTask<T>(DeserializeAsync<T>(stream, cancellationToken));
public new Task<T> DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return JsonSerializer.DeserializeAsync<T>(stream, _resolver);
}

public override string Serialize<T>(T item)
{
var json = JsonSerializer.ToJsonString(item, _resolver);
return _indent ? JsonSerializer.PrettyPrint(json) : json;
}
ValueTask<T> IDeserializeAsync.DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken)
=> new(DeserializeAsync<T>(stream, cancellationToken));

public new Task SerializeAsync<T>(Stream stream, T item, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
if (!_indent) return JsonSerializer.SerializeAsync(stream, item);
public override string Serialize<T>(T item)
{
var json = JsonSerializer.ToJsonString(item, _resolver);
return _indent ? JsonSerializer.PrettyPrint(json) : json;
}

var result = JsonSerializer.PrettyPrintByteArray(JsonSerializer.Serialize(item));
return stream.WriteAsync(result, 0, result.Length, cancellationToken);
}
public new Task SerializeAsync<T>(Stream stream, T item, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
if (!_indent) return JsonSerializer.SerializeAsync(stream, item);

ValueTask ISerializeAsync.SerializeAsync<T>(Stream stream, T item, CancellationToken cancellationToken)
=> new ValueTask(SerializeAsync(stream, item, cancellationToken));
var result = JsonSerializer.PrettyPrintByteArray(JsonSerializer.Serialize(item));
return stream.WriteAsync(result, 0, result.Length, cancellationToken);
}

public override object? Deserialize(string? value, Type type)
=> JsonSerializer.NonGeneric.Deserialize(type, value, _resolver);
ValueTask ISerializeAsync.SerializeAsync<T>(Stream stream, T item, CancellationToken cancellationToken)
=> new(SerializeAsync(stream, item, cancellationToken));

public override string? Serialize(object? item, Type type)
{
var json = JsonSerializer.NonGeneric.ToJsonString(type, item, _resolver);
return _indent ? JsonSerializer.PrettyPrint(json) : json;
}
public override object? Deserialize(string value, Type type)
=> JsonSerializer.NonGeneric.Deserialize(type, value, _resolver);

public override ValueTask<object?> DeserializeAsync(Stream source, Type type, CancellationToken cancellationToken = default)
=> new ValueTask<object?>(JsonSerializer.NonGeneric.DeserializeAsync(type, source, _resolver));
public override string Serialize(object? item, Type type)
{
var json = JsonSerializer.NonGeneric.ToJsonString(type, item, _resolver);
return _indent ? JsonSerializer.PrettyPrint(json) : json;
}

public override ValueTask SerializeAsync(Stream target, object? item, Type type, CancellationToken cancellationToken = default)
=> _indent
? base.SerializeAsync(target, item, type, cancellationToken)
: new ValueTask(JsonSerializer.NonGeneric.SerializeAsync(type, target, item, _resolver));
public override ValueTask<object?> DeserializeAsync(Stream source, Type type, CancellationToken cancellationToken = default)
=> new(JsonSerializer.NonGeneric.DeserializeAsync(type, source, _resolver));

public new JsonSerializer<T> Cast<T>()
=> new JsonSerializer<T>(Deserialize<T>, Serialize, ((IDeserializeAsync)this).DeserializeAsync<T>, ((ISerializeAsync)this).SerializeAsync);
public override ValueTask SerializeAsync(Stream target, object? item, Type type, CancellationToken cancellationToken = default)
=> _indent
? base.SerializeAsync(target, item, type, cancellationToken)
: new ValueTask(JsonSerializer.NonGeneric.SerializeAsync(type, target, item, _resolver));

}
public new JsonSerializer<T> Cast<T>()
=> new(Deserialize<T>, Serialize, ((IDeserializeAsync)this).DeserializeAsync<T>, ((ISerializeAsync)this).SerializeAsync);
}
Original file line number Diff line number Diff line change
@@ -4,31 +4,50 @@
<TargetFrameworks>netstandard2.0; netstandard2.1</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<Authors>electricessence</Authors>
<Company>electricessence</Company>
<Description>
Extentions and DI utilities for Utf8Json.
Extentions and DI utilities for Utf8Json.

Part of the "Open" set of libraries.
Part of the "Open" set of libraries.
</Description>
<Copyright>https://github.com/electricessence/Open.Serialization/blob/master/LICENSE</Copyright>
<PackageTags>serialization;json;utf8json</PackageTags>
<Copyright>© electricessence (Oren F.) All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/electricessence/Open.Serialization</PackageProjectUrl>
<RepositoryUrl>https://github.com/electricessence/Open.Serialization</RepositoryUrl>
<PackageProjectUrl>https://github.com/Open-NET-Libraries/Open.Serialization</PackageProjectUrl>
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Serialization</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>serialization json utf8json</PackageTags>
<Version>2.2.4</Version>
<Nullable>enable</Nullable>
<Version>3.0.0</Version>
<PackageReleaseNotes></PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageIcon>logo.png</PackageIcon>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
<PackageReference Include="Open.Serialization.Json" Version="2.2.2" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Utf8Json" Version="1.3.7" />
</ItemGroup>

<ItemGroup>
<None Include="..\logo.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Open.Serialization.Json\Open.Serialization.Json.csproj" />
</ItemGroup>

</Project>
138 changes: 83 additions & 55 deletions Open.Serialization.Json.Utf8Json/SerializationExtensions.cs
Original file line number Diff line number Diff line change
@@ -2,70 +2,98 @@
using System;
using Utf8Json;

namespace Open.Serialization.Json.Utf8Json
namespace Open.Serialization.Json.Utf8Json;

/// <summary>
/// Extensions for Utf8Json serialization with Open.Serialization.Json.
/// </summary>
public static class SerializationExtensions
{
public static class SerializationExtensions
/// <summary>
/// Adds a generic serializer <see cref="IJsonSerializer"/>and non-generic <see cref="IJsonObjectSerializer"/> to the service collection.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="options">The options overrides.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddJsonSerializer(this IServiceCollection services, IJsonSerializationOptions? options = null)
{
/// <summary>
/// Adds a generic serializer (<code>IJsonSerializer</code>) and non-generic (<code>IJsonObjectSerializer</code>) to the service collection.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="options">The options overrides.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddJsonSerializer(this IServiceCollection services, IJsonSerializationOptions? options = null)
{
var factory = JsonSerializerFactory.Default;
var serializer = factory.GetSerializerInternal(options);
services.AddSingleton<IJsonSerializer>(serializer);
services.AddSingleton<IJsonObjectSerializer>(serializer);
return services;
}
var factory = JsonSerializerFactory.Default;
var serializer = factory.GetSerializerInternal(options);
services.AddSingleton<IJsonSerializer>(serializer);
services.AddSingleton<IJsonObjectSerializer>(serializer);
return services;
}

/// <summary>
/// Adds a generic serializer (<code>IJsonSerializer<typeparamref name="T"/></code>) to the service collection.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="options">The options overrides.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddJsonSerializer<T>(this IServiceCollection services, IJsonSerializationOptions? options = null)
{
var factory = JsonSerializerFactory.Default;
services.AddSingleton<IJsonSerializer<T>>(factory.GetSerializerInternal(options).Cast<T>());
return services;
}
/// <summary>
/// Adds a generic serializer <see cref="IJsonSerializer{T}"/> to the service collection.
/// </summary>
/// <inheritdoc cref="AddJsonSerializer(IServiceCollection, IJsonSerializationOptions?)"/>
public static IServiceCollection AddJsonSerializer<T>(this IServiceCollection services, IJsonSerializationOptions? options = null)
{
var factory = JsonSerializerFactory.Default;
services.AddSingleton<IJsonSerializer<T>>(factory.GetSerializerInternal(options).Cast<T>());
return services;
}

public static Func<string?, T> GetDeserialize<T>(this IJsonFormatterResolver options)
=> json => JsonSerializer.Deserialize<T>(json, options);
/// <summary>
/// Returns a delegate for deserializing a <see cref="string"/> to <typeparamref name="T"/>.
/// </summary>
public static Func<string, T> GetDeserialize<T>(this IJsonFormatterResolver options)
=> json => JsonSerializer.Deserialize<T>(json, options);

public static Func<T, string?> GetSerialize<T>(this IJsonFormatterResolver options, bool indent = false)
=> item =>
{
var result = JsonSerializer.ToJsonString(item, options);
return indent ? JsonSerializer.PrettyPrint(result) : result;
};
/// <summary>
/// Returns a delegate for serializing <typeparamref name="T"/> to a <see cref="string"/>.
/// </summary>
public static Func<T, string> GetSerialize<T>(this IJsonFormatterResolver options, bool indent = false)
=> item =>
{
var result = JsonSerializer.ToJsonString(item, options);
return indent ? JsonSerializer.PrettyPrint(result) : result;
};

public static Func<object?, string?> GetSerialize(this IJsonFormatterResolver options, bool indent = false)
=> item =>
{
var result = JsonSerializer.ToJsonString(item, options);
return indent ? JsonSerializer.PrettyPrint(result) : result;
};
/// <summary>
/// Returns a delegate for serializing <see cref="object"/> to <see cref="string"/>.
/// </summary>
public static Func<object?, string> GetSerialize(this IJsonFormatterResolver options, bool indent = false)
=> item =>
{
var result = JsonSerializer.ToJsonString(item, options);
return indent ? JsonSerializer.PrettyPrint(result) : result;
};

public static IJsonSerializer GetSerializer(this IJsonFormatterResolver options, bool indent = false)
=> new JsonSerializerInternal(options, indent);
/// <summary>
/// Returns an <see cref="IJsonSerializer"/> with the providied options.
/// </summary>
public static IJsonSerializer GetSerializer(this IJsonFormatterResolver options, bool indent = false)
=> new JsonSerializerInternal(options, indent);

public static IJsonSerializer<T> GetSerializer<T>(this IJsonFormatterResolver options, bool indent = false)
=> new JsonSerializerInternal(options, indent).Cast<T>();
/// <summary>
/// Returns an <see cref="IJsonSerializer{T}"/> with the providied options.
/// </summary>
public static IJsonSerializer<T> GetSerializer<T>(this IJsonFormatterResolver options, bool indent = false)
=> new JsonSerializerInternal(options, indent).Cast<T>();

public static IJsonSerializerFactory GetSerializerFactory(this IJsonFormatterResolver options)
=> new JsonSerializerFactory(options);
/// <summary>
/// Returns an <see cref="IJsonSerializerFactory"/> with the providied options.
/// </summary>
public static IJsonSerializerFactory GetSerializerFactory(this IJsonFormatterResolver options)
=> new JsonSerializerFactory(options);

public static string? Serialize<TValue>(this IJsonFormatterResolver options, TValue value)
=> JsonSerializer.ToJsonString(value, options);
public static string? Serialize(this IJsonFormatterResolver options, object? value)
=> JsonSerializer.ToJsonString(value, options);
public static TValue Deserialize<TValue>(this IJsonFormatterResolver options, string? value)
=> JsonSerializer.Deserialize<TValue>(value, options);
/// <summary>
/// Serializes a <typeparamref name="TValue"/> to a <see cref="string"/> using the provided options.
/// </summary>
public static string Serialize<TValue>(this IJsonFormatterResolver options, TValue value)
=> JsonSerializer.ToJsonString(value, options);

}
/// <summary>
/// Serializes an <see cref="object"/> to a <see cref="string"/> using the provided options.
/// </summary>
public static string Serialize(this IJsonFormatterResolver options, object? value)
=> JsonSerializer.ToJsonString(value, options);

/// <summary>
/// Deserializes a <see cref="string"/> to <typeparamref name="TValue"/> using the provided options.
/// </summary>
public static TValue Deserialize<TValue>(this IJsonFormatterResolver options, string value)
=> JsonSerializer.Deserialize<TValue>(value, options);
}
9 changes: 4 additions & 5 deletions Open.Serialization.Json/IJsonAsyncObjectSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonAsyncObjectSerializer : IAsyncObjectSerializer
{
/// <inheritdoc />
public interface IJsonAsyncObjectSerializer : IAsyncObjectSerializer
{
}
}
17 changes: 8 additions & 9 deletions Open.Serialization.Json/IJsonAsyncSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonAsyncSerializer : IAsyncSerializer
{
/// <inheritdoc />
public interface IJsonAsyncSerializer : IAsyncSerializer
{
}
}

/// <inheritdoc />
public interface IJsonAsyncSerializer<T> : IAsyncSerializer<T>
{
}
/// <inheritdoc />
public interface IJsonAsyncSerializer<T> : IAsyncSerializer<T>
{
}
17 changes: 8 additions & 9 deletions Open.Serialization.Json/IJsonDeserialize.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonDeserialize : IDeserialize
{
/// <inheritdoc />
public interface IJsonDeserialize : IDeserialize
{
}
}

/// <inheritdoc />
public interface IJsonDeserialize<out T> : IDeserialize<T>
{
}
/// <inheritdoc />
public interface IJsonDeserialize<out T> : IDeserialize<T>
{
}
17 changes: 8 additions & 9 deletions Open.Serialization.Json/IJsonDeserializeAsync.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonDeserializeAsync : IDeserializeAsync
{
/// <inheritdoc />
public interface IJsonDeserializeAsync : IDeserializeAsync
{
}
}

/// <inheritdoc />
public interface IJsonDeserializeAsync<T> : IDeserializeAsync<T>
{
}
/// <inheritdoc />
public interface IJsonDeserializeAsync<T> : IDeserializeAsync<T>
{
}
9 changes: 4 additions & 5 deletions Open.Serialization.Json/IJsonDeserializeObject.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonDeserializeObject : IDeserializeObject
{
/// <inheritdoc />
public interface IJsonDeserializeObject : IDeserializeObject
{
}
}
9 changes: 4 additions & 5 deletions Open.Serialization.Json/IJsonDeserializeObjectAsync.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonDeserializeObjectAsync : IDeserializeObjectAsync
{
/// <inheritdoc />
public interface IJsonDeserializeObjectAsync : IDeserializeObjectAsync
{
}
}
41 changes: 20 additions & 21 deletions Open.Serialization.Json/IJsonObjectSerializationFactory.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <summary>
/// Factory for generating JSON serializers and deserializers.
/// </summary>
public interface IJsonObjectSerializationFactory
{
/// <summary>
/// Factory for generating JSON serializers and deserializers.
/// Returns the requested deserializer
/// </summary>
public interface IJsonObjectSerializationFactory
{
/// <summary>
/// Returns the requested deserializer
/// </summary>
IJsonDeserializeObject GetDeserializer(bool caseSensitive = false);
IJsonDeserializeObject GetDeserializer(bool caseSensitive = false);

/// <summary>
/// Returns the requested deserializer
/// </summary>
IJsonDeserializeObjectAsync GetAsyncDeserializer(bool caseSensitive = false);
/// <summary>
/// Returns the requested deserializer
/// </summary>
IJsonDeserializeObjectAsync GetAsyncDeserializer(bool caseSensitive = false);

/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonSerializeObject GetSerializer(IJsonSerializationOptions? options = null);
/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonSerializeObject GetSerializer(IJsonSerializationOptions? options = null);

/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonSerializeObjectAsync GetAsyncSerializer(IJsonSerializationOptions? options = null);
}
/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonSerializeObjectAsync GetAsyncSerializer(IJsonSerializationOptions? options = null);
}
13 changes: 6 additions & 7 deletions Open.Serialization.Json/IJsonObjectSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
namespace Open.Serialization.Json
{
// Provided as a means of specificity when setting up DI.
namespace Open.Serialization.Json;

// Provided as a means of specificity when setting up DI.

/// <inheritdoc />
public interface IJsonObjectSerializer : IObjectSerializer, IJsonSerializeObject, IJsonDeserializeObject, IJsonDeserializeObjectAsync, IJsonSerializeObjectAsync
{
}
/// <inheritdoc />
public interface IJsonObjectSerializer : IObjectSerializer, IJsonSerializeObject, IJsonDeserializeObject, IJsonDeserializeObjectAsync, IJsonSerializeObjectAsync
{
}
17 changes: 8 additions & 9 deletions Open.Serialization.Json/IJsonObjectSerializerFactory.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <summary>
/// Factory for generating a JSON serializer/deserializer.
/// </summary>
public interface IJsonObjectSerializerFactory
{
/// <summary>
/// Factory for generating a JSON serializer/deserializer.
/// Returns the requested serializer
/// </summary>
public interface IJsonObjectSerializerFactory
{
/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonObjectSerializer GetObjectSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false);
}
IJsonObjectSerializer GetObjectSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false);
}
41 changes: 20 additions & 21 deletions Open.Serialization.Json/IJsonSerializationFactory.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <summary>
/// Factory for generating JSON generic serializers and deserializers.
/// </summary>
public interface IJsonSerializationFactory
{
/// <summary>
/// Factory for generating JSON generic serializers and deserializers.
/// Returns the requested deserializer
/// </summary>
public interface IJsonSerializationFactory
{
/// <summary>
/// Returns the requested deserializer
/// </summary>
IJsonDeserialize GetDeserializer(bool caseSensitive = false);
IJsonDeserialize GetDeserializer(bool caseSensitive = false);

/// <summary>
/// Returns the requested deserializer
/// </summary>
IJsonDeserializeAsync GetAsyncDeserializer(bool caseSensitive = false);
/// <summary>
/// Returns the requested deserializer
/// </summary>
IJsonDeserializeAsync GetAsyncDeserializer(bool caseSensitive = false);

/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonSerialize GetSerializer(IJsonSerializationOptions? options = null);
/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonSerialize GetSerializer(IJsonSerializationOptions? options = null);

/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonSerializeAsync GetAsyncSerializer(IJsonSerializationOptions? options = null);
}
/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonSerializeAsync GetAsyncSerializer(IJsonSerializationOptions? options = null);
}
41 changes: 20 additions & 21 deletions Open.Serialization.Json/IJsonSerializationOptions.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <summary>
/// Basic properties needed for modifying how JSON is written.
/// </summary>
public interface IJsonSerializationOptions
{
/// <summary>
/// Basic properties needed for modifying how JSON is written.
/// If true the serializer will format the JSON properties camelCase.
/// </summary>
public interface IJsonSerializationOptions
{
/// <summary>
/// If true the serializer will format the JSON properties camelCase.
/// </summary>
bool? CamelCaseProperties { get; }
bool? CamelCaseProperties { get; }

/// <summary>
/// If true the serializer will format the JSON map keys camelCase.
/// </summary>
bool? CamelCaseKeys { get; }
/// <summary>
/// If true the serializer will format the JSON map keys camelCase.
/// </summary>
bool? CamelCaseKeys { get; }

/// <summary>
/// If true the serializer will omit map entries with null values.
/// </summary>
bool? OmitNull { get; }
/// <summary>
/// If true the serializer will omit map entries with null values.
/// </summary>
bool? OmitNull { get; }

/// <summary>
/// If true the serializer will format the JSON multi-line indented.
/// </summary>
bool? Indent { get; }
}
/// <summary>
/// If true the serializer will format the JSON multi-line indented.
/// </summary>
bool? Indent { get; }
}
17 changes: 8 additions & 9 deletions Open.Serialization.Json/IJsonSerialize.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonSerialize : ISerialize
{
/// <inheritdoc />
public interface IJsonSerialize : ISerialize
{
}
}

/// <inheritdoc />
public interface IJsonSerialize<in T> : ISerialize<T>
{
}
/// <inheritdoc />
public interface IJsonSerialize<in T> : ISerialize<T>
{
}
17 changes: 8 additions & 9 deletions Open.Serialization.Json/IJsonSerializeAsync.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonSerializeAsync : ISerializeAsync
{
/// <inheritdoc />
public interface IJsonSerializeAsync : ISerializeAsync
{
}
}

/// <inheritdoc />
public interface IJsonSerializeAsync<in T> : ISerializeAsync<T>
{
}
/// <inheritdoc />
public interface IJsonSerializeAsync<in T> : ISerializeAsync<T>
{
}
9 changes: 4 additions & 5 deletions Open.Serialization.Json/IJsonSerializeObject.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonSerializeObject : ISerializeObject
{
/// <inheritdoc />
public interface IJsonSerializeObject : ISerializeObject
{
}
}
9 changes: 4 additions & 5 deletions Open.Serialization.Json/IJsonSerializeObjectAsync.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonSerializeObjectAsync : ISerializeObjectAsync
{
/// <inheritdoc />
public interface IJsonSerializeObjectAsync : ISerializeObjectAsync
{
}
}
21 changes: 10 additions & 11 deletions Open.Serialization.Json/IJsonSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
namespace Open.Serialization.Json
{
// Provided as a means of specificity when setting up DI.
namespace Open.Serialization.Json;

/// <inheritdoc />
public interface IJsonSerializer : ISerializer, IJsonSerialize, IJsonDeserialize, IJsonDeserializeAsync, IJsonSerializeAsync
{
}
// Provided as a means of specificity when setting up DI.

/// <inheritdoc />
public interface IJsonSerializer<T> : ISerializer<T>, IJsonSerialize<T>, IJsonDeserialize<T>, IJsonDeserializeAsync<T>, IJsonSerializeAsync<T>
{
}
/// <inheritdoc />
public interface IJsonSerializer : ISerializer, IJsonSerialize, IJsonDeserialize, IJsonDeserializeAsync, IJsonSerializeAsync
{
}

/// <inheritdoc />
public interface IJsonSerializer<T> : ISerializer<T>, IJsonSerialize<T>, IJsonDeserialize<T>, IJsonDeserializeAsync<T>, IJsonSerializeAsync<T>
{
}
17 changes: 8 additions & 9 deletions Open.Serialization.Json/IJsonSerializerFactory.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <summary>
/// Factory for generating a JSON generic serializer/deserializer.
/// </summary>
public interface IJsonSerializerFactory
{
/// <summary>
/// Factory for generating a JSON generic serializer/deserializer.
/// Returns the requested serializer
/// </summary>
public interface IJsonSerializerFactory
{
/// <summary>
/// Returns the requested serializer
/// </summary>
IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false);
}
IJsonSerializer GetSerializer(IJsonSerializationOptions? options = null, bool caseSensitive = false);
}
21 changes: 10 additions & 11 deletions Open.Serialization.Json/JsonObjectSerializer.cs
Original file line number Diff line number Diff line change
@@ -3,19 +3,18 @@
using System.Threading;
using System.Threading.Tasks;

namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public class JsonObjectSerializer : ObjectSerializer, IJsonObjectSerializer, IJsonAsyncObjectSerializer
{
/// <inheritdoc />
public class JsonObjectSerializer : ObjectSerializer, IJsonObjectSerializer, IJsonAsyncObjectSerializer
public JsonObjectSerializer(
Func<string, Type, object?>? deserializer,
Func<object?, Type, string>? serializer = null,
Func<Stream, Type, CancellationToken, ValueTask<object?>>? deserializerAsync = null,
Func<Stream, object?, Type, CancellationToken, ValueTask>? serializerAsync = null)
: base(deserializer, serializer, deserializerAsync, serializerAsync)
{
/// <inheritdoc />
public JsonObjectSerializer(
Func<string?, Type, object?>? deserializer,
Func<object?, Type, string?>? serializer = null,
Func<Stream, Type, CancellationToken, ValueTask<object?>>? deserializerAsync = null,
Func<Stream, object?, Type, CancellationToken, ValueTask>? serializerAsync = null)
: base(deserializer, serializer, deserializerAsync, serializerAsync)
{
}
}
}
9 changes: 4 additions & 5 deletions Open.Serialization.Json/JsonObjectSerializerBase.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public abstract class JsonObjectSerializerBase : ObjectSerializerBase, IJsonObjectSerializer, IJsonAsyncObjectSerializer
{
/// <inheritdoc />
public abstract class JsonObjectSerializerBase : ObjectSerializerBase, IJsonObjectSerializer, IJsonAsyncObjectSerializer
{
}
}
23 changes: 11 additions & 12 deletions Open.Serialization.Json/JsonSerializationOptions.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public class JsonSerializationOptions : IJsonSerializationOptions
{
/// <inheritdoc />
public class JsonSerializationOptions : IJsonSerializationOptions
{
/// <inheritdoc />
public bool? CamelCaseProperties { get; set; }
public bool? CamelCaseProperties { get; set; }

/// <inheritdoc />
public bool? CamelCaseKeys { get; set; }
/// <inheritdoc />
public bool? CamelCaseKeys { get; set; }

/// <inheritdoc />
public bool? OmitNull { get; set; }
/// <inheritdoc />
public bool? OmitNull { get; set; }

/// <inheritdoc />
public bool? Indent { get; set; }
}
/// <inheritdoc />
public bool? Indent { get; set; }
}
21 changes: 10 additions & 11 deletions Open.Serialization.Json/JsonSerializer.cs
Original file line number Diff line number Diff line change
@@ -3,19 +3,18 @@
using System.Threading;
using System.Threading.Tasks;

namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public class JsonSerializer<T> : Serializer<T>, IJsonSerializer<T>, IJsonAsyncSerializer<T>
{
/// <inheritdoc />
public class JsonSerializer<T> : Serializer<T>, IJsonSerializer<T>, IJsonAsyncSerializer<T>
public JsonSerializer(
Func<string, T>? deserializer,
Func<T, string>? serializer = null,
Func<Stream, CancellationToken, ValueTask<T>>? deserializerAsync = null,
Func<Stream, T, CancellationToken, ValueTask>? serializerAsync = null)
: base(deserializer, serializer, deserializerAsync, serializerAsync)
{
/// <inheritdoc />
public JsonSerializer(
Func<string?, T>? deserializer,
Func<T, string?>? serializer = null,
Func<Stream, CancellationToken, ValueTask<T>>? deserializerAsync = null,
Func<Stream, T, CancellationToken, ValueTask>? serializerAsync = null)
: base(deserializer, serializer, deserializerAsync, serializerAsync)
{
}
}
}
23 changes: 11 additions & 12 deletions Open.Serialization.Json/JsonSerializerBase.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
namespace Open.Serialization.Json
namespace Open.Serialization.Json;

/// <inheritdoc />
public abstract class JsonSerializerBase : SerializerBase, IJsonSerializer, IJsonAsyncSerializer
{
/// <inheritdoc />
public abstract class JsonSerializerBase : SerializerBase, IJsonSerializer, IJsonAsyncSerializer
{
/// <inheritdoc cref="SerializerBase.Cast{T}" />
public new JsonSerializer<T> Cast<T>()
=> new JsonSerializer<T>(Deserialize<T>, Serialize, DeserializeAsync<T>, SerializeAsync);
}
/// <inheritdoc cref="SerializerBase.Cast{T}" />
public new JsonSerializer<T> Cast<T>()
=> new(Deserialize<T>, Serialize, DeserializeAsync<T>, SerializeAsync);
}

/// <inheritdoc />
public abstract class JsonSerializerBase<T> : SerializerBase<T>, IJsonSerializer<T>, IJsonAsyncSerializer<T>
{
}
/// <inheritdoc />
public abstract class JsonSerializerBase<T> : SerializerBase<T>, IJsonSerializer<T>, IJsonAsyncSerializer<T>
{
}
45 changes: 30 additions & 15 deletions Open.Serialization.Json/Open.Serialization.Json.csproj
Original file line number Diff line number Diff line change
@@ -4,33 +4,48 @@
<TargetFrameworks>netstandard2.0; netstandard2.1</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<Authors>electricessence</Authors>
<Company>electricessence</Company>
<Description>
DI/IoC agnostic interfaces for injecting any JSON serialization library.

Part of the "Open" set of libraries.
</Description>
<Copyright>https://github.com/electricessence/Open.Serialization/blob/master/LICENSE</Copyright>
<PackageTags>serialization;json</PackageTags>
<Copyright>© electricessence (Oren F.) All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/electricessence/Open.Serialization</PackageProjectUrl>
<RepositoryUrl>https://github.com/electricessence/Open.Serialization</RepositoryUrl>
<PackageProjectUrl>https://github.com/Open-NET-Libraries/Open.Serialization</PackageProjectUrl>
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Serialization</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>serialization json</PackageTags>
<Version>2.2.2</Version>
<Nullable>enable</Nullable>
<Version>3.0.0</Version>
<PackageReleaseNotes></PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageIcon>logo.png</PackageIcon>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard2.0|AnyCPU'">
<DocumentationFile>C:\Users\essence\Development\Open\Open.Serialization\Open.Serialization.Json\Open.Serialization.Json.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<None Include="..\logo.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Open.Serialization" Version="2.2.2" />
<ProjectReference Include="..\Open.Serialization\Open.Serialization.csproj" />
</ItemGroup>

</Project>
74 changes: 36 additions & 38 deletions Open.Serialization.Tests/DefaultImplementationTests.cs
Original file line number Diff line number Diff line change
@@ -2,45 +2,43 @@
using System;
using Xunit;

namespace Open.Serialization.Tests
namespace Open.Serialization.Tests;

public static class DefaultImplementationTests
{
public static class DefaultImplementationTests
[global::System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")]
[global::System.Diagnostics.CodeAnalysis.SuppressMessage("Roslynator", "RCS1163:Unused parameter.")]
[global::System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter")]
class A : IDeserializeObject
{
public object Deserialize(string value, Type type) => 1;

public object Deserialize(ReadOnlySpan<char> value, Type type) => 1;
}

[global::System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")]
class B : A
{
public T Deserialize<T>(string _) => default;
}

class C : B, IDeserializeObject
{
}

[Fact]
public static void DefaultImplementation()
{
class A : IDeserializeObject
{
public object Deserialize(string value, Type type)
{
return 1;
}
}

class B : A
{
public T Deserialize<T>(string _)
{
return default;
}
}

class C : B, IDeserializeObject
{

}

[Fact]
public static void DefaultImplementation()
{
IDeserializeObject a1 = new A();
Assert.Equal(1, a1.Deserialize<int>("0"));

var b1 = new B();
Assert.Equal(0, b1.Deserialize<int>("0"));

IDeserializeObject b2 = new B();
Assert.Equal(1, b2.Deserialize<int>("0"));

var c1 = new C();
Assert.Equal(0, c1.Deserialize<int>("0"));
}
IDeserializeObject a1 = new A();
Assert.Equal(1, a1.Deserialize<int>("0"));

var b1 = new B();
Assert.Equal(0, b1.Deserialize<int>("0"));

IDeserializeObject b2 = new B();
Assert.Equal(1, b2.Deserialize<int>("0"));

var c1 = new C();
Assert.Equal(0, c1.Deserialize<int>("0"));
}
}
206 changes: 106 additions & 100 deletions Open.Serialization.Tests/Newtonsoft/JsonExtensionTests.cs
Original file line number Diff line number Diff line change
@@ -2,135 +2,141 @@
using Open.Serialization.Json.Newtonsoft;
using System;
using Xunit;
using FluentAssertions;

namespace Open.Serialization.Tests.Newtonsoft
namespace Open.Serialization.Tests.Newtonsoft;

public static class JsonExtensionTests
{
public static class JsonExtensionTests
[Fact]
public static void ValidateJsonExtensions()
{
[Fact]
public static void ValidateJsonExtensions()
{
{
var basic = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore };
var serializer = basic
.NormalizeDecimals()
.RoundDoubles(2)
.RoundDecimals(2)
.GetSerializer();

var json = serializer.Serialize(SampleModel.Instance);
var obj = serializer.Deserialize<SampleModel>(json);
Assert.NotNull(obj);
}
var basic = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore };
var serializer = basic
.NormalizeDecimals()
.RoundDoubles(2)
.RoundDecimals(2)
.GetSerializer();

var json = serializer.Serialize(SampleModel.Instance);
var obj = serializer.Deserialize<SampleModel>(json);
Assert.NotNull(obj);
}

{
var basic = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore };
var serializer = basic
.RoundDoubles(2)
.RoundDecimals(2)
.GetSerializer();

var json = serializer.Serialize(SampleModel.Instance);
var obj = serializer.Deserialize<SampleModel>(json);
Assert.NotNull(obj);
}
{
var basic = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore };
var serializer = basic
.RoundDoubles(2)
.RoundDecimals(2)
.GetSerializer();

var json = serializer.Serialize(SampleModel.Instance);
var obj = serializer.Deserialize<SampleModel>(json);
Assert.NotNull(obj);
}

{
var basic = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore };
var serializer = basic
.RoundDecimals(2)
.RoundDoubles(2)
.GetSerializer();

var json = serializer.Serialize(SampleModel.Instance);
var obj = serializer.Deserialize<SampleModel>(json);
Assert.NotNull(obj);
}
{
var basic = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore };
var serializer = basic
.RoundDecimals(2)
.RoundDoubles(2)
.GetSerializer();

var json = serializer.Serialize(SampleModel.Instance);
var obj = serializer.Deserialize<SampleModel>(json);
Assert.NotNull(obj);
}
}

[Fact]
public static void ValidateDecimals()
[Fact]
public static void ValidateDecimals()
{
const decimal sample = 0.234567890123456789012345m;
{
const decimal sample = 0.234567890123456789012345m;
{
var serializer = CamelCaseJson
.Default()
.NormalizeDecimals()
.GetSerializer();
var serializer = CamelCaseJson
.Default()
.NormalizeDecimals()
.GetSerializer();

{
var model = new SampleModel
{
var model = new SampleModel
{
DecimalValue1 = sample
};
var json = serializer.Serialize(model);
Assert.Equal(sample, serializer.Deserialize<SampleModel>(json).DecimalValue1);
}
DecimalValue1 = sample
};
var json = serializer.Serialize(model);
Assert.Equal(sample, serializer.Deserialize<SampleModel>(json).DecimalValue1);
}

{
var model = new SampleModel
{
var model = new SampleModel
{
NullableDecimalValue = sample
};
var json = serializer.Serialize(model);
Assert.Equal(sample, serializer.Deserialize<SampleModel>(json).NullableDecimalValue);
}
NullableDecimalValue = sample
};
var json = serializer.Serialize(model);
Assert.Equal(sample, serializer.Deserialize<SampleModel>(json).NullableDecimalValue);
}
}

{
var serializer = CamelCaseJson
.Default()
.RoundDecimals(2)
.GetSerializer();
{
var serializer = CamelCaseJson
.Default()
.RoundDecimals(2)
.GetSerializer();

var sample2 = Math.Round(sample, 2);
var sample2 = Math.Round(sample, 2);

{
var model = new SampleModel
{
var model = new SampleModel
{
DecimalValue1 = sample
};
var json = serializer.Serialize(model);
Assert.Equal(sample2, serializer.Deserialize<SampleModel>(json).DecimalValue1);
}
DecimalValue1 = sample
};
var json = serializer.Serialize(model);
Assert.Equal(sample2, serializer.Deserialize<SampleModel>(json).DecimalValue1);
}

{
var model = new SampleModel
{
var model = new SampleModel
{
NullableDecimalValue = sample
};
var json = serializer.Serialize(model);
Assert.Equal(sample2, serializer.Deserialize<SampleModel>(json).NullableDecimalValue);
}
NullableDecimalValue = sample
};
var json = serializer.Serialize(model);
Assert.Equal(sample2, serializer.Deserialize<SampleModel>(json).NullableDecimalValue);
}
}

{
var serializer = CamelCaseJson
.Default()
.GetSerializer();
{
var serializer = CamelCaseJson
.Default()
.GetSerializer();

var sample3 = 1;
const int sample3 = 1;

{
var model = new SampleModel
{
var model = new SampleModel
{
DecimalValue1 = sample3
};
var json = serializer.Serialize(model);
Assert.Equal(sample3, serializer.Deserialize<SampleModel>(json).DecimalValue1);
}
DecimalValue1 = sample3
};
var json = serializer.Serialize(model);
Assert.Equal(sample3, serializer.Deserialize<SampleModel>(json).DecimalValue1);
}

{
var model = new SampleModel
{
var model = new SampleModel
{
NullableDecimalValue = sample3
};
var json = serializer.Serialize(model);
Assert.Equal(sample3, serializer.Deserialize<SampleModel>(json).NullableDecimalValue);
}
NullableDecimalValue = sample3
};
var json = serializer.Serialize(model);
Assert.Equal(sample3, serializer.Deserialize<SampleModel>(json).NullableDecimalValue);
}

}
}

[Fact]
public static void ValidateNulls()
{
Assert.Throws<ArgumentNullException>(()=>RelaxedJson.Deserialize<double?>(null));
RelaxedJson.Deserialize<double?>("null").Should().BeNull();
Assert.Throws<NullReferenceException>(()=>RelaxedJson.Deserialize<double>("null"));
}
}
Loading