From 5b61d869754b2e30e32dbba7d3420cf2028343ee Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Sun, 24 Dec 2023 00:18:19 +0500 Subject: [PATCH 01/13] Project refactoring --- .../{Solved => }/ObjectExtensions.cs | 2 +- ObjectPrinting/ObjectPrinting.csproj | 5 -- ObjectPrinting/PrintingConfig.cs | 23 ++++++- .../{Solved => }/PropertyPrintingConfig.cs | 2 +- .../PropertyPrintingConfigExtensions.cs | 2 +- ObjectPrinting/Solved/ObjectPrinter.cs | 10 --- ObjectPrinting/Solved/PrintingConfig.cs | 62 ------------------- ObjectPrinting/Solved/Tests/Person.cs | 12 ---- .../Tests/ObjectPrinterAcceptanceTests.cs | 27 -------- .../ObjectPrinterAcceptanceTests.cs | 7 ++- .../ObjectPrinting_Should.csproj | 24 +++++++ .../Tests => ObjectPrinting_Should}/Person.cs | 2 +- ObjectPrinting_Should/Usings.cs | 1 + fluent-api.sln | 18 ++++-- 14 files changed, 67 insertions(+), 130 deletions(-) rename ObjectPrinting/{Solved => }/ObjectExtensions.cs (85%) rename ObjectPrinting/{Solved => }/PropertyPrintingConfig.cs (96%) rename ObjectPrinting/{Solved => }/PropertyPrintingConfigExtensions.cs (94%) delete mode 100644 ObjectPrinting/Solved/ObjectPrinter.cs delete mode 100644 ObjectPrinting/Solved/PrintingConfig.cs delete mode 100644 ObjectPrinting/Solved/Tests/Person.cs delete mode 100644 ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs rename {ObjectPrinting/Solved/Tests => ObjectPrinting_Should}/ObjectPrinterAcceptanceTests.cs (96%) create mode 100644 ObjectPrinting_Should/ObjectPrinting_Should.csproj rename {ObjectPrinting/Tests => ObjectPrinting_Should}/Person.cs (87%) create mode 100644 ObjectPrinting_Should/Usings.cs diff --git a/ObjectPrinting/Solved/ObjectExtensions.cs b/ObjectPrinting/ObjectExtensions.cs similarity index 85% rename from ObjectPrinting/Solved/ObjectExtensions.cs rename to ObjectPrinting/ObjectExtensions.cs index b0c94553..87976203 100644 --- a/ObjectPrinting/Solved/ObjectExtensions.cs +++ b/ObjectPrinting/ObjectExtensions.cs @@ -1,4 +1,4 @@ -namespace ObjectPrinting.Solved +namespace ObjectPrinting { public static class ObjectExtensions { diff --git a/ObjectPrinting/ObjectPrinting.csproj b/ObjectPrinting/ObjectPrinting.csproj index 1c5eaf1c..d7f3a084 100644 --- a/ObjectPrinting/ObjectPrinting.csproj +++ b/ObjectPrinting/ObjectPrinting.csproj @@ -6,9 +6,4 @@ false - - - - - diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a9e08211..1433d519 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,11 +1,32 @@ -using System; +using System; using System.Linq; +using System.Linq.Expressions; using System.Text; namespace ObjectPrinting { public class PrintingConfig { + public PropertyPrintingConfig Printing() + { + return new PropertyPrintingConfig(this); + } + + public PropertyPrintingConfig Printing(Expression> memberSelector) + { + return new PropertyPrintingConfig(this); + } + + public PrintingConfig Excluding(Expression> memberSelector) + { + return this; + } + + public PrintingConfig Excluding() + { + return this; + } + public string PrintToString(TOwner obj) { return PrintToString(obj, 0); diff --git a/ObjectPrinting/Solved/PropertyPrintingConfig.cs b/ObjectPrinting/PropertyPrintingConfig.cs similarity index 96% rename from ObjectPrinting/Solved/PropertyPrintingConfig.cs rename to ObjectPrinting/PropertyPrintingConfig.cs index a509697d..84f537b8 100644 --- a/ObjectPrinting/Solved/PropertyPrintingConfig.cs +++ b/ObjectPrinting/PropertyPrintingConfig.cs @@ -1,7 +1,7 @@ using System; using System.Globalization; -namespace ObjectPrinting.Solved +namespace ObjectPrinting { public class PropertyPrintingConfig : IPropertyPrintingConfig { diff --git a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/PropertyPrintingConfigExtensions.cs similarity index 94% rename from ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs rename to ObjectPrinting/PropertyPrintingConfigExtensions.cs index dd392239..1e192203 100644 --- a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs +++ b/ObjectPrinting/PropertyPrintingConfigExtensions.cs @@ -1,6 +1,6 @@ using System; -namespace ObjectPrinting.Solved +namespace ObjectPrinting { public static class PropertyPrintingConfigExtensions { diff --git a/ObjectPrinting/Solved/ObjectPrinter.cs b/ObjectPrinting/Solved/ObjectPrinter.cs deleted file mode 100644 index 540ee769..00000000 --- a/ObjectPrinting/Solved/ObjectPrinter.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ObjectPrinting.Solved -{ - public class ObjectPrinter - { - public static PrintingConfig For() - { - return new PrintingConfig(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PrintingConfig.cs b/ObjectPrinting/Solved/PrintingConfig.cs deleted file mode 100644 index 0ec5aeb2..00000000 --- a/ObjectPrinting/Solved/PrintingConfig.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Text; - -namespace ObjectPrinting.Solved -{ - public class PrintingConfig - { - public PropertyPrintingConfig Printing() - { - return new PropertyPrintingConfig(this); - } - - public PropertyPrintingConfig Printing(Expression> memberSelector) - { - return new PropertyPrintingConfig(this); - } - - public PrintingConfig Excluding(Expression> memberSelector) - { - return this; - } - - internal PrintingConfig Excluding() - { - return this; - } - - public string PrintToString(TOwner obj) - { - return PrintToString(obj, 0); - } - - private string PrintToString(object obj, int nestingLevel) - { - //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(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/Tests/Person.cs b/ObjectPrinting/Solved/Tests/Person.cs deleted file mode 100644 index 858ebbf8..00000000 --- a/ObjectPrinting/Solved/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Solved.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/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/Solved/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs similarity index 96% rename from ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs rename to ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index ac52d5ee..7e20d7b3 100644 --- a/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -1,8 +1,9 @@ using System; using System.Globalization; using NUnit.Framework; +using ObjectPrinting; -namespace ObjectPrinting.Solved.Tests +namespace ObjectPrinting_Should { [TestFixture] public class ObjectPrinterAcceptanceTests @@ -26,10 +27,10 @@ public void Demo() .Excluding(p => p.Age); string s1 = printer.PrintToString(person); - + //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию string s2 = person.PrintToString(); - + //8. ...с конфигурированием string s3 = person.PrintToString(s => s.Excluding(p => p.Age)); Console.WriteLine(s1); diff --git a/ObjectPrinting_Should/ObjectPrinting_Should.csproj b/ObjectPrinting_Should/ObjectPrinting_Should.csproj new file mode 100644 index 00000000..fb63b262 --- /dev/null +++ b/ObjectPrinting_Should/ObjectPrinting_Should.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + enable + enable + + false + true + + + + + + + + + + + + + + + diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting_Should/Person.cs similarity index 87% rename from ObjectPrinting/Tests/Person.cs rename to ObjectPrinting_Should/Person.cs index f9555955..3eac91f3 100644 --- a/ObjectPrinting/Tests/Person.cs +++ b/ObjectPrinting_Should/Person.cs @@ -1,6 +1,6 @@ using System; -namespace ObjectPrinting.Tests +namespace ObjectPrinting_Should { public class Person { diff --git a/ObjectPrinting_Should/Usings.cs b/ObjectPrinting_Should/Usings.cs new file mode 100644 index 00000000..cefced49 --- /dev/null +++ b/ObjectPrinting_Should/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/fluent-api.sln b/fluent-api.sln index 69c8db9e..2de370c9 100644 --- a/fluent-api.sln +++ b/fluent-api.sln @@ -1,17 +1,19 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33815.320 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrinting", "ObjectPrinting\ObjectPrinting.csproj", "{07B8C9B7-8289-46CB-9875-048A57758EEE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObjectPrinting", "ObjectPrinting\ObjectPrinting.csproj", "{07B8C9B7-8289-46CB-9875-048A57758EEE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{6D308E4A-CEC7-4536-9B87-81CD337A87AD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping", "Samples\FluentMapper\FluentMapping.csproj", "{FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentMapping", "Samples\FluentMapper\FluentMapping.csproj", "{FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping.Tests", "Samples\FluentMapper.Tests\FluentMapping.Tests.csproj", "{8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentMapping.Tests", "Samples\FluentMapper.Tests\FluentMapping.Tests.csproj", "{8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrinting_Should", "ObjectPrinting_Should\ObjectPrinting_Should.csproj", "{46DA74C4-460B-4AA0-8647-E1204FF8125D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -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 + {46DA74C4-460B-4AA0-8647-E1204FF8125D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46DA74C4-460B-4AA0-8647-E1204FF8125D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46DA74C4-460B-4AA0-8647-E1204FF8125D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46DA74C4-460B-4AA0-8647-E1204FF8125D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 6e7a1aaf1c190cad3079330b1451e787b0a12b65 Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Sun, 24 Dec 2023 02:48:10 +0500 Subject: [PATCH 02/13] Tests and excluded realisation --- ObjectPrinting/PrintingConfig.cs | 16 ++++ .../ObjectPrinterAcceptanceTests.cs | 79 ++++++++++++++++++- .../ObjectPrinting_Should.csproj | 1 + 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 1433d519..4cfff79a 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,12 +1,17 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using System.Text; namespace ObjectPrinting { public class PrintingConfig { + private HashSet excludedTypes = new HashSet(); + private HashSet excludedProperites = new HashSet(); + public PropertyPrintingConfig Printing() { return new PropertyPrintingConfig(this); @@ -19,11 +24,14 @@ public PropertyPrintingConfig Printing(Expression< public PrintingConfig Excluding(Expression> memberSelector) { + var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo; + excludedProperites.Add(propInfo); return this; } public PrintingConfig Excluding() { + excludedTypes.Add(typeof(TPropType)); return this; } @@ -52,11 +60,19 @@ private string PrintToString(object obj, int nestingLevel) sb.AppendLine(type.Name); foreach (var propertyInfo in type.GetProperties()) { + if (Excluded(propertyInfo)) + continue; + sb.Append(identation + propertyInfo.Name + " = " + PrintToString(propertyInfo.GetValue(obj), nestingLevel + 1)); } return sb.ToString(); } + + private bool Excluded(PropertyInfo propertyInfo) + { + return excludedTypes.Contains(propertyInfo.PropertyType) || excludedProperites.Contains(propertyInfo); + } } } \ No newline at end of file diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index 7e20d7b3..ee3c68fc 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using FluentAssertions; using NUnit.Framework; using ObjectPrinting; @@ -8,11 +9,17 @@ namespace ObjectPrinting_Should [TestFixture] public class ObjectPrinterAcceptanceTests { + private Person person = null!; + + [SetUp] + public void Setup() + { + person = new Person { Name = "Alex", Age = 19, Height = 179.5, Id = new Guid() }; + } + [Test] public void Demo() { - var person = new Person { Name = "Alex", Age = 19 }; - var printer = ObjectPrinter.For() //1. Исключить из сериализации свойства определенного типа .Excluding() @@ -21,6 +28,7 @@ public void Demo() //3. Для числовых типов указать культуру .Printing().Using(CultureInfo.InvariantCulture) //4. Настроить сериализацию конкретного свойства + .Printing(p => p.Age).Using(i => i.ToString("X")) //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) .Printing(p => p.Name).TrimmedToLength(10) //6. Исключить из сериализации конкретного свойства @@ -36,6 +44,73 @@ public void Demo() Console.WriteLine(s1); Console.WriteLine(s2); Console.WriteLine(s3); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", ""); + } + + [Test] + public void PrintToString_SkipsExcludedTypes() + { + var printer = ObjectPrinter.For().Excluding(); + + var expectedString = string.Join(Environment.NewLine, "Person", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SkipsExcludedProperty() + { + var printer = ObjectPrinter.For().Excluding(p => p.Id); + + var expectedString = string.Join(Environment.NewLine, "Person", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_UsesCustomSerializator_WhenGivenToType() + { + var printer = ObjectPrinter.For().Printing().Using(i => i.ToString("X")); + + //var change = 19.ToString("X"); //13 + + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_UsesCustomSerialization_WhenGivenToProperty() + { + var printer = ObjectPrinter.For().Printing(p => p.Age).Using(i => i.ToString("X")); + + //var change = 19.ToString("X"); //13 + + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179.5", "\tAge = 13", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_TrimsStringProperties_WhenTrimmingIsSet() + { + var printer = ObjectPrinter.For().Printing(p => p.Name).TrimmedToLength(1); + + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = A", "\tHeight = 179.5", "\tAge = 19", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() + { + var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); + + //var change = 179.5.ToString(CultureInfo.InvariantCulture); 179.5 + + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); } } } \ No newline at end of file diff --git a/ObjectPrinting_Should/ObjectPrinting_Should.csproj b/ObjectPrinting_Should/ObjectPrinting_Should.csproj index fb63b262..23e8a9b0 100644 --- a/ObjectPrinting_Should/ObjectPrinting_Should.csproj +++ b/ObjectPrinting_Should/ObjectPrinting_Should.csproj @@ -10,6 +10,7 @@ + From 91872a6dc2eafa381eae949eca9b0235b0dcb489 Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Sun, 24 Dec 2023 17:03:19 +0500 Subject: [PATCH 03/13] Basic functionality --- ObjectPrinting/PrintingConfig.cs | 34 ++++++++++---- ObjectPrinting/PropertyPrintingConfig.cs | 20 ++++----- .../PropertyPrintingConfigExtensions.cs | 8 +++- ObjectPrinting/TypePrintingConfig.cs | 24 ++++++++++ .../TypePrintingConfigExtensions.cs | 44 +++++++++++++++++++ .../ObjectPrinterAcceptanceTests.cs | 4 +- 6 files changed, 112 insertions(+), 22 deletions(-) create mode 100644 ObjectPrinting/TypePrintingConfig.cs create mode 100644 ObjectPrinting/TypePrintingConfigExtensions.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 4cfff79a..9ccee46f 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -9,17 +9,23 @@ namespace ObjectPrinting { public class PrintingConfig { - private HashSet excludedTypes = new HashSet(); - private HashSet excludedProperites = new HashSet(); + private readonly HashSet excludedTypes = new HashSet(); + private readonly HashSet excludedProperites = new HashSet(); + internal readonly Dictionary> typeSerializers = + new Dictionary>(); - public PropertyPrintingConfig Printing() + internal readonly Dictionary> propertySerializers = + new Dictionary>(); + + public TypePrintingConfig Printing() { - return new PropertyPrintingConfig(this); + return new TypePrintingConfig(this); } public PropertyPrintingConfig Printing(Expression> memberSelector) { - return new PropertyPrintingConfig(this); + var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo; + return new PropertyPrintingConfig(this, propInfo); } public PrintingConfig Excluding(Expression> memberSelector) @@ -51,18 +57,28 @@ private string PrintToString(object obj, int nestingLevel) typeof(int), typeof(double), typeof(float), typeof(string), typeof(DateTime), typeof(TimeSpan) }; - if (finalTypes.Contains(obj.GetType())) + var objType = obj.GetType(); + if (finalTypes.Contains(objType)) + { + if (typeSerializers.TryGetValue(objType, out var serializer)) + return serializer(obj) + Environment.NewLine; 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.AppendLine(objType.Name); + foreach (var propertyInfo in objType.GetProperties()) { if (Excluded(propertyInfo)) continue; + if (propertySerializers.TryGetValue(propertyInfo, out var serializer)) + { + sb.Append(identation + propertyInfo.Name + " = " + serializer(propertyInfo.GetValue(obj)) + Environment.NewLine); + continue; + } + sb.Append(identation + propertyInfo.Name + " = " + PrintToString(propertyInfo.GetValue(obj), nestingLevel + 1)); diff --git a/ObjectPrinting/PropertyPrintingConfig.cs b/ObjectPrinting/PropertyPrintingConfig.cs index 84f537b8..47df3d6d 100644 --- a/ObjectPrinting/PropertyPrintingConfig.cs +++ b/ObjectPrinting/PropertyPrintingConfig.cs @@ -1,28 +1,28 @@ using System; using System.Globalization; +using System.Reflection; namespace ObjectPrinting { public class PropertyPrintingConfig : IPropertyPrintingConfig { - private readonly PrintingConfig printingConfig; + public PrintingConfig PrintingConfig { get; } + public PropertyInfo PropertyInfo { get; } - public PropertyPrintingConfig(PrintingConfig printingConfig) + public PropertyPrintingConfig(PrintingConfig printingConfig, PropertyInfo propertyInfo) { - this.printingConfig = printingConfig; + this.PrintingConfig = printingConfig; + this.PropertyInfo = propertyInfo; } public PrintingConfig Using(Func print) { - return printingConfig; + var castedFunc = new Func((obj) => print((TPropType)obj)); + PrintingConfig.propertySerializers.Add(PropertyInfo, castedFunc); + return PrintingConfig; } - public PrintingConfig Using(CultureInfo culture) - { - return printingConfig; - } - - PrintingConfig IPropertyPrintingConfig.ParentConfig => printingConfig; + PrintingConfig IPropertyPrintingConfig.ParentConfig => PrintingConfig; } public interface IPropertyPrintingConfig diff --git a/ObjectPrinting/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/PropertyPrintingConfigExtensions.cs index 1e192203..97491d6b 100644 --- a/ObjectPrinting/PropertyPrintingConfigExtensions.cs +++ b/ObjectPrinting/PropertyPrintingConfigExtensions.cs @@ -1,4 +1,6 @@ using System; +using System.Reflection; +using System.Text.RegularExpressions; namespace ObjectPrinting { @@ -11,7 +13,11 @@ public static string PrintToString(this T obj, Func, Printi public static PrintingConfig TrimmedToLength(this PropertyPrintingConfig propConfig, int maxLen) { - return ((IPropertyPrintingConfig)propConfig).ParentConfig; + var culturedFunc = new Func(s => s[..Math.Min(maxLen, s.Length)]); + var castedFunc = new Func(obj => culturedFunc((string)obj)); + var parent = propConfig.PrintingConfig; + parent.propertySerializers.Add(propConfig.PropertyInfo, castedFunc); + return parent; } } diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/TypePrintingConfig.cs new file mode 100644 index 00000000..62cb5dcc --- /dev/null +++ b/ObjectPrinting/TypePrintingConfig.cs @@ -0,0 +1,24 @@ +using System.Globalization; +using System; + +namespace ObjectPrinting +{ + public class TypePrintingConfig : IPropertyPrintingConfig + { + private readonly PrintingConfig printingConfig; + + PrintingConfig IPropertyPrintingConfig.ParentConfig => printingConfig; + + public TypePrintingConfig(PrintingConfig printingConfig) + { + this.printingConfig = printingConfig; + } + + public PrintingConfig Using(Func print) + { + var castedFunc = new Func((obj) => print((TPropType)obj)); + printingConfig.typeSerializers.Add(typeof(TPropType), castedFunc); + return printingConfig; + } + } +} \ No newline at end of file diff --git a/ObjectPrinting/TypePrintingConfigExtensions.cs b/ObjectPrinting/TypePrintingConfigExtensions.cs new file mode 100644 index 00000000..32263552 --- /dev/null +++ b/ObjectPrinting/TypePrintingConfigExtensions.cs @@ -0,0 +1,44 @@ +using System; +using System.Globalization; + +namespace ObjectPrinting +{ + public static class TypePrintingConfigExtensions + { + public static PrintingConfig Using(this TypePrintingConfig typeConfig, CultureInfo culture) + { + var culturedFunc = new Func(d => d.ToString(culture)); + var castedFunc = new Func(obj => culturedFunc((double)obj)); + var parent = ((IPropertyPrintingConfig)typeConfig).ParentConfig; + parent.typeSerializers.Add(typeof(double), castedFunc); + return parent; + } + + public static PrintingConfig Using(this TypePrintingConfig typeConfig, CultureInfo culture) + { + var culturedFunc = new Func(f => f.ToString(culture)); + var castedFunc = new Func(obj => culturedFunc((float)obj)); + var parent = ((IPropertyPrintingConfig)typeConfig).ParentConfig; + parent.typeSerializers.Add(typeof(float), castedFunc); + return parent; + } + + public static PrintingConfig Using(this TypePrintingConfig typeConfig, CultureInfo culture) + { + var culturedFunc = new Func(d => d.ToString(culture)); + var castedFunc = new Func(obj => culturedFunc((DateTime)obj)); + var parent = ((IPropertyPrintingConfig)typeConfig).ParentConfig; + parent.typeSerializers.Add(typeof(DateTime), castedFunc); + return parent; + } + + public static PrintingConfig TrimmedToLength(this TypePrintingConfig typeConfig, int maxLen) + { + var culturedFunc = new Func(s => s[..Math.Min(maxLen, s.Length)]); + var castedFunc = new Func(obj => culturedFunc((string)obj)); + var parent = ((IPropertyPrintingConfig)typeConfig).ParentConfig; + parent.typeSerializers.Add(typeof(string), castedFunc); + return parent; + } + } +} \ No newline at end of file diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index ee3c68fc..4da9ba2d 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -86,7 +86,7 @@ public void PrintToString_UsesCustomSerialization_WhenGivenToProperty() //var change = 19.ToString("X"); //13 - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179.5", "\tAge = 13", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -96,7 +96,7 @@ public void PrintToString_TrimsStringProperties_WhenTrimmingIsSet() { var printer = ObjectPrinter.For().Printing(p => p.Name).TrimmedToLength(1); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = A", "\tHeight = 179.5", "\tAge = 19", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = A", "\tHeight = 179,5", "\tAge = 19", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } From dab6760dce1960ab18b0ad99f0c050c51d5ed998 Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Sun, 24 Dec 2023 17:15:14 +0500 Subject: [PATCH 04/13] Test refactoring --- .../ObjectPrinterAcceptanceTests.cs | 92 +++++++++++-------- ObjectPrinting_Should/Person.cs | 1 + 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index 4da9ba2d..71ab88e7 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -17,41 +17,10 @@ public void Setup() person = new Person { Name = "Alex", Age = 19, Height = 179.5, Id = new Guid() }; } - [Test] - public void Demo() - { - var printer = ObjectPrinter.For() - //1. Исключить из сериализации свойства определенного типа - .Excluding() - //2. Указать альтернативный способ сериализации для определенного типа - .Printing().Using(i => i.ToString("X")) - //3. Для числовых типов указать культуру - .Printing().Using(CultureInfo.InvariantCulture) - //4. Настроить сериализацию конкретного свойства - .Printing(p => p.Age).Using(i => i.ToString("X")) - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - .Printing(p => p.Name).TrimmedToLength(10) - //6. Исключить из сериализации конкретного свойства - .Excluding(p => p.Age); - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - string s2 = person.PrintToString(); - - //8. ...с конфигурированием - string s3 = person.PrintToString(s => s.Excluding(p => p.Age)); - Console.WriteLine(s1); - Console.WriteLine(s2); - Console.WriteLine(s3); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", ""); - } - [Test] public void PrintToString_SkipsExcludedTypes() { var printer = ObjectPrinter.For().Excluding(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); @@ -72,8 +41,6 @@ public void PrintToString_UsesCustomSerializator_WhenGivenToType() { var printer = ObjectPrinter.For().Printing().Using(i => i.ToString("X")); - //var change = 19.ToString("X"); //13 - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); @@ -84,8 +51,6 @@ public void PrintToString_UsesCustomSerialization_WhenGivenToProperty() { var printer = ObjectPrinter.For().Printing(p => p.Age).Using(i => i.ToString("X")); - //var change = 19.ToString("X"); //13 - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); @@ -106,11 +71,64 @@ public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() { var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); - //var change = 179.5.ToString(CultureInfo.InvariantCulture); 179.5 - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } + + [Test] + public void PrintToString_SerializesClass_WhenCalledFromItInstance() + { + var outputString = person.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesClass_WhenCalledFromItInstanceWithConfig() + { + var outputString = person.PrintToString(s => s.Excluding(p => p.Age)); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesCyclicReferences() + { + var firstPerson = new Person() { Age = 20, Name = "Ben" }; + var secondPerson = new Person() { Age = 20, Name = "John", Sibling = firstPerson}; + firstPerson.Sibling = secondPerson; + + var outputString = firstPerson.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesArray() + { + var numbers = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var outputString = numbers.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesList() + { + var numbers = new List { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var outputString = numbers.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesDictionary() + { + var numbers = new Dictionary { { "a", 1 }, { "b", 2 }, { "c", 3 } }; + var outputString = numbers.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + outputString.Should().Be(expectedString); + } } } \ No newline at end of file diff --git a/ObjectPrinting_Should/Person.cs b/ObjectPrinting_Should/Person.cs index 3eac91f3..811b5817 100644 --- a/ObjectPrinting_Should/Person.cs +++ b/ObjectPrinting_Should/Person.cs @@ -5,6 +5,7 @@ namespace ObjectPrinting_Should public class Person { public Guid Id { get; set; } + public Person Sibling { get; set; } public string Name { get; set; } public double Height { get; set; } public int Age { get; set; } From dbfabfe85da60c6df37729b5c9c6647864ea8940 Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Sun, 24 Dec 2023 20:08:18 +0500 Subject: [PATCH 05/13] IEnumerable serialization --- ObjectPrinting/PrintingConfig.cs | 37 +++++++++++--- .../ObjectPrinterAcceptanceTests.cs | 48 ++++++++++++------- ObjectPrinting_Should/Person.cs | 3 +- 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 9ccee46f..b326480b 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -11,6 +12,15 @@ public class PrintingConfig { private readonly HashSet excludedTypes = new HashSet(); private readonly HashSet excludedProperites = new HashSet(); + + private readonly Type[] finalTypes = + { + typeof(int), typeof(double), typeof(float), typeof(string), + typeof(DateTime), typeof(TimeSpan) + }; + + private readonly HashSet serializedPropertiesMetadataToken = new HashSet(); + internal readonly Dictionary> typeSerializers = new Dictionary>(); @@ -46,17 +56,28 @@ public string PrintToString(TOwner obj) return PrintToString(obj, 0); } + public string SerializeIEnumerable(IEnumerable enumerable) + { + var sb = new StringBuilder(); + var openingBracket = enumerable is IDictionary ? "{ " : "[ "; + var closingBracket = enumerable is IDictionary ? "}" : "]"; + sb.Append(openingBracket); + foreach (var element in enumerable) + { + sb.Append(element); + sb.Append(" "); + } + + sb.Append(closingBracket); + return sb.ToString(); + } + private string PrintToString(object obj, int nestingLevel) { //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) - }; var objType = obj.GetType(); if (finalTypes.Contains(objType)) { @@ -65,14 +86,18 @@ private string PrintToString(object obj, int nestingLevel) return obj + Environment.NewLine; } + if (obj is IEnumerable enumerable) + return SerializeIEnumerable(enumerable) + Environment.NewLine; + var identation = new string('\t', nestingLevel + 1); var sb = new StringBuilder(); sb.AppendLine(objType.Name); foreach (var propertyInfo in objType.GetProperties()) { - if (Excluded(propertyInfo)) + if (Excluded(propertyInfo) || serializedPropertiesMetadataToken.Contains(propertyInfo.MetadataToken)) continue; + serializedPropertiesMetadataToken.Add(propertyInfo.MetadataToken); if (propertySerializers.TryGetValue(propertyInfo, out var serializer)) { sb.Append(identation + propertyInfo.Name + " = " + serializer(propertyInfo.GetValue(obj)) + Environment.NewLine); diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index 71ab88e7..6629ea3c 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -21,7 +21,7 @@ public void Setup() public void PrintToString_SkipsExcludedTypes() { var printer = ObjectPrinter.For().Excluding(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -31,7 +31,7 @@ public void PrintToString_SkipsExcludedProperty() { var printer = ObjectPrinter.For().Excluding(p => p.Id); - var expectedString = string.Join(Environment.NewLine, "Person", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -41,7 +41,7 @@ public void PrintToString_UsesCustomSerializator_WhenGivenToType() { var printer = ObjectPrinter.For().Printing().Using(i => i.ToString("X")); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -51,7 +51,7 @@ public void PrintToString_UsesCustomSerialization_WhenGivenToProperty() { var printer = ObjectPrinter.For().Printing(p => p.Age).Using(i => i.ToString("X")); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -61,7 +61,7 @@ public void PrintToString_TrimsStringProperties_WhenTrimmingIsSet() { var printer = ObjectPrinter.For().Printing(p => p.Name).TrimmedToLength(1); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = A", "\tHeight = 179,5", "\tAge = 19", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = A", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -71,7 +71,7 @@ public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() { var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -80,7 +80,7 @@ public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() public void PrintToString_SerializesClass_WhenCalledFromItInstance() { var outputString = person.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); outputString.Should().Be(expectedString); } @@ -88,19 +88,31 @@ public void PrintToString_SerializesClass_WhenCalledFromItInstance() public void PrintToString_SerializesClass_WhenCalledFromItInstanceWithConfig() { var outputString = person.PrintToString(s => s.Excluding(p => p.Age)); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tFavouriteNumbers = null", ""); outputString.Should().Be(expectedString); } + //[Test] + //public void PrintToString_SerializesCyclicReferences() + //{ + // var firstPerson = new Person() { Age = 20, Name = "Ben" }; + // var secondPerson = new Person() { Age = 20, Name = "John", Sibling = firstPerson}; + // firstPerson.Sibling = secondPerson; + // //var printer = ObjectPrinter.For().Excluding().Excluding(); + + // var outputString = firstPerson.PrintToString(); + // var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tSibling = Person", "\t\tName = John", "\t\tAge = 20", ""); + // outputString.Should().Be(expectedString); + //} + [Test] - public void PrintToString_SerializesCyclicReferences() + public void PrintToString_SerializesArray_WhenInClass() { - var firstPerson = new Person() { Age = 20, Name = "Ben" }; - var secondPerson = new Person() { Age = 20, Name = "John", Sibling = firstPerson}; - firstPerson.Sibling = secondPerson; - - var outputString = firstPerson.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + person.FavouriteNumbers = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var outputString = person.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", + "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", + "\tFavouriteNumbers = [ 1,1 2,2 3,3 4,4 5,5 ]", ""); outputString.Should().Be(expectedString); } @@ -109,7 +121,7 @@ public void PrintToString_SerializesArray() { var numbers = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; var outputString = numbers.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + var expectedString = string.Join(Environment.NewLine, "[ 1,1 2,2 3,3 4,4 5,5 ]", ""); outputString.Should().Be(expectedString); } @@ -118,7 +130,7 @@ public void PrintToString_SerializesList() { var numbers = new List { 1.1, 2.2, 3.3, 4.4, 5.5 }; var outputString = numbers.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + var expectedString = string.Join(Environment.NewLine, "[ 1,1 2,2 3,3 4,4 5,5 ]", ""); outputString.Should().Be(expectedString); } @@ -127,7 +139,7 @@ public void PrintToString_SerializesDictionary() { var numbers = new Dictionary { { "a", 1 }, { "b", 2 }, { "c", 3 } }; var outputString = numbers.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", ""); + var expectedString = string.Join(Environment.NewLine, "{ [a, 1] [b, 2] [c, 3] }", ""); outputString.Should().Be(expectedString); } } diff --git a/ObjectPrinting_Should/Person.cs b/ObjectPrinting_Should/Person.cs index 811b5817..e6b71dd7 100644 --- a/ObjectPrinting_Should/Person.cs +++ b/ObjectPrinting_Should/Person.cs @@ -5,9 +5,10 @@ namespace ObjectPrinting_Should public class Person { public Guid Id { get; set; } - public Person Sibling { get; set; } + //public Person Sibling { get; set; } public string Name { get; set; } public double Height { get; set; } public int Age { get; set; } + public double[] FavouriteNumbers { get; set; } } } \ No newline at end of file From de8e9f62b90ba41c68e896128b726fab54113e5c Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Sun, 24 Dec 2023 20:35:44 +0500 Subject: [PATCH 06/13] CyclicReference test added with refactoring --- ObjectPrinting/PrintingConfig.cs | 1 - .../ObjectPrinterAcceptanceTests.cs | 55 +++++++++++-------- ObjectPrinting_Should/Person.cs | 2 +- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index b326480b..a7bf6538 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -74,7 +74,6 @@ public string SerializeIEnumerable(IEnumerable enumerable) private string PrintToString(object obj, int nestingLevel) { - //TODO apply configurations if (obj == null) return "null" + Environment.NewLine; diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index 6629ea3c..8067e9ec 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -21,7 +21,8 @@ public void Setup() public void PrintToString_SkipsExcludedTypes() { var printer = ObjectPrinter.For().Excluding(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tSibling = null", + "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -31,7 +32,8 @@ public void PrintToString_SkipsExcludedProperty() { var printer = ObjectPrinter.For().Excluding(p => p.Id); - var expectedString = string.Join(Environment.NewLine, "Person", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tSibling = null", + "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -41,7 +43,8 @@ public void PrintToString_UsesCustomSerializator_WhenGivenToType() { var printer = ObjectPrinter.For().Printing().Using(i => i.ToString("X")); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -51,7 +54,8 @@ public void PrintToString_UsesCustomSerialization_WhenGivenToProperty() { var printer = ObjectPrinter.For().Printing(p => p.Age).Using(i => i.ToString("X")); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -61,7 +65,8 @@ public void PrintToString_TrimsStringProperties_WhenTrimmingIsSet() { var printer = ObjectPrinter.For().Printing(p => p.Name).TrimmedToLength(1); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = A", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + "\tSibling = null", "\tName = A", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -71,7 +76,8 @@ public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() { var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + "\tSibling = null", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); } @@ -80,7 +86,8 @@ public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() public void PrintToString_SerializesClass_WhenCalledFromItInstance() { var outputString = person.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); outputString.Should().Be(expectedString); } @@ -88,30 +95,34 @@ public void PrintToString_SerializesClass_WhenCalledFromItInstance() public void PrintToString_SerializesClass_WhenCalledFromItInstanceWithConfig() { var outputString = person.PrintToString(s => s.Excluding(p => p.Age)); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tFavouriteNumbers = null", ""); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tFavouriteNumbers = null", ""); outputString.Should().Be(expectedString); } - //[Test] - //public void PrintToString_SerializesCyclicReferences() - //{ - // var firstPerson = new Person() { Age = 20, Name = "Ben" }; - // var secondPerson = new Person() { Age = 20, Name = "John", Sibling = firstPerson}; - // firstPerson.Sibling = secondPerson; - // //var printer = ObjectPrinter.For().Excluding().Excluding(); - - // var outputString = firstPerson.PrintToString(); - // var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", "\tSibling = Person", "\t\tName = John", "\t\tAge = 20", ""); - // outputString.Should().Be(expectedString); - //} + [Test] + public void PrintToString_SerializesCyclicReferences() + { + var firstPerson = new Person() { Age = 20, Name = "Ben" }; + var secondPerson = new Person() { Age = 20, Name = "John", Sibling = firstPerson }; + firstPerson.Sibling = secondPerson; + //var printer = ObjectPrinter.For().Excluding().Excluding(); + + var outputString = firstPerson.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + "\tSibling = Person", "\t\tName = John", "\t\tHeight = 0", "\t\tAge = 20", + "\t\tFavouriteNumbers = null", "\tHeight = 0", "\tAge = 20", + "\tFavouriteNumbers = null", ""); + outputString.Should().Be(expectedString); + } [Test] public void PrintToString_SerializesArray_WhenInClass() { person.FavouriteNumbers = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; var outputString = person.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", - "\tId = Guid", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", + var expectedString = string.Join(Environment.NewLine, "Person", + "\tId = Guid", "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = [ 1,1 2,2 3,3 4,4 5,5 ]", ""); outputString.Should().Be(expectedString); } diff --git a/ObjectPrinting_Should/Person.cs b/ObjectPrinting_Should/Person.cs index e6b71dd7..e1d09f3a 100644 --- a/ObjectPrinting_Should/Person.cs +++ b/ObjectPrinting_Should/Person.cs @@ -5,7 +5,7 @@ namespace ObjectPrinting_Should public class Person { public Guid Id { get; set; } - //public Person Sibling { get; set; } + public Person Sibling { get; set; } public string Name { get; set; } public double Height { get; set; } public int Age { get; set; } From b4dffde6f4d7c5f2cbc702efb9d7800e3df61d2d Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Sun, 24 Dec 2023 22:16:00 +0500 Subject: [PATCH 07/13] Now works correctly with cyclic references --- ObjectPrinting/PrintingConfig.cs | 52 +++++++++++++------ .../ObjectPrinterAcceptanceTests.cs | 34 ++++++++++-- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a7bf6538..cfdd7b1e 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -19,8 +20,6 @@ public class PrintingConfig typeof(DateTime), typeof(TimeSpan) }; - private readonly HashSet serializedPropertiesMetadataToken = new HashSet(); - internal readonly Dictionary> typeSerializers = new Dictionary>(); @@ -53,26 +52,45 @@ public PrintingConfig Excluding() public string PrintToString(TOwner obj) { - return PrintToString(obj, 0); + return PrintToString(obj, ImmutableHashSet.Empty); } - public string SerializeIEnumerable(IEnumerable enumerable) + private string SerializeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) { var sb = new StringBuilder(); - var openingBracket = enumerable is IDictionary ? "{ " : "[ "; - var closingBracket = enumerable is IDictionary ? "}" : "]"; - sb.Append(openingBracket); + if (enumerable is IDictionary) + return SerializeIDictionary((IDictionary)enumerable, nestedObjects); + sb.Append("[ "); foreach (var element in enumerable) { - sb.Append(element); + sb.Append(PrintToString(element, nestedObjects).TrimEnd()); sb.Append(" "); } - sb.Append(closingBracket); + sb.Append("]"); return sb.ToString(); } - private string PrintToString(object obj, int nestingLevel) + private string SerializeIDictionary(IDictionary dictionary, ImmutableHashSet nestedObjects) + { + var sb = new StringBuilder(); + var identation = new string('\t', nestedObjects.Count); + sb.Append(identation + "{" + Environment.NewLine); + nestedObjects = nestedObjects.Add(dictionary); + foreach (DictionaryEntry dictionaryEntry in dictionary) + { + sb.Append(identation + "\t[" + Environment.NewLine); + sb.Append("\t" + PrintToString(dictionaryEntry.Key, nestedObjects)); + sb.Append(identation + "\t:" + Environment.NewLine); + sb.Append("\t" + PrintToString(dictionaryEntry.Value, nestedObjects)); + sb.Append(identation + "\t]," + Environment.NewLine); + } + + sb.Append(identation + "}" + Environment.NewLine); + return sb.ToString().TrimEnd(); + } + + private string PrintToString(object obj, ImmutableHashSet nestedObjects) { if (obj == null) return "null" + Environment.NewLine; @@ -86,26 +104,26 @@ private string PrintToString(object obj, int nestingLevel) } if (obj is IEnumerable enumerable) - return SerializeIEnumerable(enumerable) + Environment.NewLine; + return SerializeIEnumerable(enumerable, nestedObjects) + Environment.NewLine; - var identation = new string('\t', nestingLevel + 1); + var identation = new string('\t', nestedObjects.Count + 1); var sb = new StringBuilder(); sb.AppendLine(objType.Name); + nestedObjects = nestedObjects.Add(obj); foreach (var propertyInfo in objType.GetProperties()) { - if (Excluded(propertyInfo) || serializedPropertiesMetadataToken.Contains(propertyInfo.MetadataToken)) + var propertyValue = propertyInfo.GetValue(obj); + if (Excluded(propertyInfo) || nestedObjects.Contains(propertyValue)) continue; - serializedPropertiesMetadataToken.Add(propertyInfo.MetadataToken); if (propertySerializers.TryGetValue(propertyInfo, out var serializer)) { - sb.Append(identation + propertyInfo.Name + " = " + serializer(propertyInfo.GetValue(obj)) + Environment.NewLine); + sb.Append(identation + propertyInfo.Name + " = " + serializer(propertyValue) + Environment.NewLine); continue; } sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); + PrintToString(propertyInfo.GetValue(obj), nestedObjects)); } return sb.ToString(); } diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index 8067e9ec..a6467a25 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -110,8 +110,8 @@ public void PrintToString_SerializesCyclicReferences() var outputString = firstPerson.PrintToString(); var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", - "\tSibling = Person", "\t\tName = John", "\t\tHeight = 0", "\t\tAge = 20", - "\t\tFavouriteNumbers = null", "\tHeight = 0", "\tAge = 20", + "\tSibling = Person", "\t\tId = Guid", "\t\tName = John", "\t\tHeight = 0", "\t\tAge = 20", + "\t\tFavouriteNumbers = null", "\tName = Ben", "\tHeight = 0", "\tAge = 20", "\tFavouriteNumbers = null", ""); outputString.Should().Be(expectedString); } @@ -148,9 +148,33 @@ public void PrintToString_SerializesList() [Test] public void PrintToString_SerializesDictionary() { - var numbers = new Dictionary { { "a", 1 }, { "b", 2 }, { "c", 3 } }; - var outputString = numbers.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "{ [a, 1] [b, 2] [c, 3] }", ""); + var numbers = new Dictionary { { new Person(), 1 }, { new Person(), 2 } }; + var printer = ObjectPrinter.For>().Excluding(); + + var outputString = printer.PrintToString(numbers); + var expectedString = @"{ + [ + Person + Sibling = null + Name = null + Height = 0 + Age = 0 + FavouriteNumbers = null + : + 1 + ], + [ + Person + Sibling = null + Name = null + Height = 0 + Age = 0 + FavouriteNumbers = null + : + 2 + ], +} +"; outputString.Should().Be(expectedString); } } From 3913fd43f8b2292d6c39e01879d2539492fe7b25 Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Mon, 25 Dec 2023 20:13:33 +0500 Subject: [PATCH 08/13] Serializes value types by value. Culture serialization for all culture types --- ObjectPrinting/PrintingConfig.cs | 22 ++++------ ObjectPrinting/TypePrintingConfig.cs | 8 ++++ .../TypePrintingConfigExtensions.cs | 44 ------------------- .../ObjectPrinterAcceptanceTests.cs | 38 ++++++++-------- 4 files changed, 36 insertions(+), 76 deletions(-) delete mode 100644 ObjectPrinting/TypePrintingConfigExtensions.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index cfdd7b1e..a8cc4063 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -12,13 +12,7 @@ namespace ObjectPrinting public class PrintingConfig { private readonly HashSet excludedTypes = new HashSet(); - private readonly HashSet excludedProperites = new HashSet(); - - private readonly Type[] finalTypes = - { - typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) - }; + private readonly HashSet excludedProperties = new HashSet(); internal readonly Dictionary> typeSerializers = new Dictionary>(); @@ -40,7 +34,7 @@ public PropertyPrintingConfig Printing(Expression< public PrintingConfig Excluding(Expression> memberSelector) { var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo; - excludedProperites.Add(propInfo); + excludedProperties.Add(propInfo); return this; } @@ -58,8 +52,8 @@ public string PrintToString(TOwner obj) private string SerializeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) { var sb = new StringBuilder(); - if (enumerable is IDictionary) - return SerializeIDictionary((IDictionary)enumerable, nestedObjects); + if (enumerable is IDictionary dictionary) + return SerializeIDictionary(dictionary, nestedObjects); sb.Append("[ "); foreach (var element in enumerable) { @@ -96,7 +90,7 @@ private string PrintToString(object obj, ImmutableHashSet nestedObjects) return "null" + Environment.NewLine; var objType = obj.GetType(); - if (finalTypes.Contains(objType)) + if (obj.GetType().IsValueType || obj is string) { if (typeSerializers.TryGetValue(objType, out var serializer)) return serializer(obj) + Environment.NewLine; @@ -113,7 +107,7 @@ private string PrintToString(object obj, ImmutableHashSet nestedObjects) foreach (var propertyInfo in objType.GetProperties()) { var propertyValue = propertyInfo.GetValue(obj); - if (Excluded(propertyInfo) || nestedObjects.Contains(propertyValue)) + if (ExcludedFromSerialization(propertyInfo) || nestedObjects.Contains(propertyValue)) continue; if (propertySerializers.TryGetValue(propertyInfo, out var serializer)) @@ -128,9 +122,9 @@ private string PrintToString(object obj, ImmutableHashSet nestedObjects) return sb.ToString(); } - private bool Excluded(PropertyInfo propertyInfo) + private bool ExcludedFromSerialization(PropertyInfo propertyInfo) { - return excludedTypes.Contains(propertyInfo.PropertyType) || excludedProperites.Contains(propertyInfo); + return excludedTypes.Contains(propertyInfo.PropertyType) || excludedProperties.Contains(propertyInfo); } } } \ No newline at end of file diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/TypePrintingConfig.cs index 62cb5dcc..e42783f2 100644 --- a/ObjectPrinting/TypePrintingConfig.cs +++ b/ObjectPrinting/TypePrintingConfig.cs @@ -20,5 +20,13 @@ public PrintingConfig Using(Func print) printingConfig.typeSerializers.Add(typeof(TPropType), castedFunc); return printingConfig; } + + public PrintingConfig Using(CultureInfo culture) + { + var culturedFunc = new Func(d => ((IConvertible)d).ToString(culture)); + var castedFunc = new Func(obj => culturedFunc((TPropType)obj)); + printingConfig.typeSerializers.Add(typeof(TPropType), castedFunc); + return printingConfig; + } } } \ No newline at end of file diff --git a/ObjectPrinting/TypePrintingConfigExtensions.cs b/ObjectPrinting/TypePrintingConfigExtensions.cs deleted file mode 100644 index 32263552..00000000 --- a/ObjectPrinting/TypePrintingConfigExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Globalization; - -namespace ObjectPrinting -{ - public static class TypePrintingConfigExtensions - { - public static PrintingConfig Using(this TypePrintingConfig typeConfig, CultureInfo culture) - { - var culturedFunc = new Func(d => d.ToString(culture)); - var castedFunc = new Func(obj => culturedFunc((double)obj)); - var parent = ((IPropertyPrintingConfig)typeConfig).ParentConfig; - parent.typeSerializers.Add(typeof(double), castedFunc); - return parent; - } - - public static PrintingConfig Using(this TypePrintingConfig typeConfig, CultureInfo culture) - { - var culturedFunc = new Func(f => f.ToString(culture)); - var castedFunc = new Func(obj => culturedFunc((float)obj)); - var parent = ((IPropertyPrintingConfig)typeConfig).ParentConfig; - parent.typeSerializers.Add(typeof(float), castedFunc); - return parent; - } - - public static PrintingConfig Using(this TypePrintingConfig typeConfig, CultureInfo culture) - { - var culturedFunc = new Func(d => d.ToString(culture)); - var castedFunc = new Func(obj => culturedFunc((DateTime)obj)); - var parent = ((IPropertyPrintingConfig)typeConfig).ParentConfig; - parent.typeSerializers.Add(typeof(DateTime), castedFunc); - return parent; - } - - public static PrintingConfig TrimmedToLength(this TypePrintingConfig typeConfig, int maxLen) - { - var culturedFunc = new Func(s => s[..Math.Min(maxLen, s.Length)]); - var castedFunc = new Func(obj => culturedFunc((string)obj)); - var parent = ((IPropertyPrintingConfig)typeConfig).ParentConfig; - parent.typeSerializers.Add(typeof(string), castedFunc); - return parent; - } - } -} \ No newline at end of file diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index a6467a25..b89f1b70 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -1,7 +1,5 @@ -using System; -using System.Globalization; +using System.Globalization; using FluentAssertions; -using NUnit.Framework; using ObjectPrinting; namespace ObjectPrinting_Should @@ -43,7 +41,7 @@ public void PrintToString_UsesCustomSerializator_WhenGivenToType() { var printer = ObjectPrinter.For().Printing().Using(i => i.ToString("X")); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); @@ -54,7 +52,7 @@ public void PrintToString_UsesCustomSerialization_WhenGivenToProperty() { var printer = ObjectPrinter.For().Printing(p => p.Age).Using(i => i.ToString("X")); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); @@ -65,7 +63,7 @@ public void PrintToString_TrimsStringProperties_WhenTrimmingIsSet() { var printer = ObjectPrinter.For().Printing(p => p.Name).TrimmedToLength(1); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", "\tSibling = null", "\tName = A", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); @@ -76,7 +74,7 @@ public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() { var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", "\tSibling = null", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", "\tFavouriteNumbers = null", ""); var outputString = printer.PrintToString(person); outputString.Should().Be(expectedString); @@ -86,7 +84,7 @@ public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() public void PrintToString_SerializesClass_WhenCalledFromItInstance() { var outputString = person.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); outputString.Should().Be(expectedString); } @@ -95,7 +93,7 @@ public void PrintToString_SerializesClass_WhenCalledFromItInstance() public void PrintToString_SerializesClass_WhenCalledFromItInstanceWithConfig() { var outputString = person.PrintToString(s => s.Excluding(p => p.Age)); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tFavouriteNumbers = null", ""); outputString.Should().Be(expectedString); } @@ -109,10 +107,11 @@ public void PrintToString_SerializesCyclicReferences() //var printer = ObjectPrinter.For().Excluding().Excluding(); var outputString = firstPerson.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = Guid", - "\tSibling = Person", "\t\tId = Guid", "\t\tName = John", "\t\tHeight = 0", "\t\tAge = 20", - "\t\tFavouriteNumbers = null", "\tName = Ben", "\tHeight = 0", "\tAge = 20", - "\tFavouriteNumbers = null", ""); + var expectedString = string.Join(Environment.NewLine, "Person", + "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = Person", "\t\tId = 00000000-0000-0000-0000-000000000000", + "\t\tName = John", "\t\tHeight = 0", "\t\tAge = 20", "\t\tFavouriteNumbers = null", + "\tName = Ben", "\tHeight = 0", "\tAge = 20", "\tFavouriteNumbers = null", ""); outputString.Should().Be(expectedString); } @@ -122,7 +121,8 @@ public void PrintToString_SerializesArray_WhenInClass() person.FavouriteNumbers = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; var outputString = person.PrintToString(); var expectedString = string.Join(Environment.NewLine, "Person", - "\tId = Guid", "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", + "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = [ 1,1 2,2 3,3 4,4 5,5 ]", ""); outputString.Should().Be(expectedString); } @@ -149,10 +149,12 @@ public void PrintToString_SerializesList() public void PrintToString_SerializesDictionary() { var numbers = new Dictionary { { new Person(), 1 }, { new Person(), 2 } }; - var printer = ObjectPrinter.For>().Excluding(); + var a = new[] { numbers }; + var b = new HashSet[]> {a,}; + var printer = ObjectPrinter.For[]>>().Excluding(); - var outputString = printer.PrintToString(numbers); - var expectedString = @"{ + var outputString = printer.PrintToString(b); + var expectedString = @"[ [ { [ Person Sibling = null @@ -173,7 +175,7 @@ public void PrintToString_SerializesDictionary() : 2 ], -} +} ] ] "; outputString.Should().Be(expectedString); } From b2cf99cd5a9dc56ef6ece24560290ded1e583918 Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Mon, 25 Dec 2023 23:30:07 +0500 Subject: [PATCH 09/13] Correct using(culture). And better collections serialization --- ObjectPrinting/PrintingConfig.cs | 64 ++++++++++++++----- ObjectPrinting/TypePrintingConfig.cs | 6 +- .../ObjectPrinterAcceptanceTests.cs | 52 ++++++++------- 3 files changed, 80 insertions(+), 42 deletions(-) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a8cc4063..a4c6031d 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -25,7 +25,8 @@ public TypePrintingConfig Printing() return new TypePrintingConfig(this); } - public PropertyPrintingConfig Printing(Expression> memberSelector) + public PropertyPrintingConfig Printing( + Expression> memberSelector) { var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo; return new PropertyPrintingConfig(this, propInfo); @@ -51,37 +52,67 @@ public string PrintToString(TOwner obj) private string SerializeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) { - var sb = new StringBuilder(); if (enumerable is IDictionary dictionary) return SerializeIDictionary(dictionary, nestedObjects); - sb.Append("[ "); + var enumerator = enumerable.GetEnumerator(); + var hasFirstElement = enumerator.MoveNext(); + if (!hasFirstElement) + return "[]"; + var firstItemType = enumerator.Current; + if (firstItemType.GetType().IsValueType || firstItemType is string) + { + return SerializeValueTypeIEnumerable(enumerable, nestedObjects); + } + + return SerializeReferenceTypeIEnumerable(enumerable, nestedObjects); + } + + private string SerializeValueTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) + { + var sb = new List(); + sb.Add("["); foreach (var element in enumerable) { - sb.Append(PrintToString(element, nestedObjects).TrimEnd()); - sb.Append(" "); + var serializedString = PrintToString(element, nestedObjects).Trim(); + sb.Add(serializedString); } - sb.Append("]"); - return sb.ToString(); + sb.Add("]"); + return string.Join(' ', sb); + } + + private string SerializeReferenceTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) + { + var sb = new List(); + var identation = new string('\t', nestedObjects.Count); + sb.Add(identation + "["); + foreach (var element in enumerable) + { + var serializedString = PrintToString(element, nestedObjects.Add(sb)).TrimEnd(); + sb.Add(serializedString); + } + + sb.Add(identation + "]"); + return string.Join(Environment.NewLine, sb); } private string SerializeIDictionary(IDictionary dictionary, ImmutableHashSet nestedObjects) { - var sb = new StringBuilder(); + var sb = new List(); var identation = new string('\t', nestedObjects.Count); - sb.Append(identation + "{" + Environment.NewLine); + sb.Add(identation + "{"); nestedObjects = nestedObjects.Add(dictionary); foreach (DictionaryEntry dictionaryEntry in dictionary) { - sb.Append(identation + "\t[" + Environment.NewLine); - sb.Append("\t" + PrintToString(dictionaryEntry.Key, nestedObjects)); - sb.Append(identation + "\t:" + Environment.NewLine); - sb.Append("\t" + PrintToString(dictionaryEntry.Value, nestedObjects)); - sb.Append(identation + "\t]," + Environment.NewLine); + sb.Add(identation + '\t' + "["); + sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Key, nestedObjects).TrimEnd()); + sb.Add(identation + '\t' + ":"); + sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Value, nestedObjects).TrimEnd()); + sb.Add(identation + '\t' + "]"); } - sb.Append(identation + "}" + Environment.NewLine); - return sb.ToString().TrimEnd(); + sb.Add(identation + "}"); + return string.Join(Environment.NewLine, sb); } private string PrintToString(object obj, ImmutableHashSet nestedObjects) @@ -119,6 +150,7 @@ private string PrintToString(object obj, ImmutableHashSet nestedObjects) sb.Append(identation + propertyInfo.Name + " = " + PrintToString(propertyInfo.GetValue(obj), nestedObjects)); } + return sb.ToString(); } diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/TypePrintingConfig.cs index e42783f2..afa5ffea 100644 --- a/ObjectPrinting/TypePrintingConfig.cs +++ b/ObjectPrinting/TypePrintingConfig.cs @@ -21,10 +21,10 @@ public PrintingConfig Using(Func print) return printingConfig; } - public PrintingConfig Using(CultureInfo culture) + public PrintingConfig Using(CultureInfo culture) where TFormattable : IFormattable { - var culturedFunc = new Func(d => ((IConvertible)d).ToString(culture)); - var castedFunc = new Func(obj => culturedFunc((TPropType)obj)); + var culturedFunc = new Func(d => d.ToString(null, culture)); + var castedFunc = new Func(obj => culturedFunc((TFormattable)obj)); printingConfig.typeSerializers.Add(typeof(TPropType), castedFunc); return printingConfig; } diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index b89f1b70..7ba62a0b 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -72,7 +72,7 @@ public void PrintToString_TrimsStringProperties_WhenTrimmingIsSet() [Test] public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() { - var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); + var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", "\tSibling = null", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", "\tFavouriteNumbers = null", ""); @@ -149,33 +149,39 @@ public void PrintToString_SerializesList() public void PrintToString_SerializesDictionary() { var numbers = new Dictionary { { new Person(), 1 }, { new Person(), 2 } }; - var a = new[] { numbers }; + var a = new[] { numbers , new Dictionary()}; var b = new HashSet[]> {a,}; var printer = ObjectPrinter.For[]>>().Excluding(); var outputString = printer.PrintToString(b); - var expectedString = @"[ [ { + var expectedString = @"[ [ - Person - Sibling = null - Name = null - Height = 0 - Age = 0 - FavouriteNumbers = null - : - 1 - ], - [ - Person - Sibling = null - Name = null - Height = 0 - Age = 0 - FavouriteNumbers = null - : - 2 - ], -} ] ] + { + [ + Person + Sibling = null + Name = null + Height = 0 + Age = 0 + FavouriteNumbers = null + : + 1 + ] + [ + Person + Sibling = null + Name = null + Height = 0 + Age = 0 + FavouriteNumbers = null + : + 2 + ] + } + { + } + ] +] "; outputString.Should().Be(expectedString); } From 9f3f1c9b5e0ae6a7adc9cf855643a1a354f2b76b Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Mon, 25 Dec 2023 23:30:07 +0500 Subject: [PATCH 10/13] Correct using(culture). And better collections serialization --- ObjectPrinting/PrintingConfig.cs | 64 ++++++++++++++----- ObjectPrinting/TypePrintingConfig.cs | 6 +- .../ObjectPrinterAcceptanceTests.cs | 61 +++++++++++------- 3 files changed, 89 insertions(+), 42 deletions(-) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a8cc4063..a4c6031d 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -25,7 +25,8 @@ public TypePrintingConfig Printing() return new TypePrintingConfig(this); } - public PropertyPrintingConfig Printing(Expression> memberSelector) + public PropertyPrintingConfig Printing( + Expression> memberSelector) { var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo; return new PropertyPrintingConfig(this, propInfo); @@ -51,37 +52,67 @@ public string PrintToString(TOwner obj) private string SerializeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) { - var sb = new StringBuilder(); if (enumerable is IDictionary dictionary) return SerializeIDictionary(dictionary, nestedObjects); - sb.Append("[ "); + var enumerator = enumerable.GetEnumerator(); + var hasFirstElement = enumerator.MoveNext(); + if (!hasFirstElement) + return "[]"; + var firstItemType = enumerator.Current; + if (firstItemType.GetType().IsValueType || firstItemType is string) + { + return SerializeValueTypeIEnumerable(enumerable, nestedObjects); + } + + return SerializeReferenceTypeIEnumerable(enumerable, nestedObjects); + } + + private string SerializeValueTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) + { + var sb = new List(); + sb.Add("["); foreach (var element in enumerable) { - sb.Append(PrintToString(element, nestedObjects).TrimEnd()); - sb.Append(" "); + var serializedString = PrintToString(element, nestedObjects).Trim(); + sb.Add(serializedString); } - sb.Append("]"); - return sb.ToString(); + sb.Add("]"); + return string.Join(' ', sb); + } + + private string SerializeReferenceTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) + { + var sb = new List(); + var identation = new string('\t', nestedObjects.Count); + sb.Add(identation + "["); + foreach (var element in enumerable) + { + var serializedString = PrintToString(element, nestedObjects.Add(sb)).TrimEnd(); + sb.Add(serializedString); + } + + sb.Add(identation + "]"); + return string.Join(Environment.NewLine, sb); } private string SerializeIDictionary(IDictionary dictionary, ImmutableHashSet nestedObjects) { - var sb = new StringBuilder(); + var sb = new List(); var identation = new string('\t', nestedObjects.Count); - sb.Append(identation + "{" + Environment.NewLine); + sb.Add(identation + "{"); nestedObjects = nestedObjects.Add(dictionary); foreach (DictionaryEntry dictionaryEntry in dictionary) { - sb.Append(identation + "\t[" + Environment.NewLine); - sb.Append("\t" + PrintToString(dictionaryEntry.Key, nestedObjects)); - sb.Append(identation + "\t:" + Environment.NewLine); - sb.Append("\t" + PrintToString(dictionaryEntry.Value, nestedObjects)); - sb.Append(identation + "\t]," + Environment.NewLine); + sb.Add(identation + '\t' + "["); + sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Key, nestedObjects).TrimEnd()); + sb.Add(identation + '\t' + ":"); + sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Value, nestedObjects).TrimEnd()); + sb.Add(identation + '\t' + "]"); } - sb.Append(identation + "}" + Environment.NewLine); - return sb.ToString().TrimEnd(); + sb.Add(identation + "}"); + return string.Join(Environment.NewLine, sb); } private string PrintToString(object obj, ImmutableHashSet nestedObjects) @@ -119,6 +150,7 @@ private string PrintToString(object obj, ImmutableHashSet nestedObjects) sb.Append(identation + propertyInfo.Name + " = " + PrintToString(propertyInfo.GetValue(obj), nestedObjects)); } + return sb.ToString(); } diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/TypePrintingConfig.cs index e42783f2..afa5ffea 100644 --- a/ObjectPrinting/TypePrintingConfig.cs +++ b/ObjectPrinting/TypePrintingConfig.cs @@ -21,10 +21,10 @@ public PrintingConfig Using(Func print) return printingConfig; } - public PrintingConfig Using(CultureInfo culture) + public PrintingConfig Using(CultureInfo culture) where TFormattable : IFormattable { - var culturedFunc = new Func(d => ((IConvertible)d).ToString(culture)); - var castedFunc = new Func(obj => culturedFunc((TPropType)obj)); + var culturedFunc = new Func(d => d.ToString(null, culture)); + var castedFunc = new Func(obj => culturedFunc((TFormattable)obj)); printingConfig.typeSerializers.Add(typeof(TPropType), castedFunc); return printingConfig; } diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index b89f1b70..1d7872ae 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -72,7 +72,7 @@ public void PrintToString_TrimsStringProperties_WhenTrimmingIsSet() [Test] public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() { - var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); + var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", "\tSibling = null", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", "\tFavouriteNumbers = null", ""); @@ -135,6 +135,15 @@ public void PrintToString_SerializesArray() var expectedString = string.Join(Environment.NewLine, "[ 1,1 2,2 3,3 4,4 5,5 ]", ""); outputString.Should().Be(expectedString); } + + [Test] + public void PrintToString_SerializesEmptyArray() + { + var numbers = Array.Empty(); + var outputString = numbers.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "[]", ""); + outputString.Should().Be(expectedString); + } [Test] public void PrintToString_SerializesList() @@ -149,33 +158,39 @@ public void PrintToString_SerializesList() public void PrintToString_SerializesDictionary() { var numbers = new Dictionary { { new Person(), 1 }, { new Person(), 2 } }; - var a = new[] { numbers }; + var a = new[] { numbers , new Dictionary()}; var b = new HashSet[]> {a,}; var printer = ObjectPrinter.For[]>>().Excluding(); var outputString = printer.PrintToString(b); - var expectedString = @"[ [ { - [ - Person - Sibling = null - Name = null - Height = 0 - Age = 0 - FavouriteNumbers = null - : - 1 - ], + var expectedString = @"[ [ - Person - Sibling = null - Name = null - Height = 0 - Age = 0 - FavouriteNumbers = null - : - 2 - ], -} ] ] + { + [ + Person + Sibling = null + Name = null + Height = 0 + Age = 0 + FavouriteNumbers = null + : + 1 + ] + [ + Person + Sibling = null + Name = null + Height = 0 + Age = 0 + FavouriteNumbers = null + : + 2 + ] + } + { + } + ] +] "; outputString.Should().Be(expectedString); } From 0da4dc255604e71e14664e2fe6c5efbc4f001d8b Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Tue, 26 Dec 2023 01:53:33 +0500 Subject: [PATCH 11/13] Fixed naming in test --- ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index 1d7872ae..dd65c952 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -158,11 +158,11 @@ public void PrintToString_SerializesList() public void PrintToString_SerializesDictionary() { var numbers = new Dictionary { { new Person(), 1 }, { new Person(), 2 } }; - var a = new[] { numbers , new Dictionary()}; - var b = new HashSet[]> {a,}; + var arrayOfDictionaries = new[] { numbers , new Dictionary()}; + var arrayOfDictionariesHashSet = new HashSet[]> {arrayOfDictionaries,}; var printer = ObjectPrinter.For[]>>().Excluding(); - var outputString = printer.PrintToString(b); + var outputString = printer.PrintToString(arrayOfDictionariesHashSet); var expectedString = @"[ [ { From de7ae1bf8b0b7a347a57f0c8379f2704777f4960 Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Tue, 26 Dec 2023 01:59:46 +0500 Subject: [PATCH 12/13] File scoped namespaces and unnecessary usings removal --- ObjectPrinting/ObjectExtensions.cs | 11 +- ObjectPrinting/ObjectPrinter.cs | 11 +- ObjectPrinting/ObjectPrinting.csproj | 2 +- ObjectPrinting/PrintingConfig.cs | 238 +++++++------ ObjectPrinting/PropertyPrintingConfig.cs | 42 ++- .../PropertyPrintingConfigExtensions.cs | 32 +- ObjectPrinting/TypePrintingConfig.cs | 43 ++- .../ObjectPrinterAcceptanceTests.cs | 321 +++++++++--------- ObjectPrinting_Should/Person.cs | 19 +- 9 files changed, 352 insertions(+), 367 deletions(-) diff --git a/ObjectPrinting/ObjectExtensions.cs b/ObjectPrinting/ObjectExtensions.cs index 87976203..6879ab29 100644 --- a/ObjectPrinting/ObjectExtensions.cs +++ b/ObjectPrinting/ObjectExtensions.cs @@ -1,10 +1,9 @@ -namespace ObjectPrinting +namespace ObjectPrinting; + +public static class ObjectExtensions { - public static class ObjectExtensions + public static string PrintToString(this T obj) { - public static string PrintToString(this T obj) - { - return ObjectPrinter.For().PrintToString(obj); - } + return ObjectPrinter.For().PrintToString(obj); } } \ No newline at end of file diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index 3c7867c3..b2ce90a7 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -1,10 +1,9 @@ -namespace ObjectPrinting +namespace ObjectPrinting; + +public 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 d7f3a084..b871c4c9 100644 --- a/ObjectPrinting/ObjectPrinting.csproj +++ b/ObjectPrinting/ObjectPrinting.csproj @@ -1,7 +1,7 @@  - 8 + latest netcoreapp3.1 false diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a4c6031d..277a4a5e 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -2,161 +2,159 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; -namespace ObjectPrinting +namespace ObjectPrinting; + +public class PrintingConfig { - public class PrintingConfig + private readonly HashSet excludedTypes = new HashSet(); + private readonly HashSet excludedProperties = new HashSet(); + + internal readonly Dictionary> typeSerializers = + new Dictionary>(); + + internal readonly Dictionary> propertySerializers = + new Dictionary>(); + + public TypePrintingConfig Printing() { - private readonly HashSet excludedTypes = new HashSet(); - private readonly HashSet excludedProperties = new HashSet(); + return new TypePrintingConfig(this); + } - internal readonly Dictionary> typeSerializers = - new Dictionary>(); + public PropertyPrintingConfig Printing( + Expression> memberSelector) + { + var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo; + return new PropertyPrintingConfig(this, propInfo); + } - internal readonly Dictionary> propertySerializers = - new Dictionary>(); + public PrintingConfig Excluding(Expression> memberSelector) + { + var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo; + excludedProperties.Add(propInfo); + return this; + } - public TypePrintingConfig Printing() - { - return new TypePrintingConfig(this); - } + public PrintingConfig Excluding() + { + excludedTypes.Add(typeof(TPropType)); + return this; + } - public PropertyPrintingConfig Printing( - Expression> memberSelector) - { - var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo; - return new PropertyPrintingConfig(this, propInfo); - } + public string PrintToString(TOwner obj) + { + return PrintToString(obj, ImmutableHashSet.Empty); + } - public PrintingConfig Excluding(Expression> memberSelector) + private string SerializeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) + { + if (enumerable is IDictionary dictionary) + return SerializeIDictionary(dictionary, nestedObjects); + var enumerator = enumerable.GetEnumerator(); + var hasFirstElement = enumerator.MoveNext(); + if (!hasFirstElement) + return "[]"; + var firstItemType = enumerator.Current; + if (firstItemType.GetType().IsValueType || firstItemType is string) { - var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo; - excludedProperties.Add(propInfo); - return this; + return SerializeValueTypeIEnumerable(enumerable, nestedObjects); } - public PrintingConfig Excluding() - { - excludedTypes.Add(typeof(TPropType)); - return this; - } + return SerializeReferenceTypeIEnumerable(enumerable, nestedObjects); + } - public string PrintToString(TOwner obj) + private string SerializeValueTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) + { + var sb = new List(); + sb.Add("["); + foreach (var element in enumerable) { - return PrintToString(obj, ImmutableHashSet.Empty); + var serializedString = PrintToString(element, nestedObjects).Trim(); + sb.Add(serializedString); } - private string SerializeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) - { - if (enumerable is IDictionary dictionary) - return SerializeIDictionary(dictionary, nestedObjects); - var enumerator = enumerable.GetEnumerator(); - var hasFirstElement = enumerator.MoveNext(); - if (!hasFirstElement) - return "[]"; - var firstItemType = enumerator.Current; - if (firstItemType.GetType().IsValueType || firstItemType is string) - { - return SerializeValueTypeIEnumerable(enumerable, nestedObjects); - } - - return SerializeReferenceTypeIEnumerable(enumerable, nestedObjects); - } + sb.Add("]"); + return string.Join(' ', sb); + } - private string SerializeValueTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) + private string SerializeReferenceTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) + { + var sb = new List(); + var identation = new string('\t', nestedObjects.Count); + sb.Add(identation + "["); + foreach (var element in enumerable) { - var sb = new List(); - sb.Add("["); - foreach (var element in enumerable) - { - var serializedString = PrintToString(element, nestedObjects).Trim(); - sb.Add(serializedString); - } - - sb.Add("]"); - return string.Join(' ', sb); + var serializedString = PrintToString(element, nestedObjects.Add(sb)).TrimEnd(); + sb.Add(serializedString); } - private string SerializeReferenceTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet nestedObjects) - { - var sb = new List(); - var identation = new string('\t', nestedObjects.Count); - sb.Add(identation + "["); - foreach (var element in enumerable) - { - var serializedString = PrintToString(element, nestedObjects.Add(sb)).TrimEnd(); - sb.Add(serializedString); - } + sb.Add(identation + "]"); + return string.Join(Environment.NewLine, sb); + } - sb.Add(identation + "]"); - return string.Join(Environment.NewLine, sb); + private string SerializeIDictionary(IDictionary dictionary, ImmutableHashSet nestedObjects) + { + var sb = new List(); + var identation = new string('\t', nestedObjects.Count); + sb.Add(identation + "{"); + nestedObjects = nestedObjects.Add(dictionary); + foreach (DictionaryEntry dictionaryEntry in dictionary) + { + sb.Add(identation + '\t' + "["); + sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Key, nestedObjects).TrimEnd()); + sb.Add(identation + '\t' + ":"); + sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Value, nestedObjects).TrimEnd()); + sb.Add(identation + '\t' + "]"); } - private string SerializeIDictionary(IDictionary dictionary, ImmutableHashSet nestedObjects) - { - var sb = new List(); - var identation = new string('\t', nestedObjects.Count); - sb.Add(identation + "{"); - nestedObjects = nestedObjects.Add(dictionary); - foreach (DictionaryEntry dictionaryEntry in dictionary) - { - sb.Add(identation + '\t' + "["); - sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Key, nestedObjects).TrimEnd()); - sb.Add(identation + '\t' + ":"); - sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Value, nestedObjects).TrimEnd()); - sb.Add(identation + '\t' + "]"); - } + sb.Add(identation + "}"); + return string.Join(Environment.NewLine, sb); + } - sb.Add(identation + "}"); - return string.Join(Environment.NewLine, sb); - } + private string PrintToString(object obj, ImmutableHashSet nestedObjects) + { + if (obj == null) + return "null" + Environment.NewLine; - private string PrintToString(object obj, ImmutableHashSet nestedObjects) + var objType = obj.GetType(); + if (obj.GetType().IsValueType || obj is string) { - if (obj == null) - return "null" + Environment.NewLine; + if (typeSerializers.TryGetValue(objType, out var serializer)) + return serializer(obj) + Environment.NewLine; + return obj + Environment.NewLine; + } - var objType = obj.GetType(); - if (obj.GetType().IsValueType || obj is string) - { - if (typeSerializers.TryGetValue(objType, out var serializer)) - return serializer(obj) + Environment.NewLine; - return obj + Environment.NewLine; - } + if (obj is IEnumerable enumerable) + return SerializeIEnumerable(enumerable, nestedObjects) + Environment.NewLine; - if (obj is IEnumerable enumerable) - return SerializeIEnumerable(enumerable, nestedObjects) + Environment.NewLine; + var identation = new string('\t', nestedObjects.Count + 1); + var sb = new StringBuilder(); + sb.AppendLine(objType.Name); + nestedObjects = nestedObjects.Add(obj); + foreach (var propertyInfo in objType.GetProperties()) + { + var propertyValue = propertyInfo.GetValue(obj); + if (ExcludedFromSerialization(propertyInfo) || nestedObjects.Contains(propertyValue)) + continue; - var identation = new string('\t', nestedObjects.Count + 1); - var sb = new StringBuilder(); - sb.AppendLine(objType.Name); - nestedObjects = nestedObjects.Add(obj); - foreach (var propertyInfo in objType.GetProperties()) + if (propertySerializers.TryGetValue(propertyInfo, out var serializer)) { - var propertyValue = propertyInfo.GetValue(obj); - if (ExcludedFromSerialization(propertyInfo) || nestedObjects.Contains(propertyValue)) - continue; - - if (propertySerializers.TryGetValue(propertyInfo, out var serializer)) - { - sb.Append(identation + propertyInfo.Name + " = " + serializer(propertyValue) + Environment.NewLine); - continue; - } - - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), nestedObjects)); + sb.Append(identation + propertyInfo.Name + " = " + serializer(propertyValue) + Environment.NewLine); + continue; } - return sb.ToString(); + sb.Append(identation + propertyInfo.Name + " = " + + PrintToString(propertyInfo.GetValue(obj), nestedObjects)); } - private bool ExcludedFromSerialization(PropertyInfo propertyInfo) - { - return excludedTypes.Contains(propertyInfo.PropertyType) || excludedProperties.Contains(propertyInfo); - } + return sb.ToString(); + } + + private bool ExcludedFromSerialization(PropertyInfo propertyInfo) + { + return excludedTypes.Contains(propertyInfo.PropertyType) || excludedProperties.Contains(propertyInfo); } } \ No newline at end of file diff --git a/ObjectPrinting/PropertyPrintingConfig.cs b/ObjectPrinting/PropertyPrintingConfig.cs index 47df3d6d..b2679a13 100644 --- a/ObjectPrinting/PropertyPrintingConfig.cs +++ b/ObjectPrinting/PropertyPrintingConfig.cs @@ -1,32 +1,30 @@ using System; -using System.Globalization; using System.Reflection; -namespace ObjectPrinting -{ - public class PropertyPrintingConfig : IPropertyPrintingConfig - { - public PrintingConfig PrintingConfig { get; } - public PropertyInfo PropertyInfo { get; } +namespace ObjectPrinting; - public PropertyPrintingConfig(PrintingConfig printingConfig, PropertyInfo propertyInfo) - { - this.PrintingConfig = printingConfig; - this.PropertyInfo = propertyInfo; - } - - public PrintingConfig Using(Func print) - { - var castedFunc = new Func((obj) => print((TPropType)obj)); - PrintingConfig.propertySerializers.Add(PropertyInfo, castedFunc); - return PrintingConfig; - } +public class PropertyPrintingConfig : IPropertyPrintingConfig +{ + public PrintingConfig PrintingConfig { get; } + public PropertyInfo PropertyInfo { get; } - PrintingConfig IPropertyPrintingConfig.ParentConfig => PrintingConfig; + public PropertyPrintingConfig(PrintingConfig printingConfig, PropertyInfo propertyInfo) + { + this.PrintingConfig = printingConfig; + this.PropertyInfo = propertyInfo; } - public interface IPropertyPrintingConfig + public PrintingConfig Using(Func print) { - PrintingConfig ParentConfig { get; } + var castedFunc = new Func((obj) => print((TPropType)obj)); + PrintingConfig.propertySerializers.Add(PropertyInfo, castedFunc); + return PrintingConfig; } + + PrintingConfig IPropertyPrintingConfig.ParentConfig => PrintingConfig; +} + +public interface IPropertyPrintingConfig +{ + PrintingConfig ParentConfig { get; } } \ No newline at end of file diff --git a/ObjectPrinting/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/PropertyPrintingConfigExtensions.cs index 97491d6b..0c24fa75 100644 --- a/ObjectPrinting/PropertyPrintingConfigExtensions.cs +++ b/ObjectPrinting/PropertyPrintingConfigExtensions.cs @@ -1,24 +1,20 @@ using System; -using System.Reflection; -using System.Text.RegularExpressions; +namespace ObjectPrinting; -namespace ObjectPrinting +public static class PropertyPrintingConfigExtensions { - public static class PropertyPrintingConfigExtensions + public static string PrintToString(this T obj, Func, PrintingConfig> config) { - public static string PrintToString(this T obj, Func, PrintingConfig> config) - { - return config(ObjectPrinter.For()).PrintToString(obj); - } - - public static PrintingConfig TrimmedToLength(this PropertyPrintingConfig propConfig, int maxLen) - { - var culturedFunc = new Func(s => s[..Math.Min(maxLen, s.Length)]); - var castedFunc = new Func(obj => culturedFunc((string)obj)); - var parent = propConfig.PrintingConfig; - parent.propertySerializers.Add(propConfig.PropertyInfo, castedFunc); - return parent; - } + return config(ObjectPrinter.For()).PrintToString(obj); + } + public static PrintingConfig TrimmedToLength(this PropertyPrintingConfig propConfig, int maxLen) + { + var culturedFunc = new Func(s => s[..Math.Min(maxLen, s.Length)]); + var castedFunc = new Func(obj => culturedFunc((string)obj)); + var parent = propConfig.PrintingConfig; + parent.propertySerializers.Add(propConfig.PropertyInfo, castedFunc); + return parent; } -} \ No newline at end of file + +} diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/TypePrintingConfig.cs index afa5ffea..2a84fbbd 100644 --- a/ObjectPrinting/TypePrintingConfig.cs +++ b/ObjectPrinting/TypePrintingConfig.cs @@ -1,32 +1,31 @@ using System.Globalization; using System; -namespace ObjectPrinting +namespace ObjectPrinting; + +public class TypePrintingConfig : IPropertyPrintingConfig { - public class TypePrintingConfig : IPropertyPrintingConfig - { - private readonly PrintingConfig printingConfig; + private readonly PrintingConfig printingConfig; - PrintingConfig IPropertyPrintingConfig.ParentConfig => printingConfig; + PrintingConfig IPropertyPrintingConfig.ParentConfig => printingConfig; - public TypePrintingConfig(PrintingConfig printingConfig) - { - this.printingConfig = printingConfig; - } + public TypePrintingConfig(PrintingConfig printingConfig) + { + this.printingConfig = printingConfig; + } - public PrintingConfig Using(Func print) - { - var castedFunc = new Func((obj) => print((TPropType)obj)); - printingConfig.typeSerializers.Add(typeof(TPropType), castedFunc); - return printingConfig; - } + public PrintingConfig Using(Func print) + { + var castedFunc = new Func((obj) => print((TPropType)obj)); + printingConfig.typeSerializers.Add(typeof(TPropType), castedFunc); + return printingConfig; + } - public PrintingConfig Using(CultureInfo culture) where TFormattable : IFormattable - { - var culturedFunc = new Func(d => d.ToString(null, culture)); - var castedFunc = new Func(obj => culturedFunc((TFormattable)obj)); - printingConfig.typeSerializers.Add(typeof(TPropType), castedFunc); - return printingConfig; - } + public PrintingConfig Using(CultureInfo culture) where TFormattable : IFormattable + { + var culturedFunc = new Func(d => d.ToString(null, culture)); + var castedFunc = new Func(obj => culturedFunc((TFormattable)obj)); + printingConfig.typeSerializers.Add(typeof(TPropType), castedFunc); + return printingConfig; } } \ No newline at end of file diff --git a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs index dd65c952..ca65bf68 100644 --- a/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting_Should/ObjectPrinterAcceptanceTests.cs @@ -2,168 +2,168 @@ using FluentAssertions; using ObjectPrinting; -namespace ObjectPrinting_Should +namespace ObjectPrinting_Should; + +[TestFixture] +public class ObjectPrinterAcceptanceTests { - [TestFixture] - public class ObjectPrinterAcceptanceTests + private Person person = null!; + + [SetUp] + public void Setup() { - private Person person = null!; - - [SetUp] - public void Setup() - { - person = new Person { Name = "Alex", Age = 19, Height = 179.5, Id = new Guid() }; - } - - [Test] - public void PrintToString_SkipsExcludedTypes() - { - var printer = ObjectPrinter.For().Excluding(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tSibling = null", - "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); - var outputString = printer.PrintToString(person); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_SkipsExcludedProperty() - { - var printer = ObjectPrinter.For().Excluding(p => p.Id); - - var expectedString = string.Join(Environment.NewLine, "Person", "\tSibling = null", - "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); - var outputString = printer.PrintToString(person); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_UsesCustomSerializator_WhenGivenToType() - { - var printer = ObjectPrinter.For().Printing().Using(i => i.ToString("X")); - - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", - "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); - var outputString = printer.PrintToString(person); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_UsesCustomSerialization_WhenGivenToProperty() - { - var printer = ObjectPrinter.For().Printing(p => p.Age).Using(i => i.ToString("X")); - - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", - "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); - var outputString = printer.PrintToString(person); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_TrimsStringProperties_WhenTrimmingIsSet() - { - var printer = ObjectPrinter.For().Printing(p => p.Name).TrimmedToLength(1); - - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", - "\tSibling = null", "\tName = A", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); - var outputString = printer.PrintToString(person); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() - { - var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); - - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", - "\tSibling = null", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", "\tFavouriteNumbers = null", ""); - var outputString = printer.PrintToString(person); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_SerializesClass_WhenCalledFromItInstance() - { - var outputString = person.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", - "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_SerializesClass_WhenCalledFromItInstanceWithConfig() - { - var outputString = person.PrintToString(s => s.Excluding(p => p.Age)); - var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", - "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tFavouriteNumbers = null", ""); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_SerializesCyclicReferences() - { - var firstPerson = new Person() { Age = 20, Name = "Ben" }; - var secondPerson = new Person() { Age = 20, Name = "John", Sibling = firstPerson }; - firstPerson.Sibling = secondPerson; - //var printer = ObjectPrinter.For().Excluding().Excluding(); - - var outputString = firstPerson.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", - "\tId = 00000000-0000-0000-0000-000000000000", - "\tSibling = Person", "\t\tId = 00000000-0000-0000-0000-000000000000", - "\t\tName = John", "\t\tHeight = 0", "\t\tAge = 20", "\t\tFavouriteNumbers = null", - "\tName = Ben", "\tHeight = 0", "\tAge = 20", "\tFavouriteNumbers = null", ""); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_SerializesArray_WhenInClass() - { - person.FavouriteNumbers = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; - var outputString = person.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "Person", - "\tId = 00000000-0000-0000-0000-000000000000", - "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", - "\tFavouriteNumbers = [ 1,1 2,2 3,3 4,4 5,5 ]", ""); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_SerializesArray() - { - var numbers = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; - var outputString = numbers.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "[ 1,1 2,2 3,3 4,4 5,5 ]", ""); - outputString.Should().Be(expectedString); - } + person = new Person { Name = "Alex", Age = 19, Height = 179.5, Id = new Guid() }; + } + + [Test] + public void PrintToString_SkipsExcludedTypes() + { + var printer = ObjectPrinter.For().Excluding(); + var expectedString = string.Join(Environment.NewLine, "Person", "\tSibling = null", + "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SkipsExcludedProperty() + { + var printer = ObjectPrinter.For().Excluding(p => p.Id); + + var expectedString = string.Join(Environment.NewLine, "Person", "\tSibling = null", + "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_UsesCustomSerializator_WhenGivenToType() + { + var printer = ObjectPrinter.For().Printing().Using(i => i.ToString("X")); + + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_UsesCustomSerialization_WhenGivenToProperty() + { + var printer = ObjectPrinter.For().Printing(p => p.Age).Using(i => i.ToString("X")); + + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 13", "\tFavouriteNumbers = null", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_TrimsStringProperties_WhenTrimmingIsSet() + { + var printer = ObjectPrinter.For().Printing(p => p.Name).TrimmedToLength(1); + + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = null", "\tName = A", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_UsesCustomCulture_WhenGivenToNumericType() + { + var printer = ObjectPrinter.For().Printing().Using(CultureInfo.InvariantCulture); + + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = null", "\tName = Alex", "\tHeight = 179.5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + var outputString = printer.PrintToString(person); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesClass_WhenCalledFromItInstance() + { + var outputString = person.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", "\tFavouriteNumbers = null", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesClass_WhenCalledFromItInstanceWithConfig() + { + var outputString = person.PrintToString(s => s.Excluding(p => p.Age)); + var expectedString = string.Join(Environment.NewLine, "Person", "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tFavouriteNumbers = null", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesCyclicReferences() + { + var firstPerson = new Person() { Age = 20, Name = "Ben" }; + var secondPerson = new Person() { Age = 20, Name = "John", Sibling = firstPerson }; + firstPerson.Sibling = secondPerson; + //var printer = ObjectPrinter.For().Excluding().Excluding(); + + var outputString = firstPerson.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", + "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = Person", "\t\tId = 00000000-0000-0000-0000-000000000000", + "\t\tName = John", "\t\tHeight = 0", "\t\tAge = 20", "\t\tFavouriteNumbers = null", + "\tName = Ben", "\tHeight = 0", "\tAge = 20", "\tFavouriteNumbers = null", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesArray_WhenInClass() + { + person.FavouriteNumbers = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var outputString = person.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "Person", + "\tId = 00000000-0000-0000-0000-000000000000", + "\tSibling = null", "\tName = Alex", "\tHeight = 179,5", "\tAge = 19", + "\tFavouriteNumbers = [ 1,1 2,2 3,3 4,4 5,5 ]", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesArray() + { + var numbers = new[] { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var outputString = numbers.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "[ 1,1 2,2 3,3 4,4 5,5 ]", ""); + outputString.Should().Be(expectedString); + } - [Test] - public void PrintToString_SerializesEmptyArray() - { - var numbers = Array.Empty(); - var outputString = numbers.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "[]", ""); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_SerializesList() - { - var numbers = new List { 1.1, 2.2, 3.3, 4.4, 5.5 }; - var outputString = numbers.PrintToString(); - var expectedString = string.Join(Environment.NewLine, "[ 1,1 2,2 3,3 4,4 5,5 ]", ""); - outputString.Should().Be(expectedString); - } - - [Test] - public void PrintToString_SerializesDictionary() - { - var numbers = new Dictionary { { new Person(), 1 }, { new Person(), 2 } }; - var arrayOfDictionaries = new[] { numbers , new Dictionary()}; - var arrayOfDictionariesHashSet = new HashSet[]> {arrayOfDictionaries,}; - var printer = ObjectPrinter.For[]>>().Excluding(); - - var outputString = printer.PrintToString(arrayOfDictionariesHashSet); - var expectedString = @"[ + [Test] + public void PrintToString_SerializesEmptyArray() + { + var numbers = Array.Empty(); + var outputString = numbers.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "[]", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesList() + { + var numbers = new List { 1.1, 2.2, 3.3, 4.4, 5.5 }; + var outputString = numbers.PrintToString(); + var expectedString = string.Join(Environment.NewLine, "[ 1,1 2,2 3,3 4,4 5,5 ]", ""); + outputString.Should().Be(expectedString); + } + + [Test] + public void PrintToString_SerializesDictionary() + { + var numbers = new Dictionary { { new Person(), 1 }, { new Person(), 2 } }; + var arrayOfDictionaries = new[] { numbers , new Dictionary()}; + var arrayOfDictionariesHashSet = new HashSet[]> {arrayOfDictionaries,}; + var printer = ObjectPrinter.For[]>>().Excluding(); + + var outputString = printer.PrintToString(arrayOfDictionariesHashSet); + var expectedString = @"[ [ { [ @@ -192,7 +192,6 @@ public void PrintToString_SerializesDictionary() ] ] "; - outputString.Should().Be(expectedString); - } + outputString.Should().Be(expectedString); } } \ No newline at end of file diff --git a/ObjectPrinting_Should/Person.cs b/ObjectPrinting_Should/Person.cs index e1d09f3a..2d191f77 100644 --- a/ObjectPrinting_Should/Person.cs +++ b/ObjectPrinting_Should/Person.cs @@ -1,14 +1,11 @@ -using System; +namespace ObjectPrinting_Should; -namespace ObjectPrinting_Should +public class Person { - public class Person - { - public Guid Id { get; set; } - public Person Sibling { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - public double[] FavouriteNumbers { get; set; } - } + public Guid Id { get; set; } + public Person Sibling { get; set; } + public string Name { get; set; } + public double Height { get; set; } + public int Age { get; set; } + public double[] FavouriteNumbers { get; set; } } \ No newline at end of file From 7daac047d832a76eb51d97a1a3149357e8b0d04b Mon Sep 17 00:00:00 2001 From: Kholstinin Yegor <99079920+Yrwlcm@users.noreply.github.com> Date: Tue, 26 Dec 2023 13:08:47 +0500 Subject: [PATCH 13/13] String interpolation instead of concatenation --- ObjectPrinting/PrintingConfig.cs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 277a4a5e..6ccc796d 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -72,10 +72,8 @@ private string SerializeValueTypeIEnumerable(IEnumerable enumerable, ImmutableHa sb.Add("["); foreach (var element in enumerable) { - var serializedString = PrintToString(element, nestedObjects).Trim(); - sb.Add(serializedString); + sb.Add(PrintToString(element, nestedObjects).Trim()); } - sb.Add("]"); return string.Join(' ', sb); } @@ -84,14 +82,12 @@ private string SerializeReferenceTypeIEnumerable(IEnumerable enumerable, Immutab { var sb = new List(); var identation = new string('\t', nestedObjects.Count); - sb.Add(identation + "["); + sb.Add($"{identation}["); foreach (var element in enumerable) { - var serializedString = PrintToString(element, nestedObjects.Add(sb)).TrimEnd(); - sb.Add(serializedString); + sb.Add(PrintToString(element, nestedObjects.Add(sb)).TrimEnd()); } - - sb.Add(identation + "]"); + sb.Add($"{identation}]"); return string.Join(Environment.NewLine, sb); } @@ -99,18 +95,17 @@ private string SerializeIDictionary(IDictionary dictionary, ImmutableHashSet(); var identation = new string('\t', nestedObjects.Count); - sb.Add(identation + "{"); + sb.Add($"{identation}{{"); nestedObjects = nestedObjects.Add(dictionary); foreach (DictionaryEntry dictionaryEntry in dictionary) { - sb.Add(identation + '\t' + "["); - sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Key, nestedObjects).TrimEnd()); - sb.Add(identation + '\t' + ":"); - sb.Add(identation + '\t' + PrintToString(dictionaryEntry.Value, nestedObjects).TrimEnd()); - sb.Add(identation + '\t' + "]"); + sb.Add($"{identation}\t["); + sb.Add($"{identation}\t{PrintToString(dictionaryEntry.Key, nestedObjects).TrimEnd()}"); + sb.Add($"{identation}\t:"); + sb.Add($"{identation}\t{PrintToString(dictionaryEntry.Value, nestedObjects).TrimEnd()}"); + sb.Add($"{identation}\t]"); } - - sb.Add(identation + "}"); + sb.Add($"{identation}}}"); return string.Join(Environment.NewLine, sb); }