From 864c34ec7126fb40aa3c8ef946e4b31612ba80a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B0=D0=B2=D0=B5=D0=BB=D0=B8=D0=B9?= Date: Wed, 11 Dec 2024 03:38:38 +0500 Subject: [PATCH 1/5] Add implementation of ObjectPrinter --- ObjectPrinting/ObjectPrinting.csproj | 1 + ObjectPrinting/PropertyPrintingConfig.cs | 23 +++++ .../PropertyPrintingConfigExtensions.cs | 15 ++++ .../Tests/ObjectPrinterAcceptanceTests.cs | 27 ------ ObjectPrinting/Tests/ObjectPrinterTests.cs | 83 +++++++++++++++++++ ObjectPrinting/Tests/Person.cs | 2 + 6 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 ObjectPrinting/PropertyPrintingConfig.cs create mode 100644 ObjectPrinting/PropertyPrintingConfigExtensions.cs delete mode 100644 ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs create mode 100644 ObjectPrinting/Tests/ObjectPrinterTests.cs diff --git a/ObjectPrinting/ObjectPrinting.csproj b/ObjectPrinting/ObjectPrinting.csproj index c5db392f..e2f22aab 100644 --- a/ObjectPrinting/ObjectPrinting.csproj +++ b/ObjectPrinting/ObjectPrinting.csproj @@ -5,6 +5,7 @@ + diff --git a/ObjectPrinting/PropertyPrintingConfig.cs b/ObjectPrinting/PropertyPrintingConfig.cs new file mode 100644 index 00000000..c7676121 --- /dev/null +++ b/ObjectPrinting/PropertyPrintingConfig.cs @@ -0,0 +1,23 @@ +using System; +using System.Reflection; + +namespace ObjectPrinting; + +public class PropertyPrintingConfig +{ + public readonly PrintingConfig PrintingConfig; + public readonly PropertyInfo PropertyInfo; + + + public PropertyPrintingConfig(PrintingConfig printingConfig, PropertyInfo propertyInfo) + { + PrintingConfig = printingConfig; + PropertyInfo = propertyInfo; + } + + public PrintingConfig Using(Func newSerialize) + { + PrintingConfig.AddSerializedProperty(PropertyInfo, newSerialize); + return PrintingConfig; + } +} \ No newline at end of file diff --git a/ObjectPrinting/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/PropertyPrintingConfigExtensions.cs new file mode 100644 index 00000000..1e06b438 --- /dev/null +++ b/ObjectPrinting/PropertyPrintingConfigExtensions.cs @@ -0,0 +1,15 @@ +using System; + + +namespace ObjectPrinting; + +public static class PropertyPrintingConfigExtensions +{ + public static PrintingConfig TrimmedToLength( + this PropertyPrintingConfig propertyConfig, + int maxLen) + { + propertyConfig.PrintingConfig.AddLengthOfProperty(propertyConfig.PropertyInfo, maxLen); + return propertyConfig.PrintingConfig; + } +} \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs deleted file mode 100644 index 4c8b2445..00000000 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; - -namespace ObjectPrinting.Tests -{ - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19 }; - - var printer = ObjectPrinter.For(); - //1. Исключить из сериализации свойства определенного типа - //2. Указать альтернативный способ сериализации для определенного типа - //3. Для числовых типов указать культуру - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //6. Исключить из сериализации конкретного свойства - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - //8. ...с конфигурированием - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterTests.cs b/ObjectPrinting/Tests/ObjectPrinterTests.cs new file mode 100644 index 00000000..91b57257 --- /dev/null +++ b/ObjectPrinting/Tests/ObjectPrinterTests.cs @@ -0,0 +1,83 @@ +using System.Globalization; +using FluentAssertions; +using NUnit.Framework; + +namespace ObjectPrinting.Tests +{ + public class ObjectPrinterTests + { + private Person person; + + [SetUp] + public void Setup() + { + person = new Person + {Name = "Monkey", SecondName = "D.Luffy", NameOfPet = "Usopp", Height = 1234567.89, Age = 17}; + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithOutExcludingType() + { + var printer = ObjectPrinter.For() + .Exclude(); + string result = printer.PrintToString(person); + result.Should().NotContain(person.Name) + .And.NotContain(person.SecondName) + .And.NotContain(person.NameOfPet); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithOutExcludingField() + { + var printer = ObjectPrinter.For() + .Exclude(p => p.SecondName); + string result = printer.PrintToString(person); + result.Should().NotContain(person.SecondName); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithSelectedCulture() + { + var printer = ObjectPrinter.For() + .SelectCulture(new CultureInfo("fr-FR")); + string result = printer.PrintToString(person); + result.Should().Contain("1\u00a0234\u00a0567,890"); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithSpecialSerializeType() + { + var printer = ObjectPrinter.For() + .SerializeTypeWithSpecial((x) => $"~~ {x} ~~"); + string result = printer.PrintToString(person); + result.Should().Contain("~~ 1234567,89 ~~"); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithSpecialSerializeField() + { + var printer = ObjectPrinter.For() + .SelectField(p => p.Age).Using(x => 22.ToString()); + string result = printer.PrintToString(person); + result.Should().Contain("22"); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithTrimmedStringField() + { + var printer = ObjectPrinter.For() + .SelectField(p => p.SecondName).TrimmedToLength(1); + string result = printer.PrintToString(person); + result.Should().Contain("D").And.NotContain("D.Luffy"); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithExcludingPerson() + { + var printer = ObjectPrinter.For() + .Exclude(); + string result = printer.PrintToString(person); + result.Should().BeEmpty(); + } + } +} \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs index f9555955..f18821ca 100644 --- a/ObjectPrinting/Tests/Person.cs +++ b/ObjectPrinting/Tests/Person.cs @@ -6,6 +6,8 @@ public class Person { public Guid Id { get; set; } public string Name { get; set; } + public string SecondName { get; set; } + public string NameOfPet { get; set; } public double Height { get; set; } public int Age { get; set; } } From 6e3a42398de4154423a1744a40e36b58ad475fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B0=D0=B2=D0=B5=D0=BB=D0=B8=D0=B9?= Date: Wed, 11 Dec 2024 11:41:04 +0500 Subject: [PATCH 2/5] Add PrintingConfig.cs --- ObjectPrinting/PrintingConfig.cs | 130 +++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 14 deletions(-) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a9e08211..c79c5a40 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,11 +1,29 @@ using System; +using System.Collections.Generic; +using System.Globalization; using System.Linq; +using System.Linq.Expressions; +using System.Reflection; using System.Text; namespace ObjectPrinting { public class PrintingConfig { + private readonly HashSet excludedTypes = new(); + private readonly HashSet excludedFields = new(); + private readonly HashSet proccesedObjects = new(); + private readonly Dictionary> alternativeSerializeTypes = new(); + private readonly Dictionary> alternativeSerializeProperties = new(); + private readonly Dictionary alternativeCulture = new(); + private readonly Dictionary lengthOfProperties = new(); + + private readonly Type[] finalTypes = + { + typeof(int), typeof(double), typeof(float), typeof(string), + typeof(DateTime), typeof(TimeSpan) + }; + public string PrintToString(TOwner obj) { return PrintToString(obj, 0); @@ -14,28 +32,112 @@ 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) - }; - if (finalTypes.Contains(obj.GetType())) - return obj + Environment.NewLine; - + var specialSerialization = CheckSerializationConditions(obj); + if (specialSerialization != null) + return specialSerialization; + proccesedObjects.Add(obj); 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)); + if (excludedTypes.Contains(propertyInfo.PropertyType) || excludedFields.Contains(propertyInfo)) + continue; + if (lengthOfProperties.TryGetValue(propertyInfo, out var length)) + { + var substring = propertyInfo.GetValue(obj).ToString().Substring(0, length); + sb.Append(identation + propertyInfo.Name + " = " + + substring + Environment.NewLine); + continue; + } + + if (alternativeSerializeProperties.TryGetValue(propertyInfo, out var propertyFunc)) + { + var o = propertyInfo.GetValue(obj); + var res = propertyFunc(o); + sb.Append(identation + propertyInfo.Name + " = " + + res + Environment.NewLine); + } + + else + sb.Append(identation + propertyInfo.Name + " = " + + PrintToString(propertyInfo.GetValue(obj), + nestingLevel + 1)); } + return sb.ToString(); } + + private string CheckSerializationConditions(object obj) + { + if (obj == null) + return "null" + Environment.NewLine; + if (excludedTypes.Contains(obj.GetType())) + return ""; + if (proccesedObjects.Contains(obj)) + return "Cyclic reference" + Environment.NewLine; + if (alternativeSerializeTypes.TryGetValue(obj.GetType(), out var serializeFunc)) + return serializeFunc(obj) + Environment.NewLine; + + if (obj is IFormattable formObj && alternativeCulture.TryGetValue(obj.GetType(), out var newCulture)) + return formObj.ToString("N", newCulture) + Environment.NewLine; + + if (finalTypes.Contains(obj.GetType())) + return obj + Environment.NewLine; + + return null; + } + + public PrintingConfig Exclude() + { + excludedTypes.Add(typeof(T)); + return this; + } + + public PrintingConfig Exclude(Expression> func) + { + var propertyInfo = GetPropertyInfo(func); + excludedFields.Add(propertyInfo); + return this; + } + + public PrintingConfig SerializeTypeWithSpecial(Func serializeFunc) + { + alternativeSerializeTypes[typeof(T)] = o => serializeFunc((T) o); + return this; + } + + public PrintingConfig SelectCulture(CultureInfo cultureInfo) + { + alternativeCulture[typeof(T)] = cultureInfo; + return this; + } + + public PropertyPrintingConfig SelectField( + Expression> func) + { + var propertyInfo = GetPropertyInfo(func); + return new PropertyPrintingConfig(this, propertyInfo); + } + + private PropertyInfo GetPropertyInfo(Expression> propertyExpression) + { + var propertyName = ((MemberExpression) propertyExpression.Body).Member.Name; + var propertyInfo = typeof(TOwner).GetProperty(propertyName); + if (propertyInfo == null) + throw new Exception($"Property {propertyName} could not be found."); + return propertyInfo; + } + + public void AddSerializedProperty(PropertyInfo propertyInfo, Func serializeFunc) + { + alternativeSerializeProperties[propertyInfo] = o => serializeFunc((T) o); + } + + public void AddLengthOfProperty(PropertyInfo propertyInfo, int length) + { + lengthOfProperties[propertyInfo] = length; + } } } \ No newline at end of file From 19754992feba7efa1294cf7110679ce0e4c57e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B0=D0=B2=D0=B5=D0=BB=D0=B8=D0=B9?= Date: Wed, 11 Dec 2024 16:02:09 +0500 Subject: [PATCH 3/5] Add implementation of collections --- ObjectPrinting/ObjectExtensions.cs | 9 +++ ObjectPrinting/PrintingConfig.cs | 69 ++++++++++++++--- ObjectPrinting/Tests/ObjectPrinterTests.cs | 90 +++++++++++++++++++++- ObjectPrinting/Tests/Person.cs | 7 ++ 4 files changed, 164 insertions(+), 11 deletions(-) create mode 100644 ObjectPrinting/ObjectExtensions.cs diff --git a/ObjectPrinting/ObjectExtensions.cs b/ObjectPrinting/ObjectExtensions.cs new file mode 100644 index 00000000..f2ef5db7 --- /dev/null +++ b/ObjectPrinting/ObjectExtensions.cs @@ -0,0 +1,9 @@ +namespace ObjectPrinting; + +public static class ObjectExtensions +{ + public static string PrintToString(this T obj) + { + return ObjectPrinter.For().PrintToString(obj); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index c79c5a40..3e4977a3 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -21,7 +22,7 @@ public class PrintingConfig private readonly Type[] finalTypes = { typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) + typeof(DateTime), typeof(TimeSpan), typeof(Guid) }; public string PrintToString(TOwner obj) @@ -32,11 +33,18 @@ public string PrintToString(TOwner obj) private string PrintToString(object obj, int nestingLevel) { //TODO apply configurations - var specialSerialization = CheckSerializationConditions(obj); + var identation = new string('\t', nestingLevel + 1); + + var specialSerialization = CheckSerializationConditions(obj, identation); if (specialSerialization != null) return specialSerialization; + + if (obj is IDictionary dictionary) + return PrintDictionary(dictionary, nestingLevel); + if (obj is IEnumerable collection) + return PrintCollection(collection, nestingLevel); + proccesedObjects.Add(obj); - var identation = new string('\t', nestingLevel + 1); var sb = new StringBuilder(); var type = obj.GetType(); sb.AppendLine(type.Name); @@ -53,13 +61,9 @@ private string PrintToString(object obj, int nestingLevel) } if (alternativeSerializeProperties.TryGetValue(propertyInfo, out var propertyFunc)) - { - var o = propertyInfo.GetValue(obj); - var res = propertyFunc(o); - sb.Append(identation + propertyInfo.Name + " = " + - res + Environment.NewLine); - } + sb.Append(identation + propertyInfo.Name + " = " + + propertyFunc(propertyInfo.GetValue(obj)) + Environment.NewLine); else sb.Append(identation + propertyInfo.Name + " = " + PrintToString(propertyInfo.GetValue(obj), @@ -69,7 +73,8 @@ private string PrintToString(object obj, int nestingLevel) return sb.ToString(); } - private string CheckSerializationConditions(object obj) + + private string CheckSerializationConditions(object obj, string identation) { if (obj == null) return "null" + Environment.NewLine; @@ -89,6 +94,50 @@ private string CheckSerializationConditions(object obj) return null; } + private string PrintCollection(IEnumerable collection, int nestingLevel) + { + var sb = new StringBuilder(); + var identation = new string('\t', nestingLevel + 1); + + sb.Append(identation + collection.GetType().Name + "{" + Environment.NewLine); + int index = 0; + foreach (var item in collection) + { + sb.Append(identation + "\t[" + index + "] = "); + sb.Append(GetStringWithNestingLevel(item, nestingLevel + 2).Trim()); + sb.Append(Environment.NewLine); + index++; + } + + sb.Append(identation + "}"); + return sb.ToString(); + } + + private string PrintDictionary(IDictionary dictionary, int nestingLevel) + { + var sb = new StringBuilder(); + var identation = new string('\t', nestingLevel + 1); + sb.Append(identation + dictionary.GetType().Name + "{" + Environment.NewLine); + foreach (DictionaryEntry element in dictionary) + { + var key = GetStringWithNestingLevel(element.Key, nestingLevel + 1); + var value = GetStringWithNestingLevel(element.Value, nestingLevel + 2).Trim(); + sb.Append(key + " : " + value + Environment.NewLine); + } + + sb.Append(identation + "}"); + return sb.ToString(); + } + + private string GetStringWithNestingLevel(object obj, int nestingLevel) + { + var identation = new string('\t', nestingLevel + 1); + if (finalTypes.Contains(obj.GetType())) + return identation + obj; + var result = PrintToString(obj, nestingLevel); + return Environment.NewLine + identation + result; + } + public PrintingConfig Exclude() { excludedTypes.Add(typeof(T)); diff --git a/ObjectPrinting/Tests/ObjectPrinterTests.cs b/ObjectPrinting/Tests/ObjectPrinterTests.cs index 91b57257..940114fe 100644 --- a/ObjectPrinting/Tests/ObjectPrinterTests.cs +++ b/ObjectPrinting/Tests/ObjectPrinterTests.cs @@ -1,4 +1,6 @@ -using System.Globalization; +using System; +using System.Collections.Generic; +using System.Globalization; using FluentAssertions; using NUnit.Framework; @@ -79,5 +81,91 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithExcludingPerson() string result = printer.PrintToString(person); result.Should().BeEmpty(); } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithDictionary() + { + var dict = new Dictionary + { + {"Robin", "Archeologist"}, + {"Usopp", "Cannoneer"}, + {"Luffy", "Captain"} + }; + var expected = "\tDictionary`2{" + Environment.NewLine + + "\t\tRobin : Archeologist" + Environment.NewLine + + "\t\tUsopp : Cannoneer" + Environment.NewLine + + "\t\tLuffy : Captain" + Environment.NewLine + + "\t}"; + dict.PrintToString().Should().Contain(expected); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithDictionaryContainsPerson() + { + var dict = new Dictionary + { + {"Archeologist", person}, + }; + var expected = "\tDictionary`2{" + Environment.NewLine + + "\t\tArcheologist : Person" + Environment.NewLine + + "\t\t\tId = 00000000-0000-0000-0000-000000000000" + Environment.NewLine + + "\t\t\tName = Monkey" + Environment.NewLine + + "\t\t\tSecondName = D.Luffy" + Environment.NewLine + + "\t\t\tNameOfPet = Usopp" + Environment.NewLine + + "\t\t\tHeight = 1234567,89" + Environment.NewLine + + "\t\t\tAge = 17" + Environment.NewLine + + "\t\t\tCountsOfTeamMembers = null" + Environment.NewLine + + "\t\t\tAlliedTeams = null" + Environment.NewLine + + "\t\t\tTeam = null" + Environment.NewLine + + "\t}"; + dict.PrintToString().Should().Contain(expected); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithArray() + { + int[] numbers = {1, 2, 3, 4, 5}; + var expected = "\tInt32[]{" + Environment.NewLine + + "\t\t[0] = 1" + Environment.NewLine + + "\t\t[1] = 2" + Environment.NewLine + + "\t\t[2] = 3" + Environment.NewLine + + "\t\t[3] = 4" + Environment.NewLine + + "\t\t[4] = 5" + Environment.NewLine + + "\t}"; + numbers.PrintToString().Should().Contain(expected); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithList() + { + List members = new List {"Robin", "Usopp", "Luffy", "Zoro", "Nami"}; + var expected = "\tList`1{" + Environment.NewLine + + "\t\t[0] = Robin" + Environment.NewLine + + "\t\t[1] = Usopp" + Environment.NewLine + + "\t\t[2] = Luffy" + Environment.NewLine + + "\t\t[3] = Zoro" + Environment.NewLine + + "\t\t[4] = Nami" + Environment.NewLine + + "\t}"; + members.PrintToString().Should().Contain(expected); + } + + [Test] + public void ObjetctPrinter_ShouldCorrectPrint_WithLContainsPerson() + { + List members = new List {person}; + var expected = "\tList`1{" + Environment.NewLine + + "\t\t[0] = Person" + Environment.NewLine + + "\t\t\tId = 00000000-0000-0000-0000-000000000000" + Environment.NewLine + + "\t\t\tName = Monkey" + Environment.NewLine + + "\t\t\tSecondName = D.Luffy" + Environment.NewLine + + "\t\t\tNameOfPet = Usopp" + Environment.NewLine + + "\t\t\tHeight = 1234567,89" + Environment.NewLine + + "\t\t\tAge = 17" + Environment.NewLine + + "\t\t\tCountsOfTeamMembers = null" + Environment.NewLine + + "\t\t\tAlliedTeams = null" + Environment.NewLine + + "\t\t\tTeam = null" + Environment.NewLine + + "\t}"; + members.PrintToString().Should().Contain(expected); + } } } \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs index f18821ca..4de57128 100644 --- a/ObjectPrinting/Tests/Person.cs +++ b/ObjectPrinting/Tests/Person.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace ObjectPrinting.Tests { @@ -10,5 +11,11 @@ public class Person public string NameOfPet { get; set; } public double Height { get; set; } public int Age { get; set; } + + public int[] CountsOfTeamMembers { get; set; } + + public List AlliedTeams { get; set; } + + public Dictionary Team { get; set; } } } \ No newline at end of file From 671ac257815f59c497c00cfd7f13206598ab16b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B0=D0=B2=D0=B5=D0=BB=D0=B8=D0=B9?= Date: Thu, 12 Dec 2024 00:28:42 +0500 Subject: [PATCH 4/5] refactor project --- ObjectPrinting/PrintingConfig.cs | 80 +++++++++---------- ObjectPrinting/PropertyPrintingConfig.cs | 24 +++++- .../ObjectPrintingTests.cs | 77 +++++++++--------- .../ObjectPrintingTests.csproj | 19 +++++ .../Tests => ObjectPrintingTests}/Person.cs | 0 5 files changed, 118 insertions(+), 82 deletions(-) rename ObjectPrinting/Tests/ObjectPrinterTests.cs => ObjectPrintingTests/ObjectPrintingTests.cs (72%) create mode 100644 ObjectPrintingTests/ObjectPrintingTests.csproj rename {ObjectPrinting/Tests => ObjectPrintingTests}/Person.cs (100%) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 3e4977a3..9cbccd1a 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -33,53 +33,47 @@ public string PrintToString(TOwner obj) private string PrintToString(object obj, int nestingLevel) { //TODO apply configurations - var identation = new string('\t', nestingLevel + 1); + var indentation = new string('\t', nestingLevel + 1); - var specialSerialization = CheckSerializationConditions(obj, identation); + var specialSerialization = TryGetSerializationString(obj, nestingLevel); if (specialSerialization != null) return specialSerialization; - if (obj is IDictionary dictionary) - return PrintDictionary(dictionary, nestingLevel); - if (obj is IEnumerable collection) - return PrintCollection(collection, nestingLevel); - proccesedObjects.Add(obj); var sb = new StringBuilder(); var type = obj.GetType(); sb.AppendLine(type.Name); foreach (var propertyInfo in type.GetProperties()) { + string property = indentation + propertyInfo.Name + " = "; if (excludedTypes.Contains(propertyInfo.PropertyType) || excludedFields.Contains(propertyInfo)) continue; if (lengthOfProperties.TryGetValue(propertyInfo, out var length)) { var substring = propertyInfo.GetValue(obj).ToString().Substring(0, length); - sb.Append(identation + propertyInfo.Name + " = " + - substring + Environment.NewLine); + sb.Append(property + substring + Environment.NewLine); continue; } + string serializedObj; if (alternativeSerializeProperties.TryGetValue(propertyInfo, out var propertyFunc)) - - sb.Append(identation + propertyInfo.Name + " = " + - propertyFunc(propertyInfo.GetValue(obj)) + Environment.NewLine); + serializedObj = property + propertyFunc(propertyInfo.GetValue(obj)) + Environment.NewLine; else - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); + serializedObj = property + PrintToString(propertyInfo.GetValue(obj), nestingLevel + 1); + + sb.Append(serializedObj); } return sb.ToString(); } - private string CheckSerializationConditions(object obj, string identation) + private string TryGetSerializationString(object? obj, int nestingLevel) { if (obj == null) return "null" + Environment.NewLine; if (excludedTypes.Contains(obj.GetType())) - return ""; + return string.Empty; if (proccesedObjects.Contains(obj)) return "Cyclic reference" + Environment.NewLine; if (alternativeSerializeTypes.TryGetValue(obj.GetType(), out var serializeFunc)) @@ -87,9 +81,12 @@ private string CheckSerializationConditions(object obj, string identation) if (obj is IFormattable formObj && alternativeCulture.TryGetValue(obj.GetType(), out var newCulture)) return formObj.ToString("N", newCulture) + Environment.NewLine; - if (finalTypes.Contains(obj.GetType())) return obj + Environment.NewLine; + if (obj is IDictionary dictionary) + return PrintDictionary(dictionary, nestingLevel); + if (obj is IEnumerable collection) + return PrintCollection(collection, nestingLevel); return null; } @@ -97,27 +94,27 @@ private string CheckSerializationConditions(object obj, string identation) private string PrintCollection(IEnumerable collection, int nestingLevel) { var sb = new StringBuilder(); - var identation = new string('\t', nestingLevel + 1); + var indentation = new string('\t', nestingLevel + 1); - sb.Append(identation + collection.GetType().Name + "{" + Environment.NewLine); + sb.Append(indentation + collection.GetType().Name + "{" + Environment.NewLine); int index = 0; foreach (var item in collection) { - sb.Append(identation + "\t[" + index + "] = "); + sb.Append(indentation + "\t[" + index + "] = "); sb.Append(GetStringWithNestingLevel(item, nestingLevel + 2).Trim()); sb.Append(Environment.NewLine); index++; } - sb.Append(identation + "}"); + sb.Append(indentation + "}"); return sb.ToString(); } private string PrintDictionary(IDictionary dictionary, int nestingLevel) { var sb = new StringBuilder(); - var identation = new string('\t', nestingLevel + 1); - sb.Append(identation + dictionary.GetType().Name + "{" + Environment.NewLine); + var indentation = new string('\t', nestingLevel + 1); + sb.Append(indentation + dictionary.GetType().Name + "{" + Environment.NewLine); foreach (DictionaryEntry element in dictionary) { var key = GetStringWithNestingLevel(element.Key, nestingLevel + 1); @@ -125,17 +122,17 @@ private string PrintDictionary(IDictionary dictionary, int nestingLevel) sb.Append(key + " : " + value + Environment.NewLine); } - sb.Append(identation + "}"); + sb.Append(indentation + "}"); return sb.ToString(); } private string GetStringWithNestingLevel(object obj, int nestingLevel) { - var identation = new string('\t', nestingLevel + 1); + var indentation = new string('\t', nestingLevel + 1); if (finalTypes.Contains(obj.GetType())) - return identation + obj; + return indentation + obj; var result = PrintToString(obj, nestingLevel); - return Environment.NewLine + identation + result; + return Environment.NewLine + indentation + result; } public PrintingConfig Exclude() @@ -151,19 +148,12 @@ public PrintingConfig Exclude(Expression> func) return this; } - public PrintingConfig SerializeTypeWithSpecial(Func serializeFunc) + public PropertyPrintingConfig Printing() { - alternativeSerializeTypes[typeof(T)] = o => serializeFunc((T) o); - return this; - } - - public PrintingConfig SelectCulture(CultureInfo cultureInfo) - { - alternativeCulture[typeof(T)] = cultureInfo; - return this; + return new PropertyPrintingConfig(this, null); } - public PropertyPrintingConfig SelectField( + public PropertyPrintingConfig Printing( Expression> func) { var propertyInfo = GetPropertyInfo(func); @@ -179,14 +169,24 @@ private PropertyInfo GetPropertyInfo(Expression> propertyExpr return propertyInfo; } - public void AddSerializedProperty(PropertyInfo propertyInfo, Func serializeFunc) + internal void AddSerializedProperty(PropertyInfo propertyInfo, Func serializeFunc) { alternativeSerializeProperties[propertyInfo] = o => serializeFunc((T) o); } - public void AddLengthOfProperty(PropertyInfo propertyInfo, int length) + internal void AddLengthOfProperty(PropertyInfo propertyInfo, int length) { lengthOfProperties[propertyInfo] = length; } + + internal void AddAlternativeCulture(Type type, CultureInfo cultureInfo) + { + alternativeCulture[type] = cultureInfo; + } + + internal void AddSerializedType(Func serializeFunc) + { + alternativeSerializeTypes[typeof(T)] = o => serializeFunc((T) o); + } } } \ No newline at end of file diff --git a/ObjectPrinting/PropertyPrintingConfig.cs b/ObjectPrinting/PropertyPrintingConfig.cs index c7676121..9a70fec5 100644 --- a/ObjectPrinting/PropertyPrintingConfig.cs +++ b/ObjectPrinting/PropertyPrintingConfig.cs @@ -1,12 +1,13 @@ using System; +using System.Globalization; using System.Reflection; namespace ObjectPrinting; public class PropertyPrintingConfig { - public readonly PrintingConfig PrintingConfig; - public readonly PropertyInfo PropertyInfo; + internal PrintingConfig PrintingConfig { get; } + internal readonly PropertyInfo? PropertyInfo; public PropertyPrintingConfig(PrintingConfig printingConfig, PropertyInfo propertyInfo) @@ -17,7 +18,24 @@ public PropertyPrintingConfig(PrintingConfig printingConfig, PropertyInf public PrintingConfig Using(Func newSerialize) { - PrintingConfig.AddSerializedProperty(PropertyInfo, newSerialize); + return PropertyInfo == null ? SetSerializedType(newSerialize) : SetSerializedProp(newSerialize); + } + + public PrintingConfig Using(CultureInfo cultureInfo) + { + PrintingConfig.AddAlternativeCulture(typeof(TPropType), cultureInfo); + return PrintingConfig; + } + + private PrintingConfig SetSerializedProp(Func newSerialize) + { + PrintingConfig.AddSerializedProperty(PropertyInfo!, newSerialize); + return PrintingConfig; + } + + private PrintingConfig SetSerializedType(Func newSerialize) + { + PrintingConfig.AddSerializedType(newSerialize); return PrintingConfig; } } \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterTests.cs b/ObjectPrintingTests/ObjectPrintingTests.cs similarity index 72% rename from ObjectPrinting/Tests/ObjectPrinterTests.cs rename to ObjectPrintingTests/ObjectPrintingTests.cs index 940114fe..f5043c7e 100644 --- a/ObjectPrinting/Tests/ObjectPrinterTests.cs +++ b/ObjectPrintingTests/ObjectPrintingTests.cs @@ -1,76 +1,75 @@ -using System; -using System.Collections.Generic; -using System.Globalization; +using System.Globalization; using FluentAssertions; using NUnit.Framework; +using ObjectPrinting; +using ObjectPrinting.Tests; -namespace ObjectPrinting.Tests +namespace ObjectPrintingTests; + +public class ObjectPrintingTests { public class ObjectPrinterTests { - private Person person; - - [SetUp] - public void Setup() - { - person = new Person - {Name = "Monkey", SecondName = "D.Luffy", NameOfPet = "Usopp", Height = 1234567.89, Age = 17}; - } + private static readonly Person Person = new() + {Name = "Monkey", SecondName = "D.Luffy", NameOfPet = "Usopp", Height = 168.8, Age = 17}; [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithOutExcludingType() + public void ObjetctPrinter_ShouldCorrectPrint_WithExcludingType() { var printer = ObjectPrinter.For() .Exclude(); - string result = printer.PrintToString(person); - result.Should().NotContain(person.Name) - .And.NotContain(person.SecondName) - .And.NotContain(person.NameOfPet); + + printer.PrintToString(Person).Should().NotContain(Person.Name) + .And.NotContain(Person.SecondName) + .And.NotContain(Person.NameOfPet); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithOutExcludingField() + public void ObjetctPrinter_ShouldCorrectPrint_WithExcludingField() { var printer = ObjectPrinter.For() .Exclude(p => p.SecondName); - string result = printer.PrintToString(person); - result.Should().NotContain(person.SecondName); + + printer.PrintToString(Person).Should().NotContain(Person.SecondName); } [Test] public void ObjetctPrinter_ShouldCorrectPrint_WithSelectedCulture() { - var printer = ObjectPrinter.For() - .SelectCulture(new CultureInfo("fr-FR")); - string result = printer.PrintToString(person); - result.Should().Contain("1\u00a0234\u00a0567,890"); + var printerEn = ObjectPrinter.For() + .Printing().Using(new CultureInfo("en-US")); + var printerRu = ObjectPrinter.For() + .Printing().Using(new CultureInfo("ru-Ru")); + + printerEn.PrintToString(Person).Should().Contain("168.8"); + printerRu.PrintToString(Person).Should().Contain("168,8"); } [Test] public void ObjetctPrinter_ShouldCorrectPrint_WithSpecialSerializeType() { var printer = ObjectPrinter.For() - .SerializeTypeWithSpecial((x) => $"~~ {x} ~~"); - string result = printer.PrintToString(person); - result.Should().Contain("~~ 1234567,89 ~~"); + .Printing().Using((x) => $"~~ {x} ~~"); + + printer.PrintToString(Person).Should().Contain("~~ 168,8 ~~"); } [Test] public void ObjetctPrinter_ShouldCorrectPrint_WithSpecialSerializeField() { var printer = ObjectPrinter.For() - .SelectField(p => p.Age).Using(x => 22.ToString()); - string result = printer.PrintToString(person); - result.Should().Contain("22"); + .Printing(p => p.Age).Using(x => 22.ToString()); + + printer.PrintToString(Person).Should().Contain("22"); } [Test] public void ObjetctPrinter_ShouldCorrectPrint_WithTrimmedStringField() { var printer = ObjectPrinter.For() - .SelectField(p => p.SecondName).TrimmedToLength(1); - string result = printer.PrintToString(person); - result.Should().Contain("D").And.NotContain("D.Luffy"); + .Printing(p => p.SecondName).TrimmedToLength(1); + + printer.PrintToString(Person).Should().Contain("D").And.NotContain("D.Luffy"); } [Test] @@ -78,8 +77,8 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithExcludingPerson() { var printer = ObjectPrinter.For() .Exclude(); - string result = printer.PrintToString(person); - result.Should().BeEmpty(); + + printer.PrintToString(Person).Should().BeEmpty(); } [Test] @@ -104,7 +103,7 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithDictionaryContainsPerson() { var dict = new Dictionary { - {"Archeologist", person}, + {"Archeologist", Person}, }; var expected = "\tDictionary`2{" + Environment.NewLine + "\t\tArcheologist : Person" + Environment.NewLine + @@ -112,7 +111,7 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithDictionaryContainsPerson() "\t\t\tName = Monkey" + Environment.NewLine + "\t\t\tSecondName = D.Luffy" + Environment.NewLine + "\t\t\tNameOfPet = Usopp" + Environment.NewLine + - "\t\t\tHeight = 1234567,89" + Environment.NewLine + + "\t\t\tHeight = 168,8" + Environment.NewLine + "\t\t\tAge = 17" + Environment.NewLine + "\t\t\tCountsOfTeamMembers = null" + Environment.NewLine + "\t\t\tAlliedTeams = null" + Environment.NewLine + @@ -152,14 +151,14 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithList() [Test] public void ObjetctPrinter_ShouldCorrectPrint_WithLContainsPerson() { - List members = new List {person}; + List members = new List {Person}; var expected = "\tList`1{" + Environment.NewLine + "\t\t[0] = Person" + Environment.NewLine + "\t\t\tId = 00000000-0000-0000-0000-000000000000" + Environment.NewLine + "\t\t\tName = Monkey" + Environment.NewLine + "\t\t\tSecondName = D.Luffy" + Environment.NewLine + "\t\t\tNameOfPet = Usopp" + Environment.NewLine + - "\t\t\tHeight = 1234567,89" + Environment.NewLine + + "\t\t\tHeight = 168,8" + Environment.NewLine + "\t\t\tAge = 17" + Environment.NewLine + "\t\t\tCountsOfTeamMembers = null" + Environment.NewLine + "\t\t\tAlliedTeams = null" + Environment.NewLine + diff --git a/ObjectPrintingTests/ObjectPrintingTests.csproj b/ObjectPrintingTests/ObjectPrintingTests.csproj new file mode 100644 index 00000000..4ff99ca3 --- /dev/null +++ b/ObjectPrintingTests/ObjectPrintingTests.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrintingTests/Person.cs similarity index 100% rename from ObjectPrinting/Tests/Person.cs rename to ObjectPrintingTests/Person.cs From 49f0beb2ed9c9e15cb03f671582445d8f9bdf9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B0=D0=B2=D0=B5=D0=BB=D0=B8=D0=B9?= Date: Thu, 12 Dec 2024 11:47:39 +0500 Subject: [PATCH 5/5] refactor ObjectPrintingTests.cs --- ObjectPrintingTests/ObjectPrintingTests.cs | 88 ++++++++++++++-------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/ObjectPrintingTests/ObjectPrintingTests.cs b/ObjectPrintingTests/ObjectPrintingTests.cs index f5043c7e..4c514684 100644 --- a/ObjectPrintingTests/ObjectPrintingTests.cs +++ b/ObjectPrintingTests/ObjectPrintingTests.cs @@ -14,75 +14,88 @@ public class ObjectPrinterTests {Name = "Monkey", SecondName = "D.Luffy", NameOfPet = "Usopp", Height = 168.8, Age = 17}; [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithExcludingType() + public void ObjectPrinter_ShouldCorrectPrint_WithExcludingType() { var printer = ObjectPrinter.For() .Exclude(); - printer.PrintToString(Person).Should().NotContain(Person.Name) + var actual = printer.PrintToString(Person); + + actual.Should().NotContain(Person.Name) .And.NotContain(Person.SecondName) .And.NotContain(Person.NameOfPet); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithExcludingField() + public void ObjectPrinter_ShouldCorrectPrint_WithExcludingField() { var printer = ObjectPrinter.For() .Exclude(p => p.SecondName); - printer.PrintToString(Person).Should().NotContain(Person.SecondName); + var actual = printer.PrintToString(Person); + + actual.Should().NotContain(Person.SecondName); } - [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithSelectedCulture() + + [TestCase("en-US", "168.8")] + [TestCase("ru-RU", "168,8")] + public void ObjectPrinter_ShouldCorrectPrint_WithSelectedCulture(string culture, string expectedValue) { - var printerEn = ObjectPrinter.For() - .Printing().Using(new CultureInfo("en-US")); - var printerRu = ObjectPrinter.For() - .Printing().Using(new CultureInfo("ru-Ru")); + var printer = ObjectPrinter.For() + .Printing().Using(new CultureInfo(culture)); - printerEn.PrintToString(Person).Should().Contain("168.8"); - printerRu.PrintToString(Person).Should().Contain("168,8"); + var actual = printer.PrintToString(Person); + + actual.Should().Contain(expectedValue); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithSpecialSerializeType() + public void ObjectPrinter_ShouldCorrectPrint_WithSpecialSerializeType() { var printer = ObjectPrinter.For() .Printing().Using((x) => $"~~ {x} ~~"); - printer.PrintToString(Person).Should().Contain("~~ 168,8 ~~"); + var actual = printer.PrintToString(Person); + + actual.Should().Contain("~~ 168,8 ~~"); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithSpecialSerializeField() + public void ObjectPrinter_ShouldCorrectPrint_WithSpecialSerializeField() { var printer = ObjectPrinter.For() .Printing(p => p.Age).Using(x => 22.ToString()); - printer.PrintToString(Person).Should().Contain("22"); + var actual = printer.PrintToString(Person); + + actual.Should().Contain("22"); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithTrimmedStringField() + public void ObjectPrinter_ShouldCorrectPrint_WithTrimmedStringField() { var printer = ObjectPrinter.For() .Printing(p => p.SecondName).TrimmedToLength(1); - printer.PrintToString(Person).Should().Contain("D").And.NotContain("D.Luffy"); + var actual = printer.PrintToString(Person); + + actual.Should().Contain("D").And.NotContain("D.Luffy"); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithExcludingPerson() + public void ObjectPrinter_ShouldCorrectPrint_WithExcludingPerson() { var printer = ObjectPrinter.For() .Exclude(); - printer.PrintToString(Person).Should().BeEmpty(); + var actual = printer.PrintToString(Person); + + actual.Should().BeEmpty(); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithDictionary() + public void ObjectPrinter_ShouldCorrectPrint_WithDictionary() { var dict = new Dictionary { @@ -95,11 +108,14 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithDictionary() "\t\tUsopp : Cannoneer" + Environment.NewLine + "\t\tLuffy : Captain" + Environment.NewLine + "\t}"; - dict.PrintToString().Should().Contain(expected); + + var actual = dict.PrintToString(); + + actual.Should().Contain(expected); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithDictionaryContainsPerson() + public void ObjectPrinter_ShouldCorrectPrint_WithDictionaryContainsPerson() { var dict = new Dictionary { @@ -117,11 +133,14 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithDictionaryContainsPerson() "\t\t\tAlliedTeams = null" + Environment.NewLine + "\t\t\tTeam = null" + Environment.NewLine + "\t}"; - dict.PrintToString().Should().Contain(expected); + + var actual = dict.PrintToString(); + + actual.Should().Contain(expected); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithArray() + public void ObjectPrinter_ShouldCorrectPrint_WithArray() { int[] numbers = {1, 2, 3, 4, 5}; var expected = "\tInt32[]{" + Environment.NewLine + @@ -131,11 +150,14 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithArray() "\t\t[3] = 4" + Environment.NewLine + "\t\t[4] = 5" + Environment.NewLine + "\t}"; - numbers.PrintToString().Should().Contain(expected); + + var actual = numbers.PrintToString(); + + actual.Should().Contain(expected); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithList() + public void ObjectPrinter_ShouldCorrectPrint_WithList() { List members = new List {"Robin", "Usopp", "Luffy", "Zoro", "Nami"}; var expected = "\tList`1{" + Environment.NewLine + @@ -145,11 +167,14 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithList() "\t\t[3] = Zoro" + Environment.NewLine + "\t\t[4] = Nami" + Environment.NewLine + "\t}"; - members.PrintToString().Should().Contain(expected); + + var actual = members.PrintToString(); + + actual.Should().Contain(expected); } [Test] - public void ObjetctPrinter_ShouldCorrectPrint_WithLContainsPerson() + public void ObjectPrinter_ShouldCorrectPrint_WithLContainsPerson() { List members = new List {Person}; var expected = "\tList`1{" + Environment.NewLine + @@ -164,7 +189,10 @@ public void ObjetctPrinter_ShouldCorrectPrint_WithLContainsPerson() "\t\t\tAlliedTeams = null" + Environment.NewLine + "\t\t\tTeam = null" + Environment.NewLine + "\t}"; - members.PrintToString().Should().Contain(expected); + + var actual = members.PrintToString(); + + actual.Should().Contain(expected); } } } \ No newline at end of file