From cacc26d8c0762532e34231cb5a71c5549ce1f9fa Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Sun, 18 Apr 2021 00:06:50 +0200 Subject: [PATCH] Turn Currency into a class --- NodaMoney.sln | 6 ++ .../NodaMoney.Serialization.AspNet.csproj | 8 +-- .../CurrencyJsonConverter.cs | 23 ++++++ .../Extensions.cs | 19 +++++ .../MoneyJsonConverter.cs | 72 +++++++++++++++++++ .../NodaMoney.Serialization.JsonNet.csproj | 12 ++++ src/NodaMoney/Currency.cs | 53 +++++++++----- src/NodaMoney/CurrencyBuilder.cs | 3 + src/NodaMoney/CurrencyRegistry.cs | 2 +- src/NodaMoney/Money.Parsable.cs | 10 +++ src/NodaMoney/Money.Serializable.cs | 16 +++-- src/NodaMoney/Money.cs | 13 ++-- src/NodaMoney/NodaMoney.csproj | 14 ++-- tests/NodaMoney.Tests/NodaMoney.Tests.csproj | 1 + .../Serialization/MoneySerializableSpec.cs | 19 +++-- 15 files changed, 216 insertions(+), 55 deletions(-) create mode 100644 src/NodaMoney.Serialization.JsonNet/CurrencyJsonConverter.cs create mode 100644 src/NodaMoney.Serialization.JsonNet/Extensions.cs create mode 100644 src/NodaMoney.Serialization.JsonNet/MoneyJsonConverter.cs create mode 100644 src/NodaMoney.Serialization.JsonNet/NodaMoney.Serialization.JsonNet.csproj diff --git a/NodaMoney.sln b/NodaMoney.sln index b755496..db16a57 100644 --- a/NodaMoney.sln +++ b/NodaMoney.sln @@ -20,6 +20,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NodaMoney.Tests", "tests\No EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{324D342A-2588-4422-AFB1-FA0359EE825D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NodaMoney.Serialization.JsonNet", "src\NodaMoney.Serialization.JsonNet\NodaMoney.Serialization.JsonNet.csproj", "{EF8D8552-7B23-4D03-B2A9-03246087BE6B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +43,10 @@ Global {CC01DEB3-F862-49A1-9EB5-50A4E52B82E7}.Release|Any CPU.Build.0 = Release|Any CPU {324D342A-2588-4422-AFB1-FA0359EE825D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {324D342A-2588-4422-AFB1-FA0359EE825D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF8D8552-7B23-4D03-B2A9-03246087BE6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF8D8552-7B23-4D03-B2A9-03246087BE6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF8D8552-7B23-4D03-B2A9-03246087BE6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF8D8552-7B23-4D03-B2A9-03246087BE6B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/NodaMoney.Serialization.AspNet/NodaMoney.Serialization.AspNet.csproj b/src/NodaMoney.Serialization.AspNet/NodaMoney.Serialization.AspNet.csproj index ec5389d..274a2ac 100644 --- a/src/NodaMoney.Serialization.AspNet/NodaMoney.Serialization.AspNet.csproj +++ b/src/NodaMoney.Serialization.AspNet/NodaMoney.Serialization.AspNet.csproj @@ -7,7 +7,7 @@ true NodaMoney.Serialization.AspNet Noda;Money;Currency;ExchangeRate;Serialization - net40;net45 + net45 latest @@ -15,12 +15,6 @@ - - - - - - diff --git a/src/NodaMoney.Serialization.JsonNet/CurrencyJsonConverter.cs b/src/NodaMoney.Serialization.JsonNet/CurrencyJsonConverter.cs new file mode 100644 index 0000000..db9fcef --- /dev/null +++ b/src/NodaMoney.Serialization.JsonNet/CurrencyJsonConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace NodaMoney.Serialization.JsonNet +{ + + public class CurrencyJsonConverter : JsonConverter + { + private static TypeConverter currencyConverter = TypeDescriptor.GetConverter(typeof(Currency)); + public override Currency ReadJson(JsonReader reader, Type objectType, Currency existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var code = reader.Value.ToString(); + return (Currency)currencyConverter.ConvertFromString(code); + } + + public override void WriteJson(JsonWriter writer, Currency value, JsonSerializer serializer) + { + writer.WriteValue(currencyConverter.ConvertToString(value)); + } + } +} diff --git a/src/NodaMoney.Serialization.JsonNet/Extensions.cs b/src/NodaMoney.Serialization.JsonNet/Extensions.cs new file mode 100644 index 0000000..6d34e31 --- /dev/null +++ b/src/NodaMoney.Serialization.JsonNet/Extensions.cs @@ -0,0 +1,19 @@ +using System; +using Newtonsoft.Json; + +namespace NodaMoney.Serialization.JsonNet +{ + public static class Extensions + { + public static JsonSerializerSettings ConfigureForNodaMoney(this JsonSerializerSettings settings) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + settings.Converters.Add(new MoneyJsonConverter()); + settings.Converters.Add(new CurrencyJsonConverter()); + return settings; + } + } +} diff --git a/src/NodaMoney.Serialization.JsonNet/MoneyJsonConverter.cs b/src/NodaMoney.Serialization.JsonNet/MoneyJsonConverter.cs new file mode 100644 index 0000000..68b8da5 --- /dev/null +++ b/src/NodaMoney.Serialization.JsonNet/MoneyJsonConverter.cs @@ -0,0 +1,72 @@ +using System; +using System.ComponentModel; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace NodaMoney.Serialization.JsonNet +{ + public class MoneyJsonConverter : JsonConverter + { + private static TypeConverter currencyConverter = TypeDescriptor.GetConverter(typeof(Currency)); + public override Money ReadJson(JsonReader reader, Type objectType, Money existingValue, bool hasExistingValue, JsonSerializer serializer) + { + decimal? amount = null; + string currencyCode = null; + var amountPropertyName = ResolvePropertyName(serializer, nameof(Money.Amount)); + var currencyPropertyName = ResolvePropertyName(serializer, nameof(Money.Currency)); + + while (reader.Read()) + { + if (reader.TokenType != JsonToken.PropertyName) + { + break; + } + + var propertyName = (string)reader.Value; + + if (!reader.Read()) + { + break; + } + + if (string.Equals(propertyName, amountPropertyName, StringComparison.OrdinalIgnoreCase)) + { + amount = serializer.Deserialize(reader); + } + + if (string.Equals(propertyName, currencyPropertyName, StringComparison.OrdinalIgnoreCase)) + { + currencyCode = serializer.Deserialize(reader); + } + } + + if (amount == null) + { + throw new JsonSerializationException($"Unable to deserialize to {nameof(Money)}, since the property {amountPropertyName} was null or missing"); + } + + if (string.IsNullOrEmpty(currencyCode)) + { + throw new JsonSerializationException($"Unable to deserialize to {nameof(Money)}, since the property {currencyPropertyName} was null or missing"); + } + + return new Money(amount.Value, (Currency)currencyConverter.ConvertFromString(currencyCode)); + } + + public override void WriteJson(JsonWriter writer, Money value, JsonSerializer serializer) + { + writer.WriteStartObject(); + + writer.WritePropertyName(ResolvePropertyName(serializer, nameof(Money.Amount))); + serializer.Serialize(writer, value.Amount); + + writer.WritePropertyName(ResolvePropertyName(serializer, nameof(Money.Currency))); + writer.WriteValue(value.Currency.Code); + + writer.WriteEndObject(); + } + + static string ResolvePropertyName(JsonSerializer serializer, string propertyName) => + (serializer.ContractResolver as DefaultContractResolver)?.GetResolvedPropertyName(propertyName) ?? propertyName; + } +} diff --git a/src/NodaMoney.Serialization.JsonNet/NodaMoney.Serialization.JsonNet.csproj b/src/NodaMoney.Serialization.JsonNet/NodaMoney.Serialization.JsonNet.csproj new file mode 100644 index 0000000..00ae5ab --- /dev/null +++ b/src/NodaMoney.Serialization.JsonNet/NodaMoney.Serialization.JsonNet.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.1;netstandard2.0;netstandard2.1;net45;net461;net5 + + + + + + + + diff --git a/src/NodaMoney/Currency.cs b/src/NodaMoney/Currency.cs index 141b022..b2240d7 100644 --- a/src/NodaMoney/Currency.cs +++ b/src/NodaMoney/Currency.cs @@ -19,15 +19,14 @@ namespace NodaMoney [Serializable] [DebuggerDisplay("{Code}")] [TypeConverter(typeof(CurrencyTypeConverter))] - public struct Currency : IEquatable, IXmlSerializable, ISerializable + public sealed class Currency : IEquatable, IXmlSerializable, ISerializable { /// Gets the currency sign (¤), a character used to denote the generic currency sign, when no currency sign is available. /// See https://en.wikipedia.org/wiki/Currency_sign_(typography). public const string GenericCurrencySign = "¤"; - /// A singleton instance of the currencies registry. - [NonSerialized] - internal static CurrencyRegistry Registry = new CurrencyRegistry(); + /// Gets a singleton instance of the currencies registry. + internal static CurrencyRegistry Registry { get; } = new CurrencyRegistry(); /// Initializes a new instance of the struct. /// The code. @@ -59,7 +58,6 @@ internal Currency(string code, string @namespace = "ISO-4217") /// code or number or englishName or symbol is null. /// DecimalDigits must greater or equal to zero and smaller or equal to 28, or -1 if not applicable. internal Currency(string code, string number, double decimalDigits, string englishName, string symbol, string @namespace = "ISO-4217", DateTime? validTo = null, DateTime? validFrom = null) - : this() { if (string.IsNullOrWhiteSpace(code)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(code)); @@ -78,6 +76,10 @@ internal Currency(string code, string number, double decimalDigits, string engli ValidFrom = validFrom; } + private Currency() + { + } + #pragma warning disable CA1801 // Parameter context of method.ctor is never used. private Currency(SerializationInfo info, StreamingContext context) : this(info.GetString("code"), info.GetString("namespace")) @@ -100,19 +102,19 @@ public static Currency CurrentCurrency } /// Gets the currency symbol. - public string Symbol { get; } + public string Symbol { get; private set; } /// Gets the english name of the currency. - public string EnglishName { get; } + public string EnglishName { get; private set; } /// Gets the three-character (ISO-4217) currency code. - public string Code { get; } + public string Code { get; private set; } /// Gets the numeric (ISO-4217) currency number. - public string Number { get; } + public string Number { get; private set; } /// Gets the namespace of the currency, like ISO-4217. - public string Namespace { get; } + public string Namespace { get; private set; } /// Gets the number of digits after the decimal separator. /// @@ -130,15 +132,15 @@ public static Currency CurrentCurrency /// To represent this in decimal we do the following steps: 5 is 10 to the power of log(5) = 0.69897... ~ 0.7. /// /// - public double DecimalDigits { get; } + public double DecimalDigits { get; private set; } /// Gets the date when the currency is valid from. /// The from date when the currency is valid. - public DateTime? ValidFrom { get; internal set; } + public DateTime? ValidFrom { get; private set; } /// Gets the date when the currency is valid to. /// The to date when the currency is valid. - public DateTime? ValidTo { get; internal set; } + public DateTime? ValidTo { get; private set; } /// Gets the major currency unit. [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Member of Currency type! Implementation can change in the future.")] @@ -164,7 +166,7 @@ public decimal MinorUnit /// The left Currency. /// The right Currency. /// The result of the operator. - public static bool operator ==(Currency left, Currency right) => left.Equals(right); + public static bool operator ==(Currency left, Currency right) => object.Equals(left, right); /// Implements the operator ==. /// The left Currency. @@ -270,6 +272,11 @@ public static Currency FromRegion(string name) [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Calling override method")] public bool Equals(Currency other) { + if (other == null) + { + return false; + } + return Code == other.Code && Namespace == other.Namespace && DecimalDigits == other.DecimalDigits @@ -323,17 +330,25 @@ public bool IsValidOn(DateTime date) /// produced by the method and /// consumed by the method. /// - public XmlSchema GetSchema() => null; + XmlSchema IXmlSerializable.GetSchema() => null; /// Generates an object from its XML representation. /// The stream from which the object is deserialized. /// The value of 'reader' cannot be null. - public void ReadXml(XmlReader reader) + void IXmlSerializable.ReadXml(XmlReader reader) { if (reader == null) throw new ArgumentNullException(nameof(reader)); - this = FromCode(reader["Currency"]); + var fromCode = FromCode(reader["Currency"]); + Code = fromCode.Code; + Namespace = fromCode.Namespace; + DecimalDigits = fromCode.DecimalDigits; + EnglishName = fromCode.EnglishName; + Number = fromCode.Number; + Symbol = fromCode.Symbol; + ValidFrom = fromCode.ValidFrom; + ValidTo = fromCode.ValidTo; } /// @@ -341,7 +356,7 @@ public void ReadXml(XmlReader reader) /// /// The stream to which the object is serialized. /// The value of 'writer' cannot be null. - public void WriteXml(XmlWriter writer) + void IXmlSerializable.WriteXml(XmlWriter writer) { if (writer == null) throw new ArgumentNullException(nameof(writer)); @@ -353,7 +368,7 @@ public void WriteXml(XmlWriter writer) /// The to populate with data. /// The destination (see ) for this serialization. /// The caller does not have the required permission. - public void GetObjectData(SerializationInfo info, StreamingContext context) + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException(nameof(info)); diff --git a/src/NodaMoney/CurrencyBuilder.cs b/src/NodaMoney/CurrencyBuilder.cs index 52265e5..b308f67 100644 --- a/src/NodaMoney/CurrencyBuilder.cs +++ b/src/NodaMoney/CurrencyBuilder.cs @@ -140,6 +140,9 @@ public void Save(string fileName) /// The object whose properties will be used. public void LoadDataFromCurrency(Currency currency) { + if (currency is null) + throw new ArgumentNullException(nameof(currency)); + EnglishName = currency.EnglishName; Symbol = currency.Symbol; ISONumber = currency.Number; diff --git a/src/NodaMoney/CurrencyRegistry.cs b/src/NodaMoney/CurrencyRegistry.cs index a6a4f21..a240768 100644 --- a/src/NodaMoney/CurrencyRegistry.cs +++ b/src/NodaMoney/CurrencyRegistry.cs @@ -44,7 +44,7 @@ public bool TryGet(string code, out Currency currency) } currency = found.FirstOrDefault(); // TODO: If more than one, sort by prio. - return !currency.Equals(default); + return currency != null; } /// Tries the get of the given code and namespace. diff --git a/src/NodaMoney/Money.Parsable.cs b/src/NodaMoney/Money.Parsable.cs index 7f2ebef..e259b35 100644 --- a/src/NodaMoney/Money.Parsable.cs +++ b/src/NodaMoney/Money.Parsable.cs @@ -36,6 +36,8 @@ public static Money Parse(string value, Currency currency) { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); + if (currency is null) + throw new ArgumentNullException(nameof(currency)); return Parse(value, NumberStyles.Currency, GetFormatProvider(currency, null), currency); } @@ -53,6 +55,8 @@ public static Money Parse(string value, NumberStyles style, IFormatProvider prov { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); + if (currency is null) + throw new ArgumentNullException(nameof(currency)); decimal amount = decimal.Parse(value, style, GetFormatProvider(currency, provider)); return new Money(amount, currency); @@ -101,6 +105,9 @@ public static bool TryParse(string value, out Money result) /// See for more info and remarks. public static bool TryParse(string value, Currency currency, out Money result) { + if (currency is null) + throw new ArgumentNullException(nameof(currency)); + return TryParse(value, NumberStyles.Currency, GetFormatProvider(currency, null), currency, out result); } @@ -118,6 +125,9 @@ public static bool TryParse(string value, Currency currency, out Money result) /// See for more info and remarks. public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, Currency currency, out Money result) { + if (currency is null) + throw new ArgumentNullException(nameof(currency)); + bool isParsingSuccessful = decimal.TryParse(value, style, GetFormatProvider(currency, provider), out decimal amount); if (isParsingSuccessful) { diff --git a/src/NodaMoney/Money.Serializable.cs b/src/NodaMoney/Money.Serializable.cs index 404f1cb..6fa8d6a 100644 --- a/src/NodaMoney/Money.Serializable.cs +++ b/src/NodaMoney/Money.Serializable.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Xml; using System.Xml.Schema; @@ -53,8 +54,8 @@ private Money(SerializationInfo info, StreamingContext context) currency = info.GetString("currency"); } - Currency = (Currency)TypeDescriptor.GetConverter(typeof(Currency)).ConvertFromString(currency); - Amount = Round(amount, Currency, MidpointRounding.ToEven); + this.currency = (Currency)TypeDescriptor.GetConverter(typeof(Currency)).ConvertFromString(currency); + this.amount = Round(amount, Currency, MidpointRounding.ToEven); } #pragma warning restore CA1801 // Parameter context of method.ctor is never used. @@ -71,7 +72,7 @@ private Money(SerializationInfo info, StreamingContext context) /// The stream from which the object is deserialized. /// The value of 'reader' cannot be null. /// The xml should have a content element with name Money. - public void ReadXml(XmlReader reader) + void IXmlSerializable.ReadXml(XmlReader reader) { if (reader == null) throw new ArgumentNullException(nameof(reader)); @@ -79,14 +80,15 @@ public void ReadXml(XmlReader reader) if (reader.MoveToContent() != XmlNodeType.Element) throw new SerializationException("Couldn't find content element with name Money!"); - Amount = decimal.Parse(reader["Amount"], CultureInfo.InvariantCulture); - Currency = (Currency)TypeDescriptor.GetConverter(typeof(Currency)).ConvertFromString(reader["Currency"]); + var amount = decimal.Parse(reader["Amount"], CultureInfo.InvariantCulture); + var currency = (Currency)TypeDescriptor.GetConverter(typeof(Currency)).ConvertFromString(reader["Currency"]); + Unsafe.AsRef(this) = new Money(amount, currency); } /// Converts an object into its XML representation. /// The stream to which the object is serialized. /// The value of 'writer' cannot be null. - public void WriteXml(XmlWriter writer) + void IXmlSerializable.WriteXml(XmlWriter writer) { if (writer == null) throw new ArgumentNullException(nameof(writer)); @@ -99,7 +101,7 @@ public void WriteXml(XmlWriter writer) /// The to populate with data. /// The destination (see ) for this serialization. /// The caller does not have the required permission. - public void GetObjectData(SerializationInfo info, StreamingContext context) + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException(nameof(info)); diff --git a/src/NodaMoney/Money.cs b/src/NodaMoney/Money.cs index 38d19d1..f07a5d8 100644 --- a/src/NodaMoney/Money.cs +++ b/src/NodaMoney/Money.cs @@ -12,8 +12,11 @@ namespace NodaMoney /// and ensure that two different currencies cannot be added or subtracted to each other. /// [StructLayout(LayoutKind.Sequential)] - public partial struct Money : IEquatable + public readonly partial struct Money : IEquatable { + private readonly decimal amount; + private readonly Currency currency; + /// Initializes a new instance of the struct, based on the current culture. /// The Amount of money as . /// The amount will be rounded to the number of decimal digits of the specified currency @@ -82,8 +85,8 @@ public Money(decimal amount, string code, MidpointRounding rounding) public Money(decimal amount, Currency currency, MidpointRounding rounding) : this() { - Currency = currency; - Amount = Round(amount, currency, rounding); + this.currency = currency ?? throw new ArgumentNullException(nameof(currency)); + this.amount = Round(amount, currency, rounding); } // int, uint ([CLSCompliant(false)]) // auto-casting to decimal so not needed @@ -220,10 +223,10 @@ public Money(ulong amount, Currency currency) } /// Gets the amount of money. - public decimal Amount { get; private set; } + public readonly decimal Amount { get => amount; } /// Gets the of the money. - public Currency Currency { get; private set; } + public readonly Currency Currency { get => currency; } /// Returns a value indicating whether two instances of are equal. /// A object on the left side. diff --git a/src/NodaMoney/NodaMoney.csproj b/src/NodaMoney/NodaMoney.csproj index c08e150..0d50223 100644 --- a/src/NodaMoney/NodaMoney.csproj +++ b/src/NodaMoney/NodaMoney.csproj @@ -7,9 +7,10 @@ true NodaMoney Noda;Money;Currency;ExchangeRate - netcoreapp3.1;netstandard2.0;netstandard2.1;net40;net461 + netcoreapp3.1;netstandard2.0;netstandard2.1;net45;net461;net5 latest disable + false @@ -36,13 +37,8 @@ all runtime; build; native; contentfiles; analyzers + + 5.0.0 + - - - - - - - - diff --git a/tests/NodaMoney.Tests/NodaMoney.Tests.csproj b/tests/NodaMoney.Tests/NodaMoney.Tests.csproj index b93dedb..b5fa199 100644 --- a/tests/NodaMoney.Tests/NodaMoney.Tests.csproj +++ b/tests/NodaMoney.Tests/NodaMoney.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/tests/NodaMoney.Tests/Serialization/MoneySerializableSpec.cs b/tests/NodaMoney.Tests/Serialization/MoneySerializableSpec.cs index 68b9ab3..640fc5b 100644 --- a/tests/NodaMoney.Tests/Serialization/MoneySerializableSpec.cs +++ b/tests/NodaMoney.Tests/Serialization/MoneySerializableSpec.cs @@ -9,16 +9,19 @@ using System.Diagnostics; using System.Runtime.Serialization.Formatters.Binary; using System.Collections.Generic; +using NodaMoney.Serialization.JsonNet; namespace NodaMoney.Tests.Serialization { public class GivenIWantToDeserializeMoneyWithJsonNetSerializer { + private readonly JsonSerializerSettings settings = new JsonSerializerSettings().ConfigureForNodaMoney(); + [Theory] [ClassData(typeof(ValidJsonTestData))] public void WhenDeserializing_ThenThisShouldSucceed(string json, Money expected) { - var clone = JsonConvert.DeserializeObject(json); + var clone = JsonConvert.DeserializeObject(json, settings); clone.Should().Be(expected); } @@ -27,16 +30,16 @@ public void WhenDeserializing_ThenThisShouldSucceed(string json, Money expected) [ClassData(typeof(InvalidJsonTestData))] public void WhenDeserializingWithInvalidJSON_ThenThisShouldFail(string json) { - Action action = () => JsonConvert.DeserializeObject(json); + Action action = () => JsonConvert.DeserializeObject(json, settings); - action.Should().Throw().WithMessage("Member '*"); + action.Should().Throw(); } [Theory] [ClassData(typeof(NestedJsonTestData))] public void WhenDeserializingWithNested_ThenThisShouldSucceed(string json, Order expected) { - var clone = JsonConvert.DeserializeObject(json); + var clone = JsonConvert.DeserializeObject(json, settings); clone.Should().BeEquivalentTo(expected); clone.Discount.Should().BeNull(); @@ -45,6 +48,8 @@ public void WhenDeserializingWithNested_ThenThisShouldSucceed(string json, Order public class GivenIWantToSerializeMoneyWithJsonNetSerializer { + private readonly JsonSerializerSettings settings = new JsonSerializerSettings().ConfigureForNodaMoney(); + public static IEnumerable TestData => new[] { new object[] { new Money(765.4321m, Currency.FromCode("JPY")) }, @@ -68,9 +73,9 @@ public void WhenSerializingCurrency_ThenThisShouldSucceed(Money money) [MemberData(nameof(TestData))] public void WhenSerializingMoney_ThenThisShouldSucceed(Money money) { - string json = JsonConvert.SerializeObject(money); + string json = JsonConvert.SerializeObject(money, settings); // Console.WriteLine(json); - var clone = JsonConvert.DeserializeObject(json); + var clone = JsonConvert.DeserializeObject(json, settings); clone.Should().Be(money); } @@ -88,7 +93,7 @@ public void WhenSerializingArticle_ThenThisShouldSucceed(Money money) string json = JsonConvert.SerializeObject(order); // Console.WriteLine(json); - var clone = JsonConvert.DeserializeObject(json); + var clone = JsonConvert.DeserializeObject(json, settings); clone.Should().BeEquivalentTo(order); }