From 921f6979cbdc428eb5759acb5d5f6157115eb94e Mon Sep 17 00:00:00 2001 From: Alexander Gorbatov Date: Fri, 22 Dec 2023 00:35:42 +0500 Subject: [PATCH 1/5] Implemented type exclusion and alternative serialization for type. (with tests) --- ObjectPrinting/ObjectPrinter.cs | 11 +-- ObjectPrinting/ObjectPrinting.csproj | 4 +- ObjectPrinting/PrintingConfig.cs | 99 +++++++++++++------ .../Tests/ObjectPrinterAcceptanceTests.cs | 27 ----- ObjectPrinting/Tests/Person.cs | 12 --- ObjectPrintingTests/GlobalUsings.cs | 1 + .../ObjectPrinterAcceptanceTests.cs | 20 ++++ .../ObjectPrintingTests.csproj | 44 +++++++++ ...t_WhenExcludingNonPresentType.verified.txt | 5 + ...ult_WhenExcludingForFinalType.verified.txt | 1 + ...WithExcludedComplexTypeItself.verified.txt | 1 + ...tResult_WithExcludedFinalType.verified.txt | 4 + .../PrintingConfigExcludeTypeTests.cs | 43 ++++++++ ...henNewSerializationIsProvided.verified.txt | 5 + ...xTypeSerializationIsSpecified.verified.txt | 1 + ...lTypeSerializationIsSpecified.verified.txt | 5 + ...nUsingPropertiesOfComplexType.verified.txt | 1 + .../PrintingConfigSerializationTests.cs | 44 +++++++++ ObjectPrintingTests/TestData/Person.cs | 17 ++++ .../TestData/TestDataFactory.cs | 6 ++ Samples/FluentMapper/SetterSpec.cs | 35 +++---- fluent-api.sln | 6 ++ 22 files changed, 298 insertions(+), 94 deletions(-) delete mode 100644 ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs delete mode 100644 ObjectPrinting/Tests/Person.cs create mode 100644 ObjectPrintingTests/GlobalUsings.cs create mode 100644 ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs create mode 100644 ObjectPrintingTests/ObjectPrintingTests.csproj create mode 100644 ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_DoesNotAffectResult_WhenExcludingNonPresentType.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WhenExcludingForFinalType.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedComplexTypeItself.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedFinalType.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigExcludeTypeTests.cs create mode 100644 ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigSerializationTests.cs create mode 100644 ObjectPrintingTests/TestData/Person.cs create mode 100644 ObjectPrintingTests/TestData/TestDataFactory.cs diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index 3c7867c3..caa93e56 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -1,10 +1,9 @@ -namespace ObjectPrinting +namespace ObjectPrinting; + +public static class ObjectPrinter { - public class ObjectPrinter + public static PrintingConfig For() { - public static PrintingConfig For() - { - return new PrintingConfig(); - } + return new PrintingConfig(); } } \ No newline at end of file diff --git a/ObjectPrinting/ObjectPrinting.csproj b/ObjectPrinting/ObjectPrinting.csproj index 1c5eaf1c..da4f5733 100644 --- a/ObjectPrinting/ObjectPrinting.csproj +++ b/ObjectPrinting/ObjectPrinting.csproj @@ -1,8 +1,8 @@  - 8 - netcoreapp3.1 + 11 + net7.0 false diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a9e08211..48d99ba7 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,41 +1,80 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; -namespace ObjectPrinting +namespace ObjectPrinting; + +public class PrintingConfig { - public class PrintingConfig + private readonly HashSet excludedTypes = new(); + private readonly Dictionary> customSerialization = new(); + + private readonly Type[] finalTypes = { - public string PrintToString(TOwner obj) - { - return PrintToString(obj, 0); - } + typeof(int), typeof(double), typeof(float), typeof(string), + typeof(DateTime), typeof(TimeSpan) + }; + + public string PrintToString(TOwner obj) + { + return PrintToString(obj, 0); + } + + public PrintingConfig ExcludeType() + { + excludedTypes.Add(typeof(T)); + return this; + } + + public PrintingConfig WithSerializationForType(Func serializationFunc) + { + var type = typeof(T); + customSerialization[type] = obj => serializationFunc((T) obj); + return this; + } - private string PrintToString(object obj, int nestingLevel) + // private string ApplyCustomSerializationIfPresent(object obj, string initial) + // { + // return customSerialization.TryGetValue(obj.GetType(), out var serialization) + // ? serialization(obj) + // : initial; + // } + + private string PrintToString(object obj, int nestingLevel) + { + if (obj is null) + return "null" + Environment.NewLine; + + if (excludedTypes.Contains(obj.GetType())) + return ""; + + if (customSerialization.TryGetValue(obj.GetType(), out var serialization)) + return serialization(obj) + Environment.NewLine; + + if (finalTypes.Contains(obj.GetType())) + return obj + Environment.NewLine; + + var printedProperties = PrintObjectProperties(obj, nestingLevel); + + return printedProperties; + } + + private string PrintObjectProperties(object obj, int nestingLevel) + { + var identation = new string('\t', nestingLevel + 1); + var objectType = obj.GetType(); + var sb = new StringBuilder().AppendLine(objectType.Name); + + foreach (var propertyInfo in objectType.GetProperties()) { - //TODO apply configurations - if (obj == null) - return "null" + Environment.NewLine; - - var finalTypes = new[] - { - typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) - }; - if (finalTypes.Contains(obj.GetType())) - return obj + Environment.NewLine; - - var identation = new string('\t', nestingLevel + 1); - var sb = new StringBuilder(); - var type = obj.GetType(); - sb.AppendLine(type.Name); - foreach (var propertyInfo in type.GetProperties()) - { - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); - } - return sb.ToString(); + if (excludedTypes.Contains(propertyInfo.PropertyType)) + continue; + + sb.Append(identation + propertyInfo.Name + " = " + + PrintToString(propertyInfo.GetValue(obj), nestingLevel + 1)); } + + return sb.ToString(); } } \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs deleted file mode 100644 index 4c8b2445..00000000 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; - -namespace ObjectPrinting.Tests -{ - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19 }; - - var printer = ObjectPrinter.For(); - //1. Исключить из сериализации свойства определенного типа - //2. Указать альтернативный способ сериализации для определенного типа - //3. Для числовых типов указать культуру - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //6. Исключить из сериализации конкретного свойства - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - //8. ...с конфигурированием - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs deleted file mode 100644 index f9555955..00000000 --- a/ObjectPrinting/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Tests -{ - public class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - } -} \ No newline at end of file diff --git a/ObjectPrintingTests/GlobalUsings.cs b/ObjectPrintingTests/GlobalUsings.cs new file mode 100644 index 00000000..cefced49 --- /dev/null +++ b/ObjectPrintingTests/GlobalUsings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs new file mode 100644 index 00000000..763a8b5d --- /dev/null +++ b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs @@ -0,0 +1,20 @@ +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests +{ + [TestFixture] + public class ObjectPrinterAcceptanceTests + { + [Test] + public void AcceptanceTest() + { + var person = new Person(new Guid(), "Alex", 192.8, 33); + + var printer = ObjectPrinter.For() + .ExcludeType(); + + var result = printer.PrintToString(person); + } + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrintingTests.csproj b/ObjectPrintingTests/ObjectPrintingTests.csproj new file mode 100644 index 00000000..0286cd8f --- /dev/null +++ b/ObjectPrintingTests/ObjectPrintingTests.csproj @@ -0,0 +1,44 @@ + + + + net7.0 + enable + enable + + false + true + ObjectPrintingTests + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + PrintingConfigExcludeTypeTests.cs + + + PrintingConfigSerializationTests.cs + + + PrintingConfigSerializationTests.cs + + + + diff --git a/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_DoesNotAffectResult_WhenExcludingNonPresentType.verified.txt b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_DoesNotAffectResult_WhenExcludingNonPresentType.verified.txt new file mode 100644 index 00000000..727df6a2 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_DoesNotAffectResult_WhenExcludingNonPresentType.verified.txt @@ -0,0 +1,5 @@ +Person + Id = Guid + Name = Alex + Age = 33 + Height = 192,2 diff --git a/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WhenExcludingForFinalType.verified.txt b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WhenExcludingForFinalType.verified.txt new file mode 100644 index 00000000..30ab4cf8 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WhenExcludingForFinalType.verified.txt @@ -0,0 +1 @@ +emptyString \ No newline at end of file diff --git a/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedComplexTypeItself.verified.txt b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedComplexTypeItself.verified.txt new file mode 100644 index 00000000..30ab4cf8 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedComplexTypeItself.verified.txt @@ -0,0 +1 @@ +emptyString \ No newline at end of file diff --git a/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedFinalType.verified.txt b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedFinalType.verified.txt new file mode 100644 index 00000000..f3efc50f --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedFinalType.verified.txt @@ -0,0 +1,4 @@ +Person + Id = Guid + Name = Alex + Height = 192,2 diff --git a/ObjectPrintingTests/PrintingConfigExcludeTypeTests.cs b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.cs new file mode 100644 index 00000000..fcf40765 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.cs @@ -0,0 +1,43 @@ +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests; + +public class PrintingConfigExcludeTypeTests +{ + [Test] + public async Task PrintToString_ReturnsCorrectResult_WithExcludedFinalType() + { + var printer = ObjectPrinter.For() + .ExcludeType(); + + await Verify(printer.PrintToString(TestDataFactory.Person)); + } + + [Test] + public async Task PrintToString_ReturnsCorrectResult_WithExcludedComplexTypeItself() + { + var printer = ObjectPrinter.For() + .ExcludeType(); + + await Verify(printer.PrintToString(TestDataFactory.Person)); + } + + [Test] + public async Task PrintToString_ReturnsCorrectResult_WhenExcludingForFinalType() + { + var printer = ObjectPrinter.For() + .ExcludeType(); + + await Verify(printer.PrintToString(3)); + } + + [Test] + public async Task PrintToString_DoesNotAffectResult_WhenExcludingNonPresentType() + { + var printer = ObjectPrinter.For() + .ExcludeType(); + + await Verify(printer.PrintToString(TestDataFactory.Person)); + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt b/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt new file mode 100644 index 00000000..5528a2f1 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt @@ -0,0 +1,5 @@ +Person + Id = Guid + Name = second + Age = 33 + Height = 192,2 diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt b/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt new file mode 100644 index 00000000..4971fa3c --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt @@ -0,0 +1 @@ +This is a "Person" type diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt b/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt new file mode 100644 index 00000000..33265e87 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt @@ -0,0 +1,5 @@ +Person + Id = Guid + Name = Misha + Age = 33 + Height = 192,2 diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt b/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt new file mode 100644 index 00000000..6d67a767 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt @@ -0,0 +1 @@ +Alex 33 diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.cs b/ObjectPrintingTests/PrintingConfigSerializationTests.cs new file mode 100644 index 00000000..03768caa --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigSerializationTests.cs @@ -0,0 +1,44 @@ +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests; + +public class PrintingConfigSerializationTests +{ + [Test] + public async Task WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified() + { + var printer = ObjectPrinter.For() + .WithSerializationForType(_ => "Misha"); + + await Verify(printer.PrintToString(TestDataFactory.Person)); + } + + [Test] + public async Task WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified() + { + var printer = ObjectPrinter.For() + .WithSerializationForType(_ => "This is a \"Person\" type"); + + await Verify(printer.PrintToString(TestDataFactory.Person)); + } + + [Test] + public async Task WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided() + { + var printer = ObjectPrinter.For() + .WithSerializationForType(_ => "first") + .WithSerializationForType(_ => "second"); + + await Verify(printer.PrintToString(TestDataFactory.Person)); + } + + [Test] + public async Task WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType() + { + var printer = ObjectPrinter.For() + .WithSerializationForType(p => p.Name + " " + p.Age); + + await Verify(printer.PrintToString(TestDataFactory.Person)); + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/TestData/Person.cs b/ObjectPrintingTests/TestData/Person.cs new file mode 100644 index 00000000..04160f9d --- /dev/null +++ b/ObjectPrintingTests/TestData/Person.cs @@ -0,0 +1,17 @@ +namespace ObjectPrintingTests.TestData; + +public class Person +{ + public Guid Id { get; } + public string Name { get; } + public int Age { get; } + public double Height { get; } + + public Person(Guid id, string name, double height, int age) + { + Id = id; + Name = name; + Height = height; + Age = age; + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/TestData/TestDataFactory.cs b/ObjectPrintingTests/TestData/TestDataFactory.cs new file mode 100644 index 00000000..8929a952 --- /dev/null +++ b/ObjectPrintingTests/TestData/TestDataFactory.cs @@ -0,0 +1,6 @@ +namespace ObjectPrintingTests.TestData; + +public static class TestDataFactory +{ + public static readonly Person Person = new(new Guid(), "Alex", 192.2, 33); +} \ No newline at end of file diff --git a/Samples/FluentMapper/SetterSpec.cs b/Samples/FluentMapper/SetterSpec.cs index 0cd7ac48..f03677d3 100644 --- a/Samples/FluentMapper/SetterSpec.cs +++ b/Samples/FluentMapper/SetterSpec.cs @@ -21,7 +21,7 @@ public sealed class SetterSpec : ISetterSpecProperties spec, PropertyInfo targetProperty - ) + ) { _spec = spec; _targetProperty = targetProperty; @@ -31,7 +31,8 @@ public TypeMappingSpec From(Expression From(Expression x != srcExpr.Member) - .ToArray() - ) - .Transforms().WithTargetProperties( - specProperties.TargetProperties - .Where(x => x != _targetProperty) - .ToArray() - ) + .Transforms().WithMappingActions( + specProperties.MappingActions + .Concat(new[] {setterAction}) + .ToArray() + ) + .Transforms().WithSourceProperties( + specProperties.SourceProperties + .Where(x => x != srcExpr.Member) + .ToArray() + ) + .Transforms().WithTargetProperties( + specProperties.TargetProperties + .Where(x => x != _targetProperty) + .ToArray() + ) ; } diff --git a/fluent-api.sln b/fluent-api.sln index 69c8db9e..418c65a0 100644 --- a/fluent-api.sln +++ b/fluent-api.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping.Tests", "Samp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrintingTests", "ObjectPrintingTests\ObjectPrintingTests.csproj", "{E0C40C7D-C872-413C-8AE6-6E6ABB0AE5A3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,6 +37,10 @@ Global {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.Build.0 = Release|Any CPU + {E0C40C7D-C872-413C-8AE6-6E6ABB0AE5A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0C40C7D-C872-413C-8AE6-6E6ABB0AE5A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0C40C7D-C872-413C-8AE6-6E6ABB0AE5A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0C40C7D-C872-413C-8AE6-6E6ABB0AE5A3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 71719b8dc93ea307ccf666a0f80ee4160fa36cbf Mon Sep 17 00:00:00 2001 From: Alexander Gorbatov Date: Sat, 23 Dec 2023 20:01:25 +0500 Subject: [PATCH 2/5] Added custom member serialization and string cuts --- ObjectPrinting/PrintingConfig.cs | 119 ++++++++++++++---- ObjectPrinting/PrintingConfigUtils.cs | 13 ++ ObjectPrinting/PrintingMemberConfig.cs | 22 ++++ ObjectPrinting/PrintingMemberConfigUtils.cs | 28 +++++ ObjectPrinting/PrintingStringMemberConfig.cs | 21 ++++ .../ObjectPrintingTests.csproj | 14 ++- ...playComma_WhenCultureInfoIsRu.verified.txt | 5 + ...enMoreThanOneCultureSpecified.verified.txt | 5 + .../PrintingConfigCultureTests.cs | 32 +++++ ...alueLengthIsLessThanMaxLength.verified.txt | 7 ++ ...engthIsGreaterThanValueLength.verified.txt | 7 ++ .../PrintingConfigCutStringTests.cs | 38 ++++++ ...t_WhenExcludingNonPresentType.verified.txt | 2 +- ...tResult_WithExcludedFinalType.verified.txt | 2 +- ...ame_WhenMemberFullNamesDiffer.verified.txt | 13 ++ ...henNewSerializationIsProvided.verified.txt | 7 ++ ...ativeSerializationIsSpecified.verified.txt | 7 ++ ...eSerializationIsAlsoSpecified.verified.txt | 7 ++ ...zationIsSetForMultipleMembers.verified.txt | 7 ++ ...omplexTypeMemberSerialization.verified.txt | 7 ++ .../PrintingConfigMemberSerializationTests.cs | 82 ++++++++++++ .../PrintingConfigSelectMemberTests.cs | 17 +++ ...enNewSerializationIsProvided.verified.txt} | 2 +- ...TypeSerializationIsSpecified.verified.txt} | 0 ...TypeSerializationIsSpecified.verified.txt} | 2 +- ...UsingPropertiesOfComplexType.verified.txt} | 0 ...> PrintingConfigTypeSerializationTests.cs} | 2 +- ObjectPrintingTests/TestData/ComplexPerson.cs | 12 ++ ObjectPrintingTests/TestData/Person.cs | 2 +- .../TestData/TestDataFactory.cs | 4 + 30 files changed, 454 insertions(+), 32 deletions(-) create mode 100644 ObjectPrinting/PrintingConfigUtils.cs create mode 100644 ObjectPrinting/PrintingMemberConfig.cs create mode 100644 ObjectPrinting/PrintingMemberConfigUtils.cs create mode 100644 ObjectPrinting/PrintingStringMemberConfig.cs create mode 100644 ObjectPrintingTests/PrintingConfigCultureTests.SetCultureInfoForType_MakesFloatingPointTypesDisplayComma_WhenCultureInfoIsRu.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigCultureTests.SetCultureInfoForType_OverwritesPreviousCulture_WhenMoreThanOneCultureSpecified.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigCultureTests.cs create mode 100644 ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_PrintsCutString_WhenValueLengthIsLessThanMaxLength.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_PrintsEntireString_WhenMaxLengthIsGreaterThanValueLength.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigCutStringTests.cs create mode 100644 ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_ConsidersFullMemberName_WhenMemberFullNamesDiffer.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_ReturnsCorrectResult_WhenAlternativeSerializationIsSpecified.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_TakesPrecedence_WhenTypeSerializationIsAlsoSpecified.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerializtion_ReturnsCorrectResult_WhenSerializationIsSetForMultipleMembers.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerializtion_ReturnsCorrectResult_WithComplexTypeMemberSerialization.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigMemberSerializationTests.cs create mode 100644 ObjectPrintingTests/PrintingConfigSelectMemberTests.cs rename ObjectPrintingTests/{PrintingConfigSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt => PrintingConfigTypeSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt} (54%) rename ObjectPrintingTests/{PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt => PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt} (100%) rename ObjectPrintingTests/{PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt => PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt} (53%) rename ObjectPrintingTests/{PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt => PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt} (100%) rename ObjectPrintingTests/{PrintingConfigSerializationTests.cs => PrintingConfigTypeSerializationTests.cs} (96%) create mode 100644 ObjectPrintingTests/TestData/ComplexPerson.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 48d99ba7..ef66ab8f 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; +using System.Linq.Expressions; using System.Text; namespace ObjectPrinting; @@ -8,17 +10,33 @@ namespace ObjectPrinting; public class PrintingConfig { private readonly HashSet excludedTypes = new(); - private readonly Dictionary> customSerialization = new(); - + private readonly Dictionary> customTypeSerialization = new(); + private readonly Dictionary specifiedCultureInfo = new(); + protected readonly Dictionary> CustomMemberSerialization = new(); + protected readonly Dictionary MemberMaxStringLength = new(); + private readonly Type[] finalTypes = { typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) + typeof(DateTime), typeof(TimeSpan), typeof(Guid) }; + public PrintingConfig() + { + } + + protected PrintingConfig(PrintingConfig printingConfig) + { + excludedTypes = printingConfig.excludedTypes; + customTypeSerialization = printingConfig.customTypeSerialization; + specifiedCultureInfo = printingConfig.specifiedCultureInfo; + CustomMemberSerialization = printingConfig.CustomMemberSerialization; + MemberMaxStringLength = printingConfig.MemberMaxStringLength; + } + public string PrintToString(TOwner obj) { - return PrintToString(obj, 0); + return PrintToString(obj, 0, obj.GetType().Name); } public PrintingConfig ExcludeType() @@ -30,18 +48,39 @@ public PrintingConfig ExcludeType() public PrintingConfig WithSerializationForType(Func serializationFunc) { var type = typeof(T); - customSerialization[type] = obj => serializationFunc((T) obj); + customTypeSerialization[type] = obj => serializationFunc((T) obj); return this; } - // private string ApplyCustomSerializationIfPresent(object obj, string initial) - // { - // return customSerialization.TryGetValue(obj.GetType(), out var serialization) - // ? serialization(obj) - // : initial; - // } + public PrintingConfig SetCultureForType(CultureInfo cultureInfo) where T : IFormattable + { + var type = typeof(T); + specifiedCultureInfo[type] = cultureInfo; + return this; + } - private string PrintToString(object obj, int nestingLevel) + public PrintingStringMemberConfig SelectMember(Expression> member) + { + PrintingConfigUtils.ValidateMemberExpression(member); + return new PrintingStringMemberConfig(this, member); + } + + public PrintingMemberConfig SelectMember(Expression> member) + { + PrintingConfigUtils.ValidateMemberExpression(member); + return new PrintingMemberConfig(this, member); + } + + private string PrintWithCultureIfExists(object obj, string initial) + { + if (obj is not IFormattable formattable || + !specifiedCultureInfo.TryGetValue(obj.GetType(), out var cultureInfo)) + return initial; + + return formattable.ToString("", cultureInfo) + Environment.NewLine; + } + + private string PrintToString(object obj, int nestingLevel, string memberPrefix) { if (obj is null) return "null" + Environment.NewLine; @@ -49,32 +88,68 @@ private string PrintToString(object obj, int nestingLevel) if (excludedTypes.Contains(obj.GetType())) return ""; - if (customSerialization.TryGetValue(obj.GetType(), out var serialization)) - return serialization(obj) + Environment.NewLine; + if (CustomMemberSerialization.TryGetValue(memberPrefix, out var memberSerialization)) + return memberSerialization(obj) + Environment.NewLine; + + if (customTypeSerialization.TryGetValue(obj.GetType(), out var typeSerialization)) + return typeSerialization(obj) + Environment.NewLine; + + if (MemberMaxStringLength.TryGetValue(memberPrefix, out var maxLength)) + return CutString((string) obj, maxLength) + Environment.NewLine; if (finalTypes.Contains(obj.GetType())) - return obj + Environment.NewLine; + return PrintWithCultureIfExists(obj, obj + Environment.NewLine); - var printedProperties = PrintObjectProperties(obj, nestingLevel); + var printedProperties = PrintObjectMembers(obj, nestingLevel, memberPrefix); return printedProperties; } - private string PrintObjectProperties(object obj, int nestingLevel) + private static string CutString(string s, int maxLength) { - var identation = new string('\t', nestingLevel + 1); + return s.Length > maxLength ? s[..maxLength] : s; + } + + private string PrintObjectMembers(object obj, int nestingLevel, string memberPrefix) + { + var indentation = new string('\t', nestingLevel + 1); var objectType = obj.GetType(); var sb = new StringBuilder().AppendLine(objectType.Name); + if (memberPrefix != "") + memberPrefix += "."; + foreach (var propertyInfo in objectType.GetProperties()) { - if (excludedTypes.Contains(propertyInfo.PropertyType)) - continue; + sb.Append(ProcessMember(propertyInfo.PropertyType, propertyInfo.Name, propertyInfo.GetValue(obj), + indentation, + memberPrefix, nestingLevel)); + } - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), nestingLevel + 1)); + foreach (var fieldInfo in objectType.GetFields()) + { + sb.Append(ProcessMember(fieldInfo.FieldType, fieldInfo.Name, fieldInfo.GetValue(obj), indentation, + memberPrefix, + nestingLevel)); } return sb.ToString(); } + + private string ProcessMember(Type type, string name, object value, string indentation, string memberPrefix, + int nestingLevel) + { + var sb = new StringBuilder(); + + if (excludedTypes.Contains(type)) + return ""; + + sb.Append(indentation + name + " = "); + + sb.Append(CustomMemberSerialization.TryGetValue(memberPrefix + name, out var serialization) + ? serialization(value) + Environment.NewLine + : PrintToString(value, nestingLevel + 1, memberPrefix + name)); + + return sb.ToString(); + } } \ No newline at end of file diff --git a/ObjectPrinting/PrintingConfigUtils.cs b/ObjectPrinting/PrintingConfigUtils.cs new file mode 100644 index 00000000..106f054c --- /dev/null +++ b/ObjectPrinting/PrintingConfigUtils.cs @@ -0,0 +1,13 @@ +using System; +using System.Linq.Expressions; + +namespace ObjectPrinting; + +public static class PrintingConfigUtils +{ + public static void ValidateMemberExpression(Expression> member) + { + if (member.Body.NodeType != ExpressionType.MemberAccess) + throw new MissingMemberException("Type's member has to be selected."); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingMemberConfig.cs b/ObjectPrinting/PrintingMemberConfig.cs new file mode 100644 index 00000000..cb75f34d --- /dev/null +++ b/ObjectPrinting/PrintingMemberConfig.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq.Expressions; + +namespace ObjectPrinting; + +public class PrintingMemberConfig : PrintingConfig +{ + protected readonly Expression> Member; + + public PrintingMemberConfig(PrintingConfig printingConfig, Expression> member) + : base(printingConfig) + { + Member = member; + } + + public PrintingMemberConfig WithSerialization(Func serializationFunc) + { + var memberName = PrintingMemberConfigUtils.GetFullMemberName(Member); + CustomMemberSerialization[memberName] = obj => serializationFunc((T)obj); + return this; + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingMemberConfigUtils.cs b/ObjectPrinting/PrintingMemberConfigUtils.cs new file mode 100644 index 00000000..0c25fb90 --- /dev/null +++ b/ObjectPrinting/PrintingMemberConfigUtils.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace ObjectPrinting; + +public class PrintingMemberConfigUtils +{ + public static string GetFullMemberName(Expression> expression) + { + PrintingConfigUtils.ValidateMemberExpression(expression); + var memberExpression = expression.Body as MemberExpression; + + var memberNames = new List(); + while (memberExpression != null) + { + memberNames.Add(memberExpression.Member.Name); + if (memberExpression.Expression is MemberExpression nestedMemberExpression) + memberExpression = nestedMemberExpression; + else + memberExpression = null; + } + + memberNames.Reverse(); + memberNames.Insert(0, typeof(TOwner).Name); + return string.Join(".", memberNames); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingStringMemberConfig.cs b/ObjectPrinting/PrintingStringMemberConfig.cs new file mode 100644 index 00000000..cba5d078 --- /dev/null +++ b/ObjectPrinting/PrintingStringMemberConfig.cs @@ -0,0 +1,21 @@ +using System; +using System.Linq.Expressions; + +namespace ObjectPrinting; + +public class PrintingStringMemberConfig : PrintingMemberConfig +{ + public PrintingStringMemberConfig(PrintingConfig printingConfig, Expression> member) : base(printingConfig, member) + { + } + + public PrintingStringMemberConfig SetStringMaxLength(int length) + { + if (length < 0) + throw new ArgumentException("Provided string length cannot be negative"); + + var memberName = PrintingMemberConfigUtils.GetFullMemberName(Member); + MemberMaxStringLength[memberName] = length; + return this; + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrintingTests.csproj b/ObjectPrintingTests/ObjectPrintingTests.csproj index 0286cd8f..36ee7528 100644 --- a/ObjectPrintingTests/ObjectPrintingTests.csproj +++ b/ObjectPrintingTests/ObjectPrintingTests.csproj @@ -33,11 +33,17 @@ PrintingConfigExcludeTypeTests.cs - - PrintingConfigSerializationTests.cs + + PrintingConfigTypeSerializationTests.cs - - PrintingConfigSerializationTests.cs + + PrintingConfigTypeSerializationTests.cs + + + PrintingConfigCultureTests.cs + + + PrintingConfigCutStringTests.cs diff --git a/ObjectPrintingTests/PrintingConfigCultureTests.SetCultureInfoForType_MakesFloatingPointTypesDisplayComma_WhenCultureInfoIsRu.verified.txt b/ObjectPrintingTests/PrintingConfigCultureTests.SetCultureInfoForType_MakesFloatingPointTypesDisplayComma_WhenCultureInfoIsRu.verified.txt new file mode 100644 index 00000000..fd14c007 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCultureTests.SetCultureInfoForType_MakesFloatingPointTypesDisplayComma_WhenCultureInfoIsRu.verified.txt @@ -0,0 +1,5 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Age = 33 + Height = 192,2 diff --git a/ObjectPrintingTests/PrintingConfigCultureTests.SetCultureInfoForType_OverwritesPreviousCulture_WhenMoreThanOneCultureSpecified.verified.txt b/ObjectPrintingTests/PrintingConfigCultureTests.SetCultureInfoForType_OverwritesPreviousCulture_WhenMoreThanOneCultureSpecified.verified.txt new file mode 100644 index 00000000..e75c780b --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCultureTests.SetCultureInfoForType_OverwritesPreviousCulture_WhenMoreThanOneCultureSpecified.verified.txt @@ -0,0 +1,5 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Age = 33 + Height = 192.2 diff --git a/ObjectPrintingTests/PrintingConfigCultureTests.cs b/ObjectPrintingTests/PrintingConfigCultureTests.cs new file mode 100644 index 00000000..5c501da4 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCultureTests.cs @@ -0,0 +1,32 @@ +using System.Globalization; +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests; + +public class PrintingConfigCultureTests +{ + [Test] + public async Task SetCultureInfoForType_MakesFloatingPointTypesDisplayComma_WhenCultureInfoIsRu() + { + var ruCulture = new CultureInfo("ru"); + + var printer = ObjectPrinter.For() + .SetCultureForType(ruCulture); + + await Verify(printer.PrintToString(TestDataFactory.Person)); + } + + [Test] + public async Task SetCultureInfoForType_OverwritesPreviousCulture_WhenMoreThanOneCultureSpecified() + { + var ruCulture = new CultureInfo("ru"); + var enCulture = new CultureInfo("en"); + + var printer = ObjectPrinter.For() + .SetCultureForType(ruCulture) + .SetCultureForType(enCulture); + + await Verify(printer.PrintToString(TestDataFactory.Person)); + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_PrintsCutString_WhenValueLengthIsLessThanMaxLength.verified.txt b/ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_PrintsCutString_WhenValueLengthIsLessThanMaxLength.verified.txt new file mode 100644 index 00000000..4cd637d3 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_PrintsCutString_WhenValueLengthIsLessThanMaxLength.verified.txt @@ -0,0 +1,7 @@ +ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Iv + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_PrintsEntireString_WhenMaxLengthIsGreaterThanValueLength.verified.txt b/ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_PrintsEntireString_WhenMaxLengthIsGreaterThanValueLength.verified.txt new file mode 100644 index 00000000..ea8bc74b --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_PrintsEntireString_WhenMaxLengthIsGreaterThanValueLength.verified.txt @@ -0,0 +1,7 @@ +ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigCutStringTests.cs b/ObjectPrintingTests/PrintingConfigCutStringTests.cs new file mode 100644 index 00000000..86b53499 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCutStringTests.cs @@ -0,0 +1,38 @@ +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests; + +public class PrintingConfigCutStringTests +{ + [Test] + public void SetStringMaxLength_ThrowsArgumentException_WhenProvidedLengthIsNegative() + { + Assert.Throws(() => + { + ObjectPrinter.For() + .SelectMember(p => p.Name) + .SetStringMaxLength(-1); + }); + } + + [Test] + public async Task SetStringMaxLength_PrintsEntireString_WhenMaxLengthIsGreaterThanValueLength() + { + var printer = ObjectPrinter.For() + .SelectMember(p => p.Name) + .SetStringMaxLength(100); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task SetStringMaxLength_PrintsCutString_WhenValueLengthIsLessThanMaxLength() + { + var printer = ObjectPrinter.For() + .SelectMember(p => p.Name) + .SetStringMaxLength(2); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_DoesNotAffectResult_WhenExcludingNonPresentType.verified.txt b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_DoesNotAffectResult_WhenExcludingNonPresentType.verified.txt index 727df6a2..fd14c007 100644 --- a/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_DoesNotAffectResult_WhenExcludingNonPresentType.verified.txt +++ b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_DoesNotAffectResult_WhenExcludingNonPresentType.verified.txt @@ -1,5 +1,5 @@ Person - Id = Guid + Id = 00000000-0000-0000-0000-000000000000 Name = Alex Age = 33 Height = 192,2 diff --git a/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedFinalType.verified.txt b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedFinalType.verified.txt index f3efc50f..c9974ee6 100644 --- a/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedFinalType.verified.txt +++ b/ObjectPrintingTests/PrintingConfigExcludeTypeTests.PrintToString_ReturnsCorrectResult_WithExcludedFinalType.verified.txt @@ -1,4 +1,4 @@ Person - Id = Guid + Id = 00000000-0000-0000-0000-000000000000 Name = Alex Height = 192,2 diff --git a/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_ConsidersFullMemberName_WhenMemberFullNamesDiffer.verified.txt b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_ConsidersFullMemberName_WhenMemberFullNamesDiffer.verified.txt new file mode 100644 index 00000000..ae943435 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_ConsidersFullMemberName_WhenMemberFullNamesDiffer.verified.txt @@ -0,0 +1,13 @@ +ComplexPerson + Parent = ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Asd + Age = 123 + Height = 0,12 + Weight = 0 + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt new file mode 100644 index 00000000..1c904ed3 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt @@ -0,0 +1,7 @@ +ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 321 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_ReturnsCorrectResult_WhenAlternativeSerializationIsSpecified.verified.txt b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_ReturnsCorrectResult_WhenAlternativeSerializationIsSpecified.verified.txt new file mode 100644 index 00000000..99ab9e22 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_ReturnsCorrectResult_WhenAlternativeSerializationIsSpecified.verified.txt @@ -0,0 +1,7 @@ +ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 123 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_TakesPrecedence_WhenTypeSerializationIsAlsoSpecified.verified.txt b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_TakesPrecedence_WhenTypeSerializationIsAlsoSpecified.verified.txt new file mode 100644 index 00000000..99ab9e22 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerialization_TakesPrecedence_WhenTypeSerializationIsAlsoSpecified.verified.txt @@ -0,0 +1,7 @@ +ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 123 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerializtion_ReturnsCorrectResult_WhenSerializationIsSetForMultipleMembers.verified.txt b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerializtion_ReturnsCorrectResult_WhenSerializationIsSetForMultipleMembers.verified.txt new file mode 100644 index 00000000..571afa6f --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerializtion_ReturnsCorrectResult_WhenSerializationIsSetForMultipleMembers.verified.txt @@ -0,0 +1,7 @@ +ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = 123 + Age = 123 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerializtion_ReturnsCorrectResult_WithComplexTypeMemberSerialization.verified.txt b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerializtion_ReturnsCorrectResult_WithComplexTypeMemberSerialization.verified.txt new file mode 100644 index 00000000..5f69cb80 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.WithSerializtion_ReturnsCorrectResult_WithComplexTypeMemberSerialization.verified.txt @@ -0,0 +1,7 @@ +ComplexPerson + Parent = custom + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigMemberSerializationTests.cs b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.cs new file mode 100644 index 00000000..0b5f58b3 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigMemberSerializationTests.cs @@ -0,0 +1,82 @@ +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests; + +public class PrintingConfigMemberSerializationTests +{ + [Test] + public async Task WithSerialization_ReturnsCorrectResult_WhenAlternativeSerializationIsSpecified() + { + var printer = ObjectPrinter.For() + .SelectMember(p => p.Age) + .WithSerialization(_ => "123"); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task WithSerialization_OverwritesPreviousSerialization_WhenNewSerializationIsProvided() + { + var printer = ObjectPrinter.For() + .SelectMember(p => p.Age) + .WithSerialization(_ => "123") + .SelectMember(p => p.Age) + .WithSerialization(_ => "321"); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task WithSerialization_ConsidersFullMemberName_WhenMemberFullNamesDiffer() + { + TestDataFactory.ComplexPerson.Parent = TestDataFactory.Parent; + + var printer = ObjectPrinter.For() + .SelectMember(p => p.Parent!.Age) + .WithSerialization(_ => "123"); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task WithSerialization_TakesPrecedence_WhenTypeSerializationIsAlsoSpecified() + { + var printer = ObjectPrinter.For() + .SelectMember(p => p.Age) + .WithSerialization(_ => "123") + .WithSerializationForType(_ => "321"); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task WithSerializtion_ReturnsCorrectResult_WithComplexTypeMemberSerialization() + { + TestDataFactory.ComplexPerson.Parent = TestDataFactory.Parent; + + var printer = ObjectPrinter.For() + .SelectMember(p => p.Parent) + .WithSerialization(_ => "custom"); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task WithSerializtion_ReturnsCorrectResult_WhenSerializationIsSetForMultipleMembers() + { + var printer = ObjectPrinter.For() + .SelectMember(p => p.Age) + .WithSerialization(_ => "123") + .SelectMember(p => p.Name) + .WithSerialization(_ => "123"); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [TearDown] + public void TearDown() + { + TestDataFactory.ComplexPerson.Parent = null; + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/PrintingConfigSelectMemberTests.cs b/ObjectPrintingTests/PrintingConfigSelectMemberTests.cs new file mode 100644 index 00000000..642e6f54 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigSelectMemberTests.cs @@ -0,0 +1,17 @@ +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests; + +public class PrintingConfigSelectMemberTests +{ + [Test] + public void SelectMember_ThrowsMissingMemberException_WhenInvalidMemberIsSpecified() + { + Assert.Throws(() => + { + ObjectPrinter.For() + .SelectMember(p => "123"); + }); + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt b/ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt similarity index 54% rename from ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt rename to ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt index 5528a2f1..15062f1d 100644 --- a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt +++ b/ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_OverwritesPreviousSerialization_WhenNewSerializationIsProvided.verified.txt @@ -1,5 +1,5 @@ Person - Id = Guid + Id = 00000000-0000-0000-0000-000000000000 Name = second Age = 33 Height = 192,2 diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt b/ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt similarity index 100% rename from ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt rename to ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeComplexTypeSerializationIsSpecified.verified.txt diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt b/ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt similarity index 53% rename from ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt rename to ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt index 33265e87..2208c035 100644 --- a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt +++ b/ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified.verified.txt @@ -1,5 +1,5 @@ Person - Id = Guid + Id = 00000000-0000-0000-0000-000000000000 Name = Misha Age = 33 Height = 192,2 diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt b/ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt similarity index 100% rename from ObjectPrintingTests/PrintingConfigSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt rename to ObjectPrintingTests/PrintingConfigTypeSerializationTests.WithSerializationForType_ReturnsCorrectResult_WhenUsingPropertiesOfComplexType.verified.txt diff --git a/ObjectPrintingTests/PrintingConfigSerializationTests.cs b/ObjectPrintingTests/PrintingConfigTypeSerializationTests.cs similarity index 96% rename from ObjectPrintingTests/PrintingConfigSerializationTests.cs rename to ObjectPrintingTests/PrintingConfigTypeSerializationTests.cs index 03768caa..f1653fda 100644 --- a/ObjectPrintingTests/PrintingConfigSerializationTests.cs +++ b/ObjectPrintingTests/PrintingConfigTypeSerializationTests.cs @@ -3,7 +3,7 @@ namespace ObjectPrintingTests; -public class PrintingConfigSerializationTests +public class PrintingConfigTypeSerializationTests { [Test] public async Task WithSerializationForType_ReturnsCorrectResult_WhenAlternativeFinalTypeSerializationIsSpecified() diff --git a/ObjectPrintingTests/TestData/ComplexPerson.cs b/ObjectPrintingTests/TestData/ComplexPerson.cs new file mode 100644 index 00000000..75131361 --- /dev/null +++ b/ObjectPrintingTests/TestData/ComplexPerson.cs @@ -0,0 +1,12 @@ +namespace ObjectPrintingTests.TestData; + +public class ComplexPerson : Person +{ + public ComplexPerson? Parent { get; set; } + + public float Weight = 0; + + public ComplexPerson(Guid id, string name, double height, int age) : base(id, name, height, age) + { + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/TestData/Person.cs b/ObjectPrintingTests/TestData/Person.cs index 04160f9d..5540fb26 100644 --- a/ObjectPrintingTests/TestData/Person.cs +++ b/ObjectPrintingTests/TestData/Person.cs @@ -6,7 +6,7 @@ public class Person public string Name { get; } public int Age { get; } public double Height { get; } - + public Person(Guid id, string name, double height, int age) { Id = id; diff --git a/ObjectPrintingTests/TestData/TestDataFactory.cs b/ObjectPrintingTests/TestData/TestDataFactory.cs index 8929a952..385b4382 100644 --- a/ObjectPrintingTests/TestData/TestDataFactory.cs +++ b/ObjectPrintingTests/TestData/TestDataFactory.cs @@ -3,4 +3,8 @@ public static class TestDataFactory { public static readonly Person Person = new(new Guid(), "Alex", 192.2, 33); + + public static readonly ComplexPerson ComplexPerson = new(new Guid(), "Ivan", 190.1, 30); + + public static readonly ComplexPerson Parent = new(new Guid(), "Asd", 0.12, 12); } \ No newline at end of file From e1e953cc157ec0ca1c8f854fdf16bcb475f47f22 Mon Sep 17 00:00:00 2001 From: Alexander Gorbatov Date: Sat, 23 Dec 2023 20:47:37 +0500 Subject: [PATCH 3/5] added a method for excluding members --- ObjectPrinting/PrintingConfig.cs | 6 ++- ObjectPrinting/PrintingMemberConfig.cs | 7 +++ .../ObjectPrintingTests.csproj | 3 ++ ...Result_WhenCalledMoreThanOnce.verified.txt | 7 +++ .../PrintingConfigCutStringTests.cs | 11 +++++ ...neMemberWithSameNameIsPresent.verified.txt | 12 +++++ ...sult_WhenExcludingComplexType.verified.txt | 6 +++ ...WithExcludedMemberOfFinalType.verified.txt | 6 +++ .../PrintingConfigExcludeMemberTests.cs | 45 +++++++++++++++++++ 9 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_OverwritesPreviousCallResult_WhenCalledMoreThanOnce.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ExcludesBasedOnFullName_WhenMoreThanOneMemberWithSameNameIsPresent.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ReturnsCorrectResult_WhenExcludingComplexType.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ReturnsCorrectResult_WithExcludedMemberOfFinalType.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigExcludeMemberTests.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index ef66ab8f..b07fe48c 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -14,6 +14,7 @@ public class PrintingConfig private readonly Dictionary specifiedCultureInfo = new(); protected readonly Dictionary> CustomMemberSerialization = new(); protected readonly Dictionary MemberMaxStringLength = new(); + protected readonly HashSet ExcludedMembers = new(); private readonly Type[] finalTypes = { @@ -32,6 +33,7 @@ protected PrintingConfig(PrintingConfig printingConfig) specifiedCultureInfo = printingConfig.specifiedCultureInfo; CustomMemberSerialization = printingConfig.CustomMemberSerialization; MemberMaxStringLength = printingConfig.MemberMaxStringLength; + ExcludedMembers = printingConfig.ExcludedMembers; } public string PrintToString(TOwner obj) @@ -85,7 +87,7 @@ private string PrintToString(object obj, int nestingLevel, string memberPrefix) if (obj is null) return "null" + Environment.NewLine; - if (excludedTypes.Contains(obj.GetType())) + if (excludedTypes.Contains(obj.GetType()) || ExcludedMembers.Contains(memberPrefix)) return ""; if (CustomMemberSerialization.TryGetValue(memberPrefix, out var memberSerialization)) @@ -141,7 +143,7 @@ private string ProcessMember(Type type, string name, object value, string indent { var sb = new StringBuilder(); - if (excludedTypes.Contains(type)) + if (excludedTypes.Contains(type) || ExcludedMembers.Contains(memberPrefix + name)) return ""; sb.Append(indentation + name + " = "); diff --git a/ObjectPrinting/PrintingMemberConfig.cs b/ObjectPrinting/PrintingMemberConfig.cs index cb75f34d..88ee4f4e 100644 --- a/ObjectPrinting/PrintingMemberConfig.cs +++ b/ObjectPrinting/PrintingMemberConfig.cs @@ -12,6 +12,13 @@ public PrintingMemberConfig(PrintingConfig printingConfig, Expression ExcludeMember() + { + var memberName = PrintingMemberConfigUtils.GetFullMemberName(Member); + ExcludedMembers.Add(memberName); + return this; + } public PrintingMemberConfig WithSerialization(Func serializationFunc) { diff --git a/ObjectPrintingTests/ObjectPrintingTests.csproj b/ObjectPrintingTests/ObjectPrintingTests.csproj index 36ee7528..f64b8842 100644 --- a/ObjectPrintingTests/ObjectPrintingTests.csproj +++ b/ObjectPrintingTests/ObjectPrintingTests.csproj @@ -45,6 +45,9 @@ PrintingConfigCutStringTests.cs + + PrintingConfigCutStringTests.cs + diff --git a/ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_OverwritesPreviousCallResult_WhenCalledMoreThanOnce.verified.txt b/ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_OverwritesPreviousCallResult_WhenCalledMoreThanOnce.verified.txt new file mode 100644 index 00000000..4cd637d3 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCutStringTests.SetStringMaxLength_OverwritesPreviousCallResult_WhenCalledMoreThanOnce.verified.txt @@ -0,0 +1,7 @@ +ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Iv + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigCutStringTests.cs b/ObjectPrintingTests/PrintingConfigCutStringTests.cs index 86b53499..14d9cdfb 100644 --- a/ObjectPrintingTests/PrintingConfigCutStringTests.cs +++ b/ObjectPrintingTests/PrintingConfigCutStringTests.cs @@ -35,4 +35,15 @@ public async Task SetStringMaxLength_PrintsCutString_WhenValueLengthIsLessThanMa await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); } + + [Test] + public async Task SetStringMaxLength_OverwritesPreviousCallResult_WhenCalledMoreThanOnce() + { + var printer = ObjectPrinter.For() + .SelectMember(p => p.Name) + .SetStringMaxLength(100) + .SetStringMaxLength(2); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } } \ No newline at end of file diff --git a/ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ExcludesBasedOnFullName_WhenMoreThanOneMemberWithSameNameIsPresent.verified.txt b/ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ExcludesBasedOnFullName_WhenMoreThanOneMemberWithSameNameIsPresent.verified.txt new file mode 100644 index 00000000..62a9be31 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ExcludesBasedOnFullName_WhenMoreThanOneMemberWithSameNameIsPresent.verified.txt @@ -0,0 +1,12 @@ +ComplexPerson + Parent = ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Asd + Height = 0,12 + Weight = 0 + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ReturnsCorrectResult_WhenExcludingComplexType.verified.txt b/ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ReturnsCorrectResult_WhenExcludingComplexType.verified.txt new file mode 100644 index 00000000..26d9f302 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ReturnsCorrectResult_WhenExcludingComplexType.verified.txt @@ -0,0 +1,6 @@ +ComplexPerson + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ReturnsCorrectResult_WithExcludedMemberOfFinalType.verified.txt b/ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ReturnsCorrectResult_WithExcludedMemberOfFinalType.verified.txt new file mode 100644 index 00000000..e519d05b --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigExcludeMemberTests.ExcludeMember_ReturnsCorrectResult_WithExcludedMemberOfFinalType.verified.txt @@ -0,0 +1,6 @@ +ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigExcludeMemberTests.cs b/ObjectPrintingTests/PrintingConfigExcludeMemberTests.cs new file mode 100644 index 00000000..2d22bc64 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigExcludeMemberTests.cs @@ -0,0 +1,45 @@ +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests; + +public class PrintingConfigExcludeMemberTests +{ + [Test] + public async Task ExcludeMember_ReturnsCorrectResult_WithExcludedMemberOfFinalType() + { + var printer = ObjectPrinter.For() + .SelectMember(p => p.Age) + .ExcludeMember(); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task ExcludeMember_ExcludesBasedOnFullName_WhenMoreThanOneMemberWithSameNameIsPresent() + { + TestDataFactory.ComplexPerson.Parent = TestDataFactory.Parent; + + var printer = ObjectPrinter.For() + .SelectMember(p => p.Parent!.Age) + .ExcludeMember(); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task ExcludeMember_ReturnsCorrectResult_WhenExcludingComplexType() + { + var printer = ObjectPrinter.For() + .SelectMember(p => p.Parent) + .ExcludeMember(); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [TearDown] + public void TearDown() + { + TestDataFactory.ComplexPerson.Parent = null; + } +} \ No newline at end of file From 107ba6d2e8379a39ac5e29532bdb55c8bb78c6ea Mon Sep 17 00:00:00 2001 From: Alexander Gorbatov Date: Sun, 24 Dec 2023 12:43:00 +0500 Subject: [PATCH 4/5] Added cyclic reference processing --- ObjectPrinting/PrintingConfig.cs | 55 ++++++++++++----- ObjectPrinting/PrintingMemberConfigUtils.cs | 2 +- ...WhenCyclicReferenceIsExcluded.verified.txt | 6 ++ ...ult_WithDirectCyclicReference.verified.txt | 7 +++ ...ult_WithNestedCyclicReference.verified.txt | 13 ++++ .../PrintingConfigCyclicReferenceTests.cs | 47 ++++++++++++++ .../PrintingConfigEnumerableTests.cs | 61 +++++++++++++++++++ .../TestData/EnumerableTest.cs | 16 +++++ .../TestData/PersonEnumerable.cs | 8 +++ .../TestData/TestDataFactory.cs | 6 ++ 10 files changed, 205 insertions(+), 16 deletions(-) create mode 100644 ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WhenCyclicReferenceIsExcluded.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WithDirectCyclicReference.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WithNestedCyclicReference.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigCyclicReferenceTests.cs create mode 100644 ObjectPrintingTests/PrintingConfigEnumerableTests.cs create mode 100644 ObjectPrintingTests/TestData/EnumerableTest.cs create mode 100644 ObjectPrintingTests/TestData/PersonEnumerable.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index b07fe48c..87609440 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -15,11 +16,12 @@ public class PrintingConfig protected readonly Dictionary> CustomMemberSerialization = new(); protected readonly Dictionary MemberMaxStringLength = new(); protected readonly HashSet ExcludedMembers = new(); - + private readonly Type[] finalTypes = { typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan), typeof(Guid) + typeof(DateTime), typeof(TimeSpan), typeof(Guid), typeof(uint), + typeof(decimal), typeof(long), typeof(ushort), typeof(ulong), typeof(short) }; public PrintingConfig() @@ -38,7 +40,7 @@ protected PrintingConfig(PrintingConfig printingConfig) public string PrintToString(TOwner obj) { - return PrintToString(obj, 0, obj.GetType().Name); + return PrintToString(obj, 0, obj.GetType().Name, new List()); } public PrintingConfig ExcludeType() @@ -66,7 +68,7 @@ public PrintingStringMemberConfig SelectMember(Expression(this, member); } - + public PrintingMemberConfig SelectMember(Expression> member) { PrintingConfigUtils.ValidateMemberExpression(member); @@ -82,8 +84,11 @@ private string PrintWithCultureIfExists(object obj, string initial) return formattable.ToString("", cultureInfo) + Environment.NewLine; } - private string PrintToString(object obj, int nestingLevel, string memberPrefix) + private string PrintToString(object obj, int nestingLevel, string memberPrefix, List parents) { + if (parents.Contains(obj)) + return "CYCLIC_REFERENCE" + Environment.NewLine; + if (obj is null) return "null" + Environment.NewLine; @@ -98,21 +103,39 @@ private string PrintToString(object obj, int nestingLevel, string memberPrefix) if (MemberMaxStringLength.TryGetValue(memberPrefix, out var maxLength)) return CutString((string) obj, maxLength) + Environment.NewLine; - + if (finalTypes.Contains(obj.GetType())) return PrintWithCultureIfExists(obj, obj + Environment.NewLine); - var printedProperties = PrintObjectMembers(obj, nestingLevel, memberPrefix); - - return printedProperties; + // if (obj is IEnumerable enumerable) + // return PrintEnumerable(enumerable, nestingLevel, memberPrefix, parents); + + return PrintObjectMembers(obj, nestingLevel, memberPrefix, parents); } + /*private string PrintEnumerable(IEnumerable obj, int nestingLevel, string memberPrefix, List parents) + { + var enumerator = obj.GetEnumerator(); + using var disposable = enumerator as IDisposable; + if (!enumerator.MoveNext()) + return "EMPTY_COLLECTION" + Environment.NewLine; + if (excludedTypes.Contains(enumerator.Current!.GetType())) + return ""; + + var sb = new StringBuilder(); + enumerator.Reset(); + while (enumerator.MoveNext()) + { + + } + }*/ + private static string CutString(string s, int maxLength) { return s.Length > maxLength ? s[..maxLength] : s; } - private string PrintObjectMembers(object obj, int nestingLevel, string memberPrefix) + private string PrintObjectMembers(object obj, int nestingLevel, string memberPrefix, List parents) { var indentation = new string('\t', nestingLevel + 1); var objectType = obj.GetType(); @@ -121,25 +144,27 @@ private string PrintObjectMembers(object obj, int nestingLevel, string memberPre if (memberPrefix != "") memberPrefix += "."; + parents.Add(obj); foreach (var propertyInfo in objectType.GetProperties()) { sb.Append(ProcessMember(propertyInfo.PropertyType, propertyInfo.Name, propertyInfo.GetValue(obj), indentation, - memberPrefix, nestingLevel)); + memberPrefix, nestingLevel, parents)); } foreach (var fieldInfo in objectType.GetFields()) { sb.Append(ProcessMember(fieldInfo.FieldType, fieldInfo.Name, fieldInfo.GetValue(obj), indentation, memberPrefix, - nestingLevel)); + nestingLevel, parents)); } + parents.Remove(parents.Count - 1); return sb.ToString(); } private string ProcessMember(Type type, string name, object value, string indentation, string memberPrefix, - int nestingLevel) + int nestingLevel, List parents) { var sb = new StringBuilder(); @@ -147,10 +172,10 @@ private string ProcessMember(Type type, string name, object value, string indent return ""; sb.Append(indentation + name + " = "); - + sb.Append(CustomMemberSerialization.TryGetValue(memberPrefix + name, out var serialization) ? serialization(value) + Environment.NewLine - : PrintToString(value, nestingLevel + 1, memberPrefix + name)); + : PrintToString(value, nestingLevel + 1, memberPrefix + name, parents)); return sb.ToString(); } diff --git a/ObjectPrinting/PrintingMemberConfigUtils.cs b/ObjectPrinting/PrintingMemberConfigUtils.cs index 0c25fb90..ef44c8ba 100644 --- a/ObjectPrinting/PrintingMemberConfigUtils.cs +++ b/ObjectPrinting/PrintingMemberConfigUtils.cs @@ -4,7 +4,7 @@ namespace ObjectPrinting; -public class PrintingMemberConfigUtils +public static class PrintingMemberConfigUtils { public static string GetFullMemberName(Expression> expression) { diff --git a/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WhenCyclicReferenceIsExcluded.verified.txt b/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WhenCyclicReferenceIsExcluded.verified.txt new file mode 100644 index 00000000..26d9f302 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WhenCyclicReferenceIsExcluded.verified.txt @@ -0,0 +1,6 @@ +ComplexPerson + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WithDirectCyclicReference.verified.txt b/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WithDirectCyclicReference.verified.txt new file mode 100644 index 00000000..8290b688 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WithDirectCyclicReference.verified.txt @@ -0,0 +1,7 @@ +ComplexPerson + Parent = CYCLIC_REFERENCE + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WithNestedCyclicReference.verified.txt b/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WithNestedCyclicReference.verified.txt new file mode 100644 index 00000000..a1ac7810 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.PrintToString_PrintsCorrectResult_WithNestedCyclicReference.verified.txt @@ -0,0 +1,13 @@ +ComplexPerson + Parent = ComplexPerson + Parent = CYCLIC_REFERENCE + Id = 00000000-0000-0000-0000-000000000000 + Name = Asd + Age = 12 + Height = 0,12 + Weight = 0 + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 diff --git a/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.cs b/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.cs new file mode 100644 index 00000000..af3b71fe --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigCyclicReferenceTests.cs @@ -0,0 +1,47 @@ +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests; + +public class PrintingConfigCyclicReferenceTests +{ + [Test] + public async Task PrintToString_PrintsCorrectResult_WithDirectCyclicReference() + { + TestDataFactory.ComplexPerson.Parent = TestDataFactory.ComplexPerson; + + var printer = ObjectPrinter.For(); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task PrintToString_PrintsCorrectResult_WithNestedCyclicReference() + { + TestDataFactory.ComplexPerson.Parent = TestDataFactory.Parent; + TestDataFactory.Parent.Parent = TestDataFactory.ComplexPerson; + + var printer = ObjectPrinter.For(); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [Test] + public async Task PrintToString_PrintsCorrectResult_WhenCyclicReferenceIsExcluded() + { + TestDataFactory.ComplexPerson.Parent = TestDataFactory.ComplexPerson; + + var printer = ObjectPrinter.For() + .SelectMember(p => p.Parent) + .ExcludeMember(); + + await Verify(printer.PrintToString(TestDataFactory.ComplexPerson)); + } + + [TearDown] + public void TearDown() + { + TestDataFactory.ComplexPerson.Parent = null; + TestDataFactory.Parent.Parent = null; + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/PrintingConfigEnumerableTests.cs b/ObjectPrintingTests/PrintingConfigEnumerableTests.cs new file mode 100644 index 00000000..f1fa6b35 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigEnumerableTests.cs @@ -0,0 +1,61 @@ +using System.Globalization; +using ObjectPrinting; +using ObjectPrintingTests.TestData; + +namespace ObjectPrintingTests; + +public class PrintingConfigEnumerableTests +{ + [Test] + public async Task PrintToString_PrintsEmptyLine_WhenCollectionIsExcluded() + { + var reader = ObjectPrinter.For() + .ExcludeType>(); + + await Verify(reader.PrintToString(TestDataFactory.EnumerableSimpleTest)); + } + + [Test] + public async Task PrintToString_PrintsEmptyLine_WhenTypeOfCollectionElementsIsExcluded() + { + var reader = ObjectPrinter.For() + .ExcludeType(); + + await Verify(reader.PrintToString(TestDataFactory.EnumerableSimpleTest)); + } + + [Test] + public async Task PrintToString_PrintsCorrectResult_WhenCollectionIsEmpty() + { + var reader = ObjectPrinter.For(); + + await Verify(reader.PrintToString(TestDataFactory.EnumerableEmptyTest)); + } + + [Test] + public async Task PrintToString_AppliesConfigToCollectionElements_WhenElementsMatchConfig() + { + var reader = ObjectPrinter.For() + .SetCultureForType(new CultureInfo("ru")) + .WithSerializationForType>(_ => "this is a dictionary object") + .WithSerializationForType(_ => "ComplexPerson object"); + + await Verify(reader.PrintToString(TestDataFactory.EnumerableSimpleTest)); + } + + [Test] + public async Task PrintToString_PrintsCorrectResult_WithCollectionElementsWithCyclicReference() + { + TestDataFactory.EnumerableSimpleTest.List[0].Parent = TestDataFactory.EnumerableSimpleTest.List[0]; + + var reader = ObjectPrinter.For(); + + await Verify(reader.PrintToString(TestDataFactory.EnumerableSimpleTest)); + } + + [TearDown] + public void TearDown() + { + TestDataFactory.EnumerableSimpleTest.List[0].Parent = null; + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/TestData/EnumerableTest.cs b/ObjectPrintingTests/TestData/EnumerableTest.cs new file mode 100644 index 00000000..2fd9b2cf --- /dev/null +++ b/ObjectPrintingTests/TestData/EnumerableTest.cs @@ -0,0 +1,16 @@ +namespace ObjectPrintingTests.TestData; + +public class EnumerableTest +{ + public readonly double[] Array = {1.2, 2.12, 3.2, 4.0}; + + public readonly List List; + + public Dictionary Dictionary; + + public EnumerableTest(List list, Dictionary dictionary) + { + List = list; + Dictionary = dictionary; + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/TestData/PersonEnumerable.cs b/ObjectPrintingTests/TestData/PersonEnumerable.cs new file mode 100644 index 00000000..887ba06e --- /dev/null +++ b/ObjectPrintingTests/TestData/PersonEnumerable.cs @@ -0,0 +1,8 @@ +namespace ObjectPrintingTests.TestData; + +public class PersonEnumerable +{ + public int Field = 2; + public TimeSpan Property { get; set; } + public List List { get; set; } = new(); +} \ No newline at end of file diff --git a/ObjectPrintingTests/TestData/TestDataFactory.cs b/ObjectPrintingTests/TestData/TestDataFactory.cs index 385b4382..44df1f31 100644 --- a/ObjectPrintingTests/TestData/TestDataFactory.cs +++ b/ObjectPrintingTests/TestData/TestDataFactory.cs @@ -7,4 +7,10 @@ public static class TestDataFactory public static readonly ComplexPerson ComplexPerson = new(new Guid(), "Ivan", 190.1, 30); public static readonly ComplexPerson Parent = new(new Guid(), "Asd", 0.12, 12); + + public static readonly EnumerableTest EnumerableSimpleTest = new(new List {ComplexPerson}, + new Dictionary {{1, new DateTime(1, 1, 1)}, {3, new DateTime(1988, 2, 28)}}); + + public static readonly EnumerableTest + EnumerableEmptyTest = new(new List(), new Dictionary()); } \ No newline at end of file From f825612205b46fc8a91b04cf222c88a0441d56b1 Mon Sep 17 00:00:00 2001 From: Alexander Gorbatov Date: Sun, 24 Dec 2023 15:18:08 +0500 Subject: [PATCH 5/5] Implemented serialization for lists, arrays and dictionaries --- ObjectPrinting/PrintingConfig.cs | 64 +++++++++++++++---- ObjectPrinting/PrintingMemberConfigUtils.cs | 28 +++++--- ...cceptanceTests.AcceptanceTest.verified.txt | 4 ++ .../ObjectPrinterAcceptanceTests.cs | 31 +++++---- .../ObjectPrintingTests.csproj | 6 ++ ...ments_WhenElementsMatchConfig.verified.txt | 11 ++++ ...xedElementsOnly_WhenSpecified.verified.txt | 28 ++++++++ ...tResult_WhenCollectionIsEmpty.verified.txt | 9 +++ ...onElementsWithCyclicReference.verified.txt | 24 +++++++ ...tResult_WithNestedCollections.verified.txt | 28 ++++++++ ...Line_WhenCollectionIsExcluded.verified.txt | 15 +++++ ...fCollectionElementsIsExcluded.verified.txt | 18 ++++++ .../PrintingConfigEnumerableTests.cs | 40 +++++++++--- .../TestData/ComplexEnumerable.cs | 20 ++++++ .../TestData/PersonEnumerable.cs | 8 --- .../TestData/TestDataFactory.cs | 2 + 16 files changed, 284 insertions(+), 52 deletions(-) create mode 100644 ObjectPrintingTests/ObjectPrinterAcceptanceTests.AcceptanceTest.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_AppliesConfigToCollectionElements_WhenElementsMatchConfig.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_AppliesConfigToMembersOfIndexedElementsOnly_WhenSpecified.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WhenCollectionIsEmpty.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WithCollectionElementsWithCyclicReference.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WithNestedCollections.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsEmptyLine_WhenCollectionIsExcluded.verified.txt create mode 100644 ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsEmptyLine_WhenTypeOfCollectionElementsIsExcluded.verified.txt create mode 100644 ObjectPrintingTests/TestData/ComplexEnumerable.cs delete mode 100644 ObjectPrintingTests/TestData/PersonEnumerable.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 87609440..35e6f131 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -88,7 +88,7 @@ private string PrintToString(object obj, int nestingLevel, string memberPrefix, { if (parents.Contains(obj)) return "CYCLIC_REFERENCE" + Environment.NewLine; - + if (obj is null) return "null" + Environment.NewLine; @@ -107,28 +107,68 @@ private string PrintToString(object obj, int nestingLevel, string memberPrefix, if (finalTypes.Contains(obj.GetType())) return PrintWithCultureIfExists(obj, obj + Environment.NewLine); - // if (obj is IEnumerable enumerable) - // return PrintEnumerable(enumerable, nestingLevel, memberPrefix, parents); - + if (obj is IEnumerable enumerable) + return PrintEnumerable(enumerable, nestingLevel, memberPrefix, parents); + return PrintObjectMembers(obj, nestingLevel, memberPrefix, parents); } - /*private string PrintEnumerable(IEnumerable obj, int nestingLevel, string memberPrefix, List parents) + private string PrintEnumerable(IEnumerable obj, int nestingLevel, string memberPrefix, List parents) { var enumerator = obj.GetEnumerator(); using var disposable = enumerator as IDisposable; if (!enumerator.MoveNext()) return "EMPTY_COLLECTION" + Environment.NewLine; - if (excludedTypes.Contains(enumerator.Current!.GetType())) - return ""; + if (obj is IList indexed) + return PrintIndexed(indexed, nestingLevel, memberPrefix, parents); + if (obj is IDictionary dictionary) + return PrintDictionary(dictionary, nestingLevel, memberPrefix, parents); + return "NOT_SUPPORTED_COLLECTION" + Environment.NewLine; + } + + private string PrintCollectionElement(string memberPrefix, string itemIdentificator, string indentation, + object element, int nestingLevel, List parents) + { var sb = new StringBuilder(); - enumerator.Reset(); - while (enumerator.MoveNext()) + var elementName = memberPrefix + $".get_Item({itemIdentificator})"; + sb.Append(indentation); + sb.Append(PrintToString(element, nestingLevel + 1, elementName, parents)); + sb.Remove(sb.Length - Environment.NewLine.Length, Environment.NewLine.Length); + return sb.ToString(); + } + + private string PrintDictionary(IDictionary dictionary, int nestingLevel, string memberPrefix, List parents) + { + var indentation = new string('\t', nestingLevel + 1); + var sb = new StringBuilder("{" + Environment.NewLine); + foreach (DictionaryEntry entry in dictionary) { - + sb.Append(PrintCollectionElement(memberPrefix, "DICTIONARY_ELEMENT", indentation, entry, nestingLevel, parents)); + sb.AppendLine(","); } - }*/ + + sb.Remove(sb.Length - Environment.NewLine.Length - 1, Environment.NewLine.Length + 1); + sb.AppendLine(); + sb.AppendLine(indentation[..^1] + "}"); + return sb.ToString(); + } + + private string PrintIndexed(IList indexed, int nestingLevel, string memberPrefix, List parents) + { + var indentation = new string('\t', nestingLevel + 1); + var sb = new StringBuilder("[" + Environment.NewLine); + for (var i = 0; i < indexed.Count; i++) + { + sb.Append(PrintCollectionElement(memberPrefix, i.ToString(), indentation, indexed[i], nestingLevel, parents)); + if (i != indexed.Count - 1) + sb.AppendLine(","); + } + + sb.AppendLine(); + sb.AppendLine(indentation[..^1] + "]"); + return sb.ToString(); + } private static string CutString(string s, int maxLength) { @@ -172,7 +212,7 @@ private string ProcessMember(Type type, string name, object value, string indent return ""; sb.Append(indentation + name + " = "); - + sb.Append(CustomMemberSerialization.TryGetValue(memberPrefix + name, out var serialization) ? serialization(value) + Environment.NewLine : PrintToString(value, nestingLevel + 1, memberPrefix + name, parents)); diff --git a/ObjectPrinting/PrintingMemberConfigUtils.cs b/ObjectPrinting/PrintingMemberConfigUtils.cs index ef44c8ba..b9771896 100644 --- a/ObjectPrinting/PrintingMemberConfigUtils.cs +++ b/ObjectPrinting/PrintingMemberConfigUtils.cs @@ -9,20 +9,30 @@ public static class PrintingMemberConfigUtils public static string GetFullMemberName(Expression> expression) { PrintingConfigUtils.ValidateMemberExpression(expression); - var memberExpression = expression.Body as MemberExpression; - + var expressionBody = expression.Body; + var memberNames = new List(); - while (memberExpression != null) + while (expressionBody != null) { - memberNames.Add(memberExpression.Member.Name); - if (memberExpression.Expression is MemberExpression nestedMemberExpression) - memberExpression = nestedMemberExpression; - else - memberExpression = null; + switch (expressionBody) + { + case MemberExpression memberExpression: + memberNames.Add(memberExpression.Member.Name); + expressionBody = memberExpression.Expression; + continue; + case MethodCallExpression {Method.Name: "get_Item"} methodCallExpression: + memberNames.Add($"get_Item({methodCallExpression.Arguments[0]})"); + expressionBody = methodCallExpression.Object; + continue; + default: + expressionBody = null; + break; + } } - + memberNames.Reverse(); memberNames.Insert(0, typeof(TOwner).Name); + return string.Join(".", memberNames); } } \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrinterAcceptanceTests.AcceptanceTest.verified.txt b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.AcceptanceTest.verified.txt new file mode 100644 index 00000000..be147bbf --- /dev/null +++ b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.AcceptanceTest.verified.txt @@ -0,0 +1,4 @@ +Person + Id = this is guid + Name = Al + Height = 192,8 diff --git a/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs index 763a8b5d..054c0819 100644 --- a/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs @@ -1,20 +1,25 @@ -using ObjectPrinting; +using System.Globalization; +using ObjectPrinting; using ObjectPrintingTests.TestData; -namespace ObjectPrintingTests +namespace ObjectPrintingTests; + +[TestFixture] +public class ObjectPrinterAcceptanceTests { - [TestFixture] - public class ObjectPrinterAcceptanceTests + [Test] + public async Task AcceptanceTest() { - [Test] - public void AcceptanceTest() - { - var person = new Person(new Guid(), "Alex", 192.8, 33); + var person = new Person(new Guid(), "Alex", 192.8, 33); + + var printer = ObjectPrinter.For() + .ExcludeType() + .SetCultureForType(new CultureInfo("ru")) + .SelectMember(m => m.Name) + .SetStringMaxLength(2) + .SelectMember(m => m.Id) + .WithSerialization(_ => "this is guid"); - var printer = ObjectPrinter.For() - .ExcludeType(); - - var result = printer.PrintToString(person); - } + await Verify(printer.PrintToString(person)); } } \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrintingTests.csproj b/ObjectPrintingTests/ObjectPrintingTests.csproj index f64b8842..62ab758f 100644 --- a/ObjectPrintingTests/ObjectPrintingTests.csproj +++ b/ObjectPrintingTests/ObjectPrintingTests.csproj @@ -48,6 +48,12 @@ PrintingConfigCutStringTests.cs + + PrintingConfigEnumerableTests.cs + + + ObjectPrinterAcceptanceTests.cs + diff --git a/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_AppliesConfigToCollectionElements_WhenElementsMatchConfig.verified.txt b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_AppliesConfigToCollectionElements_WhenElementsMatchConfig.verified.txt new file mode 100644 index 00000000..3387bcac --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_AppliesConfigToCollectionElements_WhenElementsMatchConfig.verified.txt @@ -0,0 +1,11 @@ +EnumerableTest + Array = [ + 1,2, + 2,12, + 3,2, + 4 + ] + List = [ + ComplexPerson object + ] + Dictionary = this is a dictionary object diff --git a/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_AppliesConfigToMembersOfIndexedElementsOnly_WhenSpecified.verified.txt b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_AppliesConfigToMembersOfIndexedElementsOnly_WhenSpecified.verified.txt new file mode 100644 index 00000000..3a3b3c34 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_AppliesConfigToMembersOfIndexedElementsOnly_WhenSpecified.verified.txt @@ -0,0 +1,28 @@ +ComplexEnumerable + List = [ + [ + 2 + ], + [ + 2, + 3 + ] + ] + Persons = [ + Person + Id = 00000000-0000-0000-0000-000000000000 + Name = 123123 + Age = 33 + Height = 192,2 + ] + Dictionary = { + DictionaryEntry + Key = asd + Value = ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 + } diff --git a/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WhenCollectionIsEmpty.verified.txt b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WhenCollectionIsEmpty.verified.txt new file mode 100644 index 00000000..97a83212 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WhenCollectionIsEmpty.verified.txt @@ -0,0 +1,9 @@ +EnumerableTest + Array = [ + 1,2, + 2,12, + 3,2, + 4 + ] + List = EMPTY_COLLECTION + Dictionary = EMPTY_COLLECTION diff --git a/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WithCollectionElementsWithCyclicReference.verified.txt b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WithCollectionElementsWithCyclicReference.verified.txt new file mode 100644 index 00000000..70ea347e --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WithCollectionElementsWithCyclicReference.verified.txt @@ -0,0 +1,24 @@ +EnumerableTest + Array = [ + 1,2, + 2,12, + 3,2, + 4 + ] + List = [ + ComplexPerson + Parent = CYCLIC_REFERENCE + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 + ] + Dictionary = { + DictionaryEntry + Key = 1 + Value = 01.01.0001 0:00:00, + DictionaryEntry + Key = 3 + Value = 28.02.1988 0:00:00 + } diff --git a/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WithNestedCollections.verified.txt b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WithNestedCollections.verified.txt new file mode 100644 index 00000000..39e0e09d --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsCorrectResult_WithNestedCollections.verified.txt @@ -0,0 +1,28 @@ +ComplexEnumerable + List = [ + [ + 2 + ], + [ + 2, + 3 + ] + ] + Persons = [ + Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Age = 33 + Height = 192,2 + ] + Dictionary = { + DictionaryEntry + Key = asd + Value = ComplexPerson + Parent = null + Id = 00000000-0000-0000-0000-000000000000 + Name = Ivan + Age = 30 + Height = 190,1 + Weight = 0 + } diff --git a/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsEmptyLine_WhenCollectionIsExcluded.verified.txt b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsEmptyLine_WhenCollectionIsExcluded.verified.txt new file mode 100644 index 00000000..633d257b --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsEmptyLine_WhenCollectionIsExcluded.verified.txt @@ -0,0 +1,15 @@ +EnumerableTest + Array = [ + 1,2, + 2,12, + 3,2, + 4 + ] + Dictionary = { + DictionaryEntry + Key = 1 + Value = 01.01.0001 0:00:00, + DictionaryEntry + Key = 3 + Value = 28.02.1988 0:00:00 + } diff --git a/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsEmptyLine_WhenTypeOfCollectionElementsIsExcluded.verified.txt b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsEmptyLine_WhenTypeOfCollectionElementsIsExcluded.verified.txt new file mode 100644 index 00000000..f8e21f83 --- /dev/null +++ b/ObjectPrintingTests/PrintingConfigEnumerableTests.PrintToString_PrintsEmptyLine_WhenTypeOfCollectionElementsIsExcluded.verified.txt @@ -0,0 +1,18 @@ +EnumerableTest + Array = [ + 1,2, + 2,12, + 3,2, + 4 + ] + List = [ + + ] + Dictionary = { + DictionaryEntry + Key = 1 + Value = 01.01.0001 0:00:00, + DictionaryEntry + Key = 3 + Value = 28.02.1988 0:00:00 + } diff --git a/ObjectPrintingTests/PrintingConfigEnumerableTests.cs b/ObjectPrintingTests/PrintingConfigEnumerableTests.cs index f1fa6b35..6189ac7c 100644 --- a/ObjectPrintingTests/PrintingConfigEnumerableTests.cs +++ b/ObjectPrintingTests/PrintingConfigEnumerableTests.cs @@ -9,38 +9,38 @@ public class PrintingConfigEnumerableTests [Test] public async Task PrintToString_PrintsEmptyLine_WhenCollectionIsExcluded() { - var reader = ObjectPrinter.For() + var printer = ObjectPrinter.For() .ExcludeType>(); - await Verify(reader.PrintToString(TestDataFactory.EnumerableSimpleTest)); + await Verify(printer.PrintToString(TestDataFactory.EnumerableSimpleTest)); } [Test] public async Task PrintToString_PrintsEmptyLine_WhenTypeOfCollectionElementsIsExcluded() { - var reader = ObjectPrinter.For() + var printer = ObjectPrinter.For() .ExcludeType(); - await Verify(reader.PrintToString(TestDataFactory.EnumerableSimpleTest)); + await Verify(printer.PrintToString(TestDataFactory.EnumerableSimpleTest)); } [Test] public async Task PrintToString_PrintsCorrectResult_WhenCollectionIsEmpty() { - var reader = ObjectPrinter.For(); + var printer = ObjectPrinter.For(); - await Verify(reader.PrintToString(TestDataFactory.EnumerableEmptyTest)); + await Verify(printer.PrintToString(TestDataFactory.EnumerableEmptyTest)); } [Test] public async Task PrintToString_AppliesConfigToCollectionElements_WhenElementsMatchConfig() { - var reader = ObjectPrinter.For() + var printer = ObjectPrinter.For() .SetCultureForType(new CultureInfo("ru")) .WithSerializationForType>(_ => "this is a dictionary object") .WithSerializationForType(_ => "ComplexPerson object"); - await Verify(reader.PrintToString(TestDataFactory.EnumerableSimpleTest)); + await Verify(printer.PrintToString(TestDataFactory.EnumerableSimpleTest)); } [Test] @@ -48,11 +48,31 @@ public async Task PrintToString_PrintsCorrectResult_WithCollectionElementsWithCy { TestDataFactory.EnumerableSimpleTest.List[0].Parent = TestDataFactory.EnumerableSimpleTest.List[0]; - var reader = ObjectPrinter.For(); + var printer = ObjectPrinter.For(); - await Verify(reader.PrintToString(TestDataFactory.EnumerableSimpleTest)); + await Verify(printer.PrintToString(TestDataFactory.EnumerableSimpleTest)); } + [Test] + public async Task PrintToString_PrintsCorrectResult_WithNestedCollections() + { + var printer = ObjectPrinter.For(); + + await Verify(printer.PrintToString(TestDataFactory.ComplexEnumerable)); + } + + [Test] + public async Task PrintToString_AppliesConfigToMembersOfIndexedElementsOnly_WhenSpecified() + { + var printer = ObjectPrinter.For() + .SelectMember(e => e.Persons[0].Name) + .WithSerialization(_ => "123123") + .SelectMember(e => e.Dictionary["asd"].Age) + .WithSerialization(_ => "age"); + + await Verify(printer.PrintToString(TestDataFactory.ComplexEnumerable)); + } + [TearDown] public void TearDown() { diff --git a/ObjectPrintingTests/TestData/ComplexEnumerable.cs b/ObjectPrintingTests/TestData/ComplexEnumerable.cs new file mode 100644 index 00000000..5f517dfb --- /dev/null +++ b/ObjectPrintingTests/TestData/ComplexEnumerable.cs @@ -0,0 +1,20 @@ +namespace ObjectPrintingTests.TestData; + +public class ComplexEnumerable +{ + public List> List = new() + { + new List {2}, + new List {2, 3} + }; + + public readonly List Persons = new() + { + TestDataFactory.Person + }; + + public readonly Dictionary Dictionary = new() + { + {"asd", TestDataFactory.ComplexPerson} + }; +} \ No newline at end of file diff --git a/ObjectPrintingTests/TestData/PersonEnumerable.cs b/ObjectPrintingTests/TestData/PersonEnumerable.cs deleted file mode 100644 index 887ba06e..00000000 --- a/ObjectPrintingTests/TestData/PersonEnumerable.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ObjectPrintingTests.TestData; - -public class PersonEnumerable -{ - public int Field = 2; - public TimeSpan Property { get; set; } - public List List { get; set; } = new(); -} \ No newline at end of file diff --git a/ObjectPrintingTests/TestData/TestDataFactory.cs b/ObjectPrintingTests/TestData/TestDataFactory.cs index 44df1f31..4fd2e783 100644 --- a/ObjectPrintingTests/TestData/TestDataFactory.cs +++ b/ObjectPrintingTests/TestData/TestDataFactory.cs @@ -13,4 +13,6 @@ public static class TestDataFactory public static readonly EnumerableTest EnumerableEmptyTest = new(new List(), new Dictionary()); + + public static readonly ComplexEnumerable ComplexEnumerable = new(); } \ No newline at end of file