From 6e0aac298bdf15ae618a4ea9452cd4cfb914a9ac Mon Sep 17 00:00:00 2001 From: artem <17artem12@gmail.com> Date: Fri, 21 Nov 2025 12:49:48 +0500 Subject: [PATCH 1/3] =?UTF-8?q?exlude=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/ObjectExtensions.cs | 13 +++ ObjectPrinting/ObjectPrinter.cs | 6 ++ ObjectPrinting/ObjectPrinting.csproj | 4 +- ObjectPrinting/PrintingConfig.cs | 22 +++++- .../Tests/ObjectPrinterAcceptanceTests.cs | 79 ++++++++++++++++--- .../FluentMapping.Tests.csproj | 1 + Samples/FluentMapper/FluentMapping.csproj | 4 + Samples/Spectacle/Spectacle.csproj | 4 + fluent-api.sln.DotSettings | 3 + 9 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 ObjectPrinting/ObjectExtensions.cs diff --git a/ObjectPrinting/ObjectExtensions.cs b/ObjectPrinting/ObjectExtensions.cs new file mode 100644 index 000000000..429062fc3 --- /dev/null +++ b/ObjectPrinting/ObjectExtensions.cs @@ -0,0 +1,13 @@ +using System; + +namespace ObjectPrinting; + +public static class ObjectExtensions +{ + public static string PrintToString(this T obj, Func, PrintingConfig> config) + { + var printingConfig = ObjectPrinter.For(); + printingConfig = config(printingConfig); + return printingConfig.PrintToString(obj); + } +} \ No newline at end of file diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index 3c7867c32..5123ed02b 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -1,10 +1,16 @@ +using System; +using System.Collections.Generic; + namespace ObjectPrinting { public class ObjectPrinter { + public static PrintingConfig For() { return new PrintingConfig(); } + + } } \ No newline at end of file diff --git a/ObjectPrinting/ObjectPrinting.csproj b/ObjectPrinting/ObjectPrinting.csproj index c5db392ff..de324a29c 100644 --- a/ObjectPrinting/ObjectPrinting.csproj +++ b/ObjectPrinting/ObjectPrinting.csproj @@ -5,8 +5,10 @@ + - + + \ No newline at end of file diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a9e082117..706fd9b4b 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; @@ -6,6 +7,15 @@ namespace ObjectPrinting { public class PrintingConfig { + private readonly HashSet excludedTypes = new HashSet(); + private readonly HashSet excludedProperties = new HashSet(); + + public PrintingConfig Exclude() + { + excludedTypes.Add(typeof(T)); + return this; + } + public string PrintToString(TOwner obj) { return PrintToString(obj, 0); @@ -13,15 +23,15 @@ public string PrintToString(TOwner obj) 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) + typeof(DateTime), typeof(TimeSpan), typeof(Guid) }; + if (finalTypes.Contains(obj.GetType())) return obj + Environment.NewLine; @@ -29,8 +39,16 @@ private string PrintToString(object obj, int nestingLevel) var sb = new StringBuilder(); var type = obj.GetType(); sb.AppendLine(type.Name); + foreach (var propertyInfo in type.GetProperties()) { + // Проверяем исключения + if (excludedTypes.Contains(propertyInfo.PropertyType)) + continue; + + if (excludedProperties.Contains(propertyInfo.Name)) + continue; + sb.Append(identation + propertyInfo.Name + " = " + PrintToString(propertyInfo.GetValue(obj), nestingLevel + 1)); diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs index 4c8b2445c..7438851ec 100644 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs @@ -1,27 +1,86 @@ -using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Globalization; +using NUnit.Framework; +using ObjectPrinting.Solved; +using FluentAssertions; namespace ObjectPrinting.Tests { [TestFixture] public class ObjectPrinterAcceptanceTests { + + private Person testPerson; + + [SetUp] + public void Setup() + { + testPerson = new Person + { + Name = "Alexander", + Age = 25, + Height = 180.5, + Id = Guid.NewGuid() + }; + } + [Test] public void Demo() { - var person = new Person { Name = "Alex", Age = 19 }; + var person = new Person { Name = "Alex", Age = 19, Height = 160.1 }; - var printer = ObjectPrinter.For(); + var printer = ObjectPrinter.For() //1. Исключить из сериализации свойства определенного типа - //2. Указать альтернативный способ сериализации для определенного типа - //3. Для числовых типов указать культуру - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //6. Исключить из сериализации конкретного свойства - + .Exclude(); + //2. Указать альтернативный способ сериализации для определенного типа + //.ConfigureType().Using(i => i + " лет") + //3. Для числовых типов указать культуру + //.ConfigureType().Using(new CultureInfo("ru-RU")) + //4. Настроить сериализацию конкретного свойства + //.ConfigureProperty(p => p.Height).Using(i => i + " см") + //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) + //.ConfigureProperty(p => p.Name).TrimTo(8) + //6. Исключить из сериализации конкретного свойства + //.Exclude(p => p.Name); + + string s1 = printer.PrintToString(person); - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию + //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию + string s2 = person.PrintToString(); + //8. ...с конфигурированием + //string s3 = person.PrintToString(s => s.Exclude(p => p.Age)); + Console.WriteLine(s1); + Console.WriteLine(s2); + //Console.WriteLine(s3); + } + + [Test] + public void PrintToString_DefaultConfiguration_ReturnsAllProperties() + { + var result = testPerson.PrintToString(); + + result.Should().Contain("Name = Alexander") + .And.Contain("Age = 25") + .And.Contain("Height = 180.5") + .And.Contain("Id = "); + } + + [Test] + public void Exclude_ByType_ExcludesPropertiesOfSpecifiedType() + { + var printer = ObjectPrinter.For().Exclude(); + var result = printer.PrintToString(testPerson); + + result.Should().NotContain("Id") + .And.Contain("Name = Alexander") + .And.Contain("Age = 25") + .And.Contain("Height = 180.5"); + } + + } } \ No newline at end of file diff --git a/Samples/FluentMapper.Tests/FluentMapping.Tests.csproj b/Samples/FluentMapper.Tests/FluentMapping.Tests.csproj index 3828c5665..295951f1d 100644 --- a/Samples/FluentMapper.Tests/FluentMapping.Tests.csproj +++ b/Samples/FluentMapper.Tests/FluentMapping.Tests.csproj @@ -6,6 +6,7 @@ + diff --git a/Samples/FluentMapper/FluentMapping.csproj b/Samples/FluentMapper/FluentMapping.csproj index fa71b7ae6..995c6adc3 100644 --- a/Samples/FluentMapper/FluentMapping.csproj +++ b/Samples/FluentMapper/FluentMapping.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/Samples/Spectacle/Spectacle.csproj b/Samples/Spectacle/Spectacle.csproj index fa71b7ae6..995c6adc3 100644 --- a/Samples/Spectacle/Spectacle.csproj +++ b/Samples/Spectacle/Spectacle.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/fluent-api.sln.DotSettings b/fluent-api.sln.DotSettings index 135b83ecb..53fe49b2f 100644 --- a/fluent-api.sln.DotSettings +++ b/fluent-api.sln.DotSettings @@ -1,6 +1,9 @@  <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> + True True True Imported 10.10.2016 From 8654199ec8a7318b40683261a3b19bdd0d546a44 Mon Sep 17 00:00:00 2001 From: artem <17artem12@gmail.com> Date: Fri, 21 Nov 2025 12:59:53 +0500 Subject: [PATCH 2/3] =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B8=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=82=D0=B8=D0=BF=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/PrintingConfig.cs | 51 +++++++--- .../Tests/ObjectPrinterAcceptanceTests.cs | 95 ++++++++++++++++++- ObjectPrinting/TypePrintingConfig.cs | 22 +++++ 3 files changed, 149 insertions(+), 19 deletions(-) create mode 100644 ObjectPrinting/TypePrintingConfig.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 706fd9b4b..edb36c1a7 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,20 +1,39 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Text; namespace ObjectPrinting { public class PrintingConfig { - private readonly HashSet excludedTypes = new HashSet(); - private readonly HashSet excludedProperties = new HashSet(); + private HashSet excludingTypes = new(); + private HashSet excludingProperties = new(); + private readonly Dictionary> typeSerializers = new(); public PrintingConfig Exclude() { - excludedTypes.Add(typeof(T)); + excludingTypes.Add(typeof(T)); return this; } + + public PrintingConfig Exclude(Expression> propertySelector) + { + var propertyName = ((MemberExpression)propertySelector.Body).Member.Name; + excludingProperties.Add(propertyName); + return this; + } + + public TypePrintingConfig ConfigureType() + { + return new TypePrintingConfig(this); + } + + internal void AddTypeSerializer(Func serializer) + { + typeSerializers[typeof(TPropType)] = obj => serializer((TPropType)obj); + } public string PrintToString(TOwner obj) { @@ -26,32 +45,34 @@ private string PrintToString(object obj, int nestingLevel) if (obj == null) return "null" + Environment.NewLine; + var objectType = obj.GetType(); + + if (typeSerializers.TryGetValue(objectType, out var serializer)) + return serializer(obj) + Environment.NewLine; + var finalTypes = new[] { typeof(int), typeof(double), typeof(float), typeof(string), typeof(DateTime), typeof(TimeSpan), typeof(Guid) }; - if (finalTypes.Contains(obj.GetType())) + if (finalTypes.Contains(objectType)) return obj + Environment.NewLine; var identation = new string('\t', nestingLevel + 1); var sb = new StringBuilder(); - var type = obj.GetType(); - sb.AppendLine(type.Name); + sb.AppendLine(objectType.Name); - foreach (var propertyInfo in type.GetProperties()) + foreach (var propertyInfo in objectType.GetProperties()) { - // Проверяем исключения - if (excludedTypes.Contains(propertyInfo.PropertyType)) - continue; - - if (excludedProperties.Contains(propertyInfo.Name)) + var propertyName = propertyInfo.Name; + var propertyType = propertyInfo.PropertyType; + + if (excludingProperties.Contains(propertyName) || excludingTypes.Contains(propertyType)) continue; - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); + var value = propertyInfo.GetValue(obj); + sb.Append(identation + propertyName + " = " + PrintToString(value, nestingLevel + 1)); } return sb.ToString(); } diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs index 7438851ec..cde258cee 100644 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs @@ -32,9 +32,9 @@ public void Demo() var printer = ObjectPrinter.For() //1. Исключить из сериализации свойства определенного типа - .Exclude(); - //2. Указать альтернативный способ сериализации для определенного типа - //.ConfigureType().Using(i => i + " лет") + .Exclude() + //2. Указать альтернативный способ сериализации для определенного типа + .ConfigureType().Using(i => i + " лет"); //3. Для числовых типов указать культуру //.ConfigureType().Using(new CultureInfo("ru-RU")) //4. Настроить сериализацию конкретного свойства @@ -51,7 +51,7 @@ public void Demo() string s2 = person.PrintToString(); //8. ...с конфигурированием - //string s3 = person.PrintToString(s => s.Exclude(p => p.Age)); + string s3 = person.PrintToString(s => s.Exclude(p => p.Age)); Console.WriteLine(s1); Console.WriteLine(s2); //Console.WriteLine(s3); @@ -80,6 +80,93 @@ public void Exclude_ByType_ExcludesPropertiesOfSpecifiedType() .And.Contain("Age = 25") .And.Contain("Height = 180.5"); } + + [Test] + public void Exclude_ByProperty_ExcludesSpecificProperty() + { + var printer = new PrintingConfig().Exclude(p => p.Name); + var result = printer.PrintToString(testPerson); + + result.Should().NotContain("Name = Alexander") + .And.Contain("Age = 25") + .And.Contain("Height = 180.5") + .And.Contain("Id = "); + } + + [Test] + public void Exclude_ByTypeAndProperty_CombinesCorrectly() + { + var printer = new PrintingConfig() + .Exclude() + .Exclude(p => p.Name); + + var result = printer.PrintToString(testPerson); + + result.Should().NotContain("Id") + .And.NotContain("Name = Alexander") + .And.Contain("Age = 25") + .And.Contain("Height = 180.5"); + } + + [Test] + public void Exclude_AllPropertiesByCombination_ReturnsOnlyTypeName() + { + var printer = new PrintingConfig() + .Exclude(p => p.Id) + .Exclude(p => p.Name) + .Exclude(p => p.Height) + .Exclude(p => p.Age); + + var result = printer.PrintToString(testPerson); + + result.Trim().Should().Be("Person"); + } + + [Test] + public void ConfigureType_UsingCustomSerialization_AppliesCustomFormat() + { + var printer = new PrintingConfig() + .ConfigureType().Using(i => i + " years"); + + var result = printer.PrintToString(testPerson); + + result.Should().Contain("Age = 25 years"); + } + + [Test] + public void ConfigureType_ForStringType_WorksCorrectly() + { + var printer = new PrintingConfig() + .ConfigureType().Using(s => s?.ToUpper() ?? "NULL"); + + var result = printer.PrintToString(testPerson); + + result.Should().Contain("Name = ALEXANDER"); + } + + [Test] + public void ConfigureType_ForDoubleType_WorksCorrectly() + { + var printer = new PrintingConfig() + .ConfigureType().Using(d => d + " cm"); + + var result = printer.PrintToString(testPerson); + + result.Should().Contain("Height = 180.5 cm"); + } + + [Test] + public void ConfigureType_WithExclude_WorksTogether() + { + var printer = new PrintingConfig() + .Exclude() + .ConfigureType().Using(i => i + " лет"); + + var result = printer.PrintToString(testPerson); + + result.Should().NotContain("Id") + .And.Contain("Age = 25 лет"); + } } diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/TypePrintingConfig.cs new file mode 100644 index 000000000..6e0ef99d9 --- /dev/null +++ b/ObjectPrinting/TypePrintingConfig.cs @@ -0,0 +1,22 @@ +using System; +using System.Globalization; + +namespace ObjectPrinting; + +public class TypePrintingConfig +{ + private readonly PrintingConfig parentConfig; + + public TypePrintingConfig(PrintingConfig parentConfig) + { + this.parentConfig = parentConfig; + } + + public PrintingConfig Using(Func serializer) + { + parentConfig.AddTypeSerializer(serializer); + return parentConfig; + } + + +} \ No newline at end of file From 40c838bf8439734cf784eabcd9c0f471bbc032db Mon Sep 17 00:00:00 2001 From: artem <17artem12@gmail.com> Date: Fri, 21 Nov 2025 13:02:39 +0500 Subject: [PATCH 3/3] =?UTF-8?q?=D0=B8=D1=82=D0=BE=D0=B3\?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/PrintingConfig.cs | 64 ++++- ObjectPrinting/PropertyPrintingConfig.cs | 22 ++ .../PropertyPrintingConfigExtensions.cs | 29 +++ .../Tests/ObjectPrinterAcceptanceTests.cs | 235 ++++++++++++++---- ObjectPrinting/TypePrintingConfig.cs | 45 +++- 5 files changed, 333 insertions(+), 62 deletions(-) create mode 100644 ObjectPrinting/PropertyPrintingConfig.cs create mode 100644 ObjectPrinting/PropertyPrintingConfigExtensions.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index edb36c1a7..522d3f00c 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Text; +using ObjectPrinting; namespace ObjectPrinting { @@ -11,6 +13,14 @@ public class PrintingConfig private HashSet excludingTypes = new(); private HashSet excludingProperties = new(); private readonly Dictionary> typeSerializers = new(); + private readonly Dictionary> propertiesSerializers = new(); + + + public PropertyPrintingConfig ConfigureProperty(Expression> propertySelector) + { + var propertyName = ((MemberExpression)propertySelector.Body).Member.Name; + return new PropertyPrintingConfig(this, propertyName); + } public PrintingConfig Exclude() { @@ -30,6 +40,18 @@ public TypePrintingConfig ConfigureType() return new TypePrintingConfig(this); } + internal void AddPropertySerializer(string propertyName, Func serializer) + { + propertiesSerializers[propertyName] = obj => + { + var value = (TProp)obj; + + + + return serializer(value); + }; + } + internal void AddTypeSerializer(Func serializer) { typeSerializers[typeof(TPropType)] = obj => serializer((TPropType)obj); @@ -43,26 +65,32 @@ public string PrintToString(TOwner obj) private string PrintToString(object obj, int nestingLevel) { if (obj == null) + { return "null" + Environment.NewLine; + } var objectType = obj.GetType(); if (typeSerializers.TryGetValue(objectType, out var serializer)) + { return serializer(obj) + Environment.NewLine; + } + var finalTypes = new[] { typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan), typeof(Guid) + typeof(DateTime), typeof(TimeSpan) }; - if (finalTypes.Contains(objectType)) + { return obj + Environment.NewLine; + } + var identation = new string('\t', nestingLevel + 1); var sb = new StringBuilder(); sb.AppendLine(objectType.Name); - foreach (var propertyInfo in objectType.GetProperties()) { var propertyName = propertyInfo.Name; @@ -71,10 +99,36 @@ private string PrintToString(object obj, int nestingLevel) if (excludingProperties.Contains(propertyName) || excludingTypes.Contains(propertyType)) continue; - var value = propertyInfo.GetValue(obj); - sb.Append(identation + propertyName + " = " + PrintToString(value, nestingLevel + 1)); + var propertyValue = propertyInfo.GetValue(obj); + string serializedValue = GetSerializedValue(propertyValue, propertyName, nestingLevel); + + sb.Append(identation + propertyName + " = " + serializedValue); } + return sb.ToString(); } + + + private string GetSerializedValue(object value, string propertyName, int nestingLevel) + { + + if (propertiesSerializers.TryGetValue(propertyName, out var propertySerializer)) + { + return propertySerializer(value) + Environment.NewLine; + } + + if (value != null && typeSerializers.TryGetValue(value.GetType(), out var typeSerializer)) + { + return typeSerializer(value) + Environment.NewLine; + } + + + return PrintToString(value, nestingLevel + 1); + } } + + + + + } \ No newline at end of file diff --git a/ObjectPrinting/PropertyPrintingConfig.cs b/ObjectPrinting/PropertyPrintingConfig.cs new file mode 100644 index 000000000..570d3a02f --- /dev/null +++ b/ObjectPrinting/PropertyPrintingConfig.cs @@ -0,0 +1,22 @@ +using System; + +namespace ObjectPrinting; + +public class PropertyPrintingConfig +{ + private readonly PrintingConfig parentConfig; + private readonly string propertyName; + + public PropertyPrintingConfig(PrintingConfig parentConfig, string propertyName) + { + this.parentConfig = parentConfig; + this.propertyName = propertyName; + } + + public PrintingConfig Using(Func serializer) + { + parentConfig.AddPropertySerializer(propertyName, serializer); + return parentConfig; + } + +} \ No newline at end of file diff --git a/ObjectPrinting/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/PropertyPrintingConfigExtensions.cs new file mode 100644 index 000000000..6c41d122b --- /dev/null +++ b/ObjectPrinting/PropertyPrintingConfigExtensions.cs @@ -0,0 +1,29 @@ +using System; + +namespace ObjectPrinting; + +public static class PropertyPrintingConfigExtensions +{ + public static PrintingConfig TrimTo(this PropertyPrintingConfig config, int maxLength) + { + Func serializer = str => + { + if (str == null) { return "null";} + + string result; + if (str.Length > maxLength) + { + result = str.Substring(0, maxLength); + } + else + { + result = str; + } + + return result; + + }; + + return config.Using(serializer); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs index cde258cee..3a70406a0 100644 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs @@ -34,15 +34,15 @@ public void Demo() //1. Исключить из сериализации свойства определенного типа .Exclude() //2. Указать альтернативный способ сериализации для определенного типа - .ConfigureType().Using(i => i + " лет"); - //3. Для числовых типов указать культуру - //.ConfigureType().Using(new CultureInfo("ru-RU")) - //4. Настроить сериализацию конкретного свойства - //.ConfigureProperty(p => p.Height).Using(i => i + " см") - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //.ConfigureProperty(p => p.Name).TrimTo(8) - //6. Исключить из сериализации конкретного свойства - //.Exclude(p => p.Name); + .ConfigureType().Using(i => i + " лет") + //3. Для числовых типов указать культуру + .ConfigureType().Using(new CultureInfo("ru-RU")) + //4. Настроить сериализацию конкретного свойства + .ConfigureProperty(p => p.Height).Using(i => i + " см") + //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) + .ConfigureProperty(p => p.Name).TrimTo(8) + //6. Исключить из сериализации конкретного свойства + .Exclude(p => p.Name); string s1 = printer.PrintToString(person); @@ -54,7 +54,7 @@ public void Demo() string s3 = person.PrintToString(s => s.Exclude(p => p.Age)); Console.WriteLine(s1); Console.WriteLine(s2); - //Console.WriteLine(s3); + Console.WriteLine(s3); } @@ -80,63 +80,115 @@ public void Exclude_ByType_ExcludesPropertiesOfSpecifiedType() .And.Contain("Age = 25") .And.Contain("Height = 180.5"); } - + [Test] public void Exclude_ByProperty_ExcludesSpecificProperty() { - var printer = new PrintingConfig().Exclude(p => p.Name); + var printer = ObjectPrinter.For().Exclude(p => p.Name); var result = printer.PrintToString(testPerson); result.Should().NotContain("Name = Alexander") .And.Contain("Age = 25") - .And.Contain("Height = 180.5") - .And.Contain("Id = "); + .And.Contain("Height = 180.5"); } [Test] - public void Exclude_ByTypeAndProperty_CombinesCorrectly() + public void ConfigureType_UsingCustomSerialization_AppliesCustomFormat() { - var printer = new PrintingConfig() - .Exclude() - .Exclude(p => p.Name); + var printer = ObjectPrinter.For() + .ConfigureType().Using(i => i + " years"); var result = printer.PrintToString(testPerson); - result.Should().NotContain("Id") - .And.NotContain("Name = Alexander") - .And.Contain("Age = 25") + result.Should().Contain("Age = 25 years"); + } + + + [Test] + public void ConfigureProperty_UsingCustomSerialization_AppliesToSpecificProperty() + { + var printer = ObjectPrinter.For() + .ConfigureProperty(p => p.Height).Using(h => h + " cm"); + + var result = printer.PrintToString(testPerson); + + result.Should().Contain("Height = 180.5 cm"); + } + + [Test] + public void ConfigureProperty_TrimTo_TrimsStringProperty() + { + var printer = ObjectPrinter.For() + .ConfigureProperty(p => p.Name).TrimTo(4); + + var result = printer.PrintToString(testPerson); + + result.Should().Contain("Name = Alex"); + } + + [Test] + public void ConfigureProperty_TrimTo_WithLongString_TrimsCorrectly() + { + var person = new Person { Name = "VeryLongName", Age = 20, Height = 170 }; + var printer = ObjectPrinter.For() + .ConfigureProperty(p => p.Name).TrimTo(5); + + var result = printer.PrintToString(person); + + result.Should().Contain("Name = VeryL"); + } + + [Test] + public void PrintToString_WithConfiguration_AppliesConfiguration() + { + var result = testPerson.PrintToString(s => s.Exclude(p => p.Age)); + + result.Should().NotContain("Age = 25") + .And.Contain("Name = Alexander") .And.Contain("Height = 180.5"); } [Test] - public void Exclude_AllPropertiesByCombination_ReturnsOnlyTypeName() + public void MultipleConfigurations_WorkTogetherCorrectly() { - var printer = new PrintingConfig() - .Exclude(p => p.Id) - .Exclude(p => p.Name) - .Exclude(p => p.Height) + var printer = ObjectPrinter.For() + .Exclude() + .ConfigureType().Using(i => i + " лет") + .ConfigureProperty(p => p.Height).Using(h => h + " см") + .ConfigureProperty(p => p.Name).TrimTo(3) .Exclude(p => p.Age); var result = printer.PrintToString(testPerson); - result.Trim().Should().Be("Person"); + result.Should().NotContain("Id") + .And.NotContain("Age") + .And.Contain("Name = Ale") + .And.Contain("Height = 180.5 см"); } - + [Test] - public void ConfigureType_UsingCustomSerialization_AppliesCustomFormat() + public void EmptyObject_ReturnsCorrectFormat() { - var printer = new PrintingConfig() - .ConfigureType().Using(i => i + " years"); + var emptyPerson = new Person(); + var result = emptyPerson.PrintToString(); - var result = printer.PrintToString(testPerson); + result.Should().Contain("Name = null") + .And.Contain("Age = 0") + .And.Contain("Height = 0"); + } - result.Should().Contain("Age = 25 years"); + [Test] + public void NullObject_ReturnsNullString() + { + Person nullPerson = null; + var result = nullPerson.PrintToString(); + result.Should().Be("null" + Environment.NewLine); } [Test] public void ConfigureType_ForStringType_WorksCorrectly() { - var printer = new PrintingConfig() + var printer = ObjectPrinter.For() .ConfigureType().Using(s => s?.ToUpper() ?? "NULL"); var result = printer.PrintToString(testPerson); @@ -145,29 +197,124 @@ public void ConfigureType_ForStringType_WorksCorrectly() } [Test] - public void ConfigureType_ForDoubleType_WorksCorrectly() + public void Demo_WithAllFeatures_ProducesExpectedOutput() { - var printer = new PrintingConfig() - .ConfigureType().Using(d => d + " cm"); + var person = new Person { Name = "Alex", Age = 19, Height = 160.1 }; - var result = printer.PrintToString(testPerson); + var printer = ObjectPrinter.For() + .Exclude() + .ConfigureType().Using(i => i + " лет") + .ConfigureType().Using(new CultureInfo("ru-RU")) + .ConfigureProperty(p => p.Height).Using(i => i + " см") + .ConfigureProperty(p => p.Name).TrimTo(3) + .Exclude(p => p.Name); - result.Should().Contain("Height = 180.5 cm"); + var result = printer.PrintToString(person); + + result.Should().NotContain("Id") + .And.NotContain("Name = Ale") + .And.Contain("Age = 19 лет") + .And.Contain("Height = 160.1 см") + .And.NotContain("160,1"); } + + [Test] + public void ConfigureProperty_TrimTo_WithNullString_HandlesGracefully() + { + var person = new Person { Name = null, Age = 25 }; + var printer = ObjectPrinter.For() + .ConfigureProperty(p => p.Name).TrimTo(5); + + var result = printer.PrintToString(person); + result.Should().Contain("Name = null"); + } + [Test] - public void ConfigureType_WithExclude_WorksTogether() + public void Exclude_MultipleTypes_ExcludesAllSpecifiedTypes() { - var printer = new PrintingConfig() + var printer = ObjectPrinter.For() .Exclude() - .ConfigureType().Using(i => i + " лет"); + .Exclude() + .Exclude(); var result = printer.PrintToString(testPerson); result.Should().NotContain("Id") - .And.Contain("Age = 25 лет"); + .And.NotContain("Name") + .And.NotContain("Age") + .And.Contain("Height = 180.5"); } + + [Test] + public void ConfigureType_And_ConfigureProperty_ForSameProperty_PropertyWins() + { + var printer = ObjectPrinter.For() + .ConfigureType().Using(d => d + " TYPE") + .ConfigureProperty(p => p.Height).Using(h => h + " PROPERTY"); + var result = printer.PrintToString(testPerson); + + result.Should().Contain("Height = 180.5 PROPERTY") + .And.NotContain("TYPE"); + } + + [Test] + public void Exclude_AllProperties_ReturnsOnlyTypeName() + { + var printer = ObjectPrinter.For() + .Exclude(p => p.Id) + .Exclude(p => p.Name) + .Exclude(p => p.Height) + .Exclude(p => p.Age); + + var result = printer.PrintToString(testPerson); + + result.Trim().Should().Be("Person"); + } + + [Test] + public void ConfigureType_WithCultureInfo_AppliesCultureFormatting() + { + // Arrange + var person = new Person { Name = "Test", Age = 25, Height = 160.5 }; + var culture = new CultureInfo("ru-RU"); + + var printer = ObjectPrinter.For() + .ConfigureType().Using(culture); + + // Act + var result = printer.PrintToString(person); + + // Assert + result.Should().Contain("Height = 160,5"); + } + + [Test] + public void ConfigureType_WithDifferentCultures_FormatsNumbersCorrectly() + { + // Arrange + var person = new Person { Name = "Test", Age = 25, Height = 160.5 }; + var usCulture = new CultureInfo("en-US"); + var frCulture = new CultureInfo("fr-FR"); + + var printerUS = ObjectPrinter.For() + .ConfigureType().Using(usCulture); + + var printerFR = ObjectPrinter.For() + .ConfigureType().Using(frCulture); + + // Act + var resultUS = printerUS.PrintToString(person); + var resultFR = printerFR.PrintToString(person); + + // Assert + resultUS.Should().Contain("Height = 160.5"); + resultFR.Should().Contain("Height = 160,5"); + } + } -} \ No newline at end of file + + +} diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/TypePrintingConfig.cs index 6e0ef99d9..50eb2467c 100644 --- a/ObjectPrinting/TypePrintingConfig.cs +++ b/ObjectPrinting/TypePrintingConfig.cs @@ -1,22 +1,41 @@ using System; using System.Globalization; -namespace ObjectPrinting; - -public class TypePrintingConfig +namespace ObjectPrinting { - private readonly PrintingConfig parentConfig; - - public TypePrintingConfig(PrintingConfig parentConfig) + public class TypePrintingConfig { - this.parentConfig = parentConfig; - } + private readonly PrintingConfig printingConfig; - public PrintingConfig Using(Func serializer) - { - parentConfig.AddTypeSerializer(serializer); - return parentConfig; - } + public TypePrintingConfig(PrintingConfig printingConfig) + { + this.printingConfig = printingConfig; + } + + public PrintingConfig Using(Func serializer) + { + printingConfig.AddTypeSerializer(serializer); + return printingConfig; + } + public PrintingConfig Using(CultureInfo culture) + { + + if (typeof(TPropType) == typeof(double)) + { + printingConfig.AddTypeSerializer(d => d.ToString(culture)); + } + else if (typeof(TPropType) == typeof(float)) + { + printingConfig.AddTypeSerializer(f => f.ToString(culture)); + } + else if (typeof(TPropType) == typeof(int)) + { + printingConfig.AddTypeSerializer(i => i.ToString(culture)); + } + + return printingConfig; + } + } } \ No newline at end of file