From a2bd173706356df16b0720d044fd41667a27c4d4 Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Tue, 26 Dec 2023 20:39:13 +0500 Subject: [PATCH 1/4] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/Extensions/MemberExtensions.cs | 24 +++ ObjectPrinting/Extensions/ObjectExtensions.cs | 18 ++ .../PrintingPropertyConfigExtensions.cs | 18 ++ ObjectPrinting/PrintingConfig.cs | 126 ++++++++++++-- ObjectPrinting/PrintingPropertyConfig.cs | 29 ++++ ObjectPrinting/Solved/ObjectExtensions.cs | 10 -- ObjectPrinting/Solved/ObjectPrinter.cs | 10 -- ObjectPrinting/Solved/PrintingConfig.cs | 62 ------- .../Solved/PropertyPrintingConfig.cs | 32 ---- .../PropertyPrintingConfigExtensions.cs | 18 -- ObjectPrinting/Solved/Tests/Person.cs | 12 -- .../Tests/ObjectPrinterAcceptanceTests.cs | 27 --- ObjectPrinting/Tests/Person.cs | 12 -- .../ObjectPrinterAcceptanceTests.cs | 42 +++-- .../ObjectPrintingTests.csproj | 26 +++ ObjectPrintingTests/ObjectPrinting_Should.cs | 163 ++++++++++++++++++ ObjectPrintingTests/Person.cs | 15 ++ fluent-api.sln | 6 + 18 files changed, 436 insertions(+), 214 deletions(-) create mode 100644 ObjectPrinting/Extensions/MemberExtensions.cs create mode 100644 ObjectPrinting/Extensions/ObjectExtensions.cs create mode 100644 ObjectPrinting/Extensions/PrintingPropertyConfigExtensions.cs create mode 100644 ObjectPrinting/PrintingPropertyConfig.cs delete mode 100644 ObjectPrinting/Solved/ObjectExtensions.cs delete mode 100644 ObjectPrinting/Solved/ObjectPrinter.cs delete mode 100644 ObjectPrinting/Solved/PrintingConfig.cs delete mode 100644 ObjectPrinting/Solved/PropertyPrintingConfig.cs delete mode 100644 ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs delete mode 100644 ObjectPrinting/Solved/Tests/Person.cs delete mode 100644 ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs delete mode 100644 ObjectPrinting/Tests/Person.cs rename {ObjectPrinting/Solved/Tests => ObjectPrintingTests}/ObjectPrinterAcceptanceTests.cs (51%) create mode 100644 ObjectPrintingTests/ObjectPrintingTests.csproj create mode 100644 ObjectPrintingTests/ObjectPrinting_Should.cs create mode 100644 ObjectPrintingTests/Person.cs diff --git a/ObjectPrinting/Extensions/MemberExtensions.cs b/ObjectPrinting/Extensions/MemberExtensions.cs new file mode 100644 index 00000000..8d1da355 --- /dev/null +++ b/ObjectPrinting/Extensions/MemberExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Reflection; + +namespace ObjectPrinting.Extensions +{ + public static class MemberExtensions + { + internal static Type GetMemberType(this MemberInfo memberInfo) => + memberInfo.MemberType switch + { + MemberTypes.Property => ((PropertyInfo)memberInfo).PropertyType, + MemberTypes.Field => ((FieldInfo)memberInfo).FieldType, + _ => throw new NotImplementedException($"Getting type of {memberInfo.MemberType} is not implemented") + }; + + public static object GetValue(this MemberInfo memberInfo, object obj) => + memberInfo.MemberType switch + { + MemberTypes.Property => ((PropertyInfo)memberInfo).GetValue(obj), + MemberTypes.Field => ((FieldInfo)memberInfo).GetValue(obj), + _ => throw new NotImplementedException($"Getting value of {memberInfo.MemberType} is not implemented") + }; + } +} diff --git a/ObjectPrinting/Extensions/ObjectExtensions.cs b/ObjectPrinting/Extensions/ObjectExtensions.cs new file mode 100644 index 00000000..92279237 --- /dev/null +++ b/ObjectPrinting/Extensions/ObjectExtensions.cs @@ -0,0 +1,18 @@ +using System; + +namespace ObjectPrinting.Extensions +{ + public static class ObjectExtensions + { + public static string PrintToString(this T obj) + { + return ObjectPrinter.For().PrintToString(obj); + } + + public static string PrintToString(this TOwner obj, + Func, PrintingConfig> config) + { + return config(ObjectPrinter.For()).PrintToString(obj); + } + } +} diff --git a/ObjectPrinting/Extensions/PrintingPropertyConfigExtensions.cs b/ObjectPrinting/Extensions/PrintingPropertyConfigExtensions.cs new file mode 100644 index 00000000..d524a044 --- /dev/null +++ b/ObjectPrinting/Extensions/PrintingPropertyConfigExtensions.cs @@ -0,0 +1,18 @@ +using System; +using System.Globalization; + +namespace ObjectPrinting.Extensions +{ + public static class PrintingPropertyConfigExtensions + { + public static PrintingConfig ToTrimmedLength( + this PrintingPropertyConfig config, int maxLength) => + config.To(x => x.Length <= maxLength ? x : x[..maxLength]); + + public static PrintingConfig To( + this PrintingPropertyConfig config, CultureInfo culture) where TPropType : IFormattable + { + return config.To(x => x.ToString(null, culture)); + } + } +} diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a9e08211..701381c0 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,41 +1,139 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; +using System.Reflection; using System.Text; +using ObjectPrinting.Extensions; namespace ObjectPrinting { public class PrintingConfig { - public string PrintToString(TOwner obj) + private readonly HashSet finalTypes = new HashSet { - return PrintToString(obj, 0); + typeof(int), typeof(double), typeof(float), + typeof(bool), typeof(long), typeof(string), + typeof(DateTime), typeof(TimeSpan), typeof(Guid) + }; + + private readonly HashSet excludedMembers = new HashSet(); + private readonly HashSet excludedTypes = new HashSet(); + private readonly HashSet serializedObjects = new HashSet(); + + private readonly Dictionary> memberSerializers = + new Dictionary>(); + + public string PrintToString(TOwner obj) => PrintToString(obj, 0); + + public PrintingConfig ExcludeProperty() + { + excludedTypes.Add(typeof(TProperty)); + return this; + } + + public PrintingConfig ExcludeProperty(Expression> memberSelector) + { + excludedMembers.Add(((MemberExpression)memberSelector.Body).Member); + return this; + } + + public PrintingPropertyConfig ChangeSerializationFor() => + new PrintingPropertyConfig(this, memberSerializers, typeof(TProperty)); + + public PrintingPropertyConfig ChangeSerializationFor( + Expression> memberSelector) + { + var body = (MemberExpression)memberSelector.Body; + return new PrintingPropertyConfig(this, memberSerializers, body.Member); } 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())) + { + if (memberSerializers.TryGetValue(obj.GetType(), out var serializer)) + return serializer(obj) + Environment.NewLine; return obj + Environment.NewLine; + } + + if (!serializedObjects.Add(obj)) + return "Cycle, object was already serialized." + Environment.NewLine; - var identation = new string('\t', nestingLevel + 1); + return obj switch + { + IList collection => SerializeCollection(collection, nestingLevel), + IDictionary dictionary => SerializeDictionary(dictionary, nestingLevel), + _ => SerializeObject(obj, nestingLevel) + }; + } + + private string SerializeCollection(IList collection, int nestingLevel) + { + var sb = new StringBuilder(collection.GetType().Name + " {" + Environment.NewLine); + foreach (var item in collection) + { + sb.Append(new string('\t', nestingLevel + 1)); + sb.Append(PrintToString(item, nestingLevel + 1)); + } + sb.Append(new string('\t', nestingLevel) + '}' + Environment.NewLine); + return sb.ToString(); + } + + private string SerializeDictionary(IDictionary dictionary, int nestingLevel) + { + var sb = new StringBuilder(dictionary.GetType().Name + " {" + Environment.NewLine); + foreach (var key in dictionary.Keys) + { + sb.Append(new string('\t', nestingLevel + 1)); + sb.Append(PrintToString(key, nestingLevel + 1).Trim() + " = "); + sb.Append(PrintToString(dictionary[key], nestingLevel+ 1)); + } + sb.Append(new string('\t', nestingLevel) + '}' + Environment.NewLine); + + return sb.ToString(); + } + + private string SerializeObject(object obj, int nestingLevel) + { + var indent = new string('\t', nestingLevel + 1); var sb = new StringBuilder(); var type = obj.GetType(); sb.AppendLine(type.Name); - foreach (var propertyInfo in type.GetProperties()) + foreach (var memberInfo in type.GetMembers(BindingFlags.Public | BindingFlags.Instance) + .Where(t => t.MemberType == MemberTypes.Field || t.MemberType == MemberTypes.Property) + .Where(x => !IsMemberExcluded(x))) { - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); + sb.Append( + indent + memberInfo.Name + " = " + + SerializeMember(obj, memberInfo, nestingLevel + 1) + ); } + return sb.ToString(); } + + private string SerializeMember(object obj, MemberInfo memberInfo, int nestingLevel) + { + return TryGetCustomSerializer(memberInfo, out var serializer) + ? serializer.Invoke(memberInfo.GetValue(obj)) + Environment.NewLine + : PrintToString(memberInfo.GetValue(obj), nestingLevel); + } + + private bool TryGetCustomSerializer(MemberInfo memberInfo, out Func serializer) + { + return memberSerializers.TryGetValue(memberInfo, out serializer) || + memberSerializers.TryGetValue(memberInfo.GetMemberType(), out serializer); + } + + private bool IsMemberExcluded(MemberInfo memberInfo) + { + return excludedMembers.Contains(memberInfo) || + excludedTypes.Contains(memberInfo.GetMemberType()); + } } -} \ No newline at end of file +} diff --git a/ObjectPrinting/PrintingPropertyConfig.cs b/ObjectPrinting/PrintingPropertyConfig.cs new file mode 100644 index 00000000..32914a68 --- /dev/null +++ b/ObjectPrinting/PrintingPropertyConfig.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; + +namespace ObjectPrinting +{ + public class PrintingPropertyConfig + { + private readonly MemberInfo memberInfo; + private readonly PrintingConfig printingConfig; + private readonly Dictionary> serializers; + + public PrintingPropertyConfig(PrintingConfig printingConfig, + Dictionary> serializers, + MemberInfo memberInfo) + { + this.printingConfig = printingConfig; + this.serializers = serializers; + this.memberInfo = memberInfo; + } + + public PrintingConfig To(Func serializeRule) + { + serializers[memberInfo] = x => serializeRule((TProperty)x); + return printingConfig; + } + } +} diff --git a/ObjectPrinting/Solved/ObjectExtensions.cs b/ObjectPrinting/Solved/ObjectExtensions.cs deleted file mode 100644 index b0c94553..00000000 --- a/ObjectPrinting/Solved/ObjectExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ObjectPrinting.Solved -{ - 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/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/PropertyPrintingConfig.cs b/ObjectPrinting/Solved/PropertyPrintingConfig.cs deleted file mode 100644 index a509697d..00000000 --- a/ObjectPrinting/Solved/PropertyPrintingConfig.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Globalization; - -namespace ObjectPrinting.Solved -{ - public class PropertyPrintingConfig : IPropertyPrintingConfig - { - private readonly PrintingConfig printingConfig; - - public PropertyPrintingConfig(PrintingConfig printingConfig) - { - this.printingConfig = printingConfig; - } - - public PrintingConfig Using(Func print) - { - return printingConfig; - } - - public PrintingConfig Using(CultureInfo culture) - { - return printingConfig; - } - - PrintingConfig IPropertyPrintingConfig.ParentConfig => printingConfig; - } - - public interface IPropertyPrintingConfig - { - PrintingConfig ParentConfig { get; } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs deleted file mode 100644 index dd392239..00000000 --- a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace ObjectPrinting.Solved -{ - public static class PropertyPrintingConfigExtensions - { - 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) - { - return ((IPropertyPrintingConfig)propConfig).ParentConfig; - } - - } -} \ 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/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs deleted file mode 100644 index f9555955..00000000 --- a/ObjectPrinting/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Tests -{ - public class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs similarity index 51% rename from ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs rename to ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs index ac52d5ee..191cdc31 100644 --- a/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs @@ -1,8 +1,9 @@ -using System; -using System.Globalization; +using System.Globalization; using NUnit.Framework; +using ObjectPrinting; +using ObjectPrinting.Extensions; -namespace ObjectPrinting.Solved.Tests +namespace ObjectPrintingTests { [TestFixture] public class ObjectPrinterAcceptanceTests @@ -10,31 +11,38 @@ public class ObjectPrinterAcceptanceTests [Test] public void Demo() { - var person = new Person { Name = "Alex", Age = 19 }; + var person = new Person + { + Name = "Alex", Age = 19, Surname = "Vasilyev", Weight = 70.3, Height = 170.9, + Friends = new List{new() {Surname = "sd"}, null} + }; var printer = ObjectPrinter.For() //1. Исключить из сериализации свойства определенного типа - .Excluding() + .ExcludeProperty() //2. Указать альтернативный способ сериализации для определенного типа - .Printing().Using(i => i.ToString("X")) + .ChangeSerializationFor() + .To(x => "hello") //3. Для числовых типов указать культуру - .Printing().Using(CultureInfo.InvariantCulture) + .ChangeSerializationFor() + .To(CultureInfo.CurrentCulture) //4. Настроить сериализацию конкретного свойства + .ChangeSerializationFor(x => x.Name) + .To(x => "Another") //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - .Printing(p => p.Name).TrimmedToLength(10) + .ChangeSerializationFor(x => x.Surname) + .ToTrimmedLength(3) //6. Исключить из сериализации конкретного свойства - .Excluding(p => p.Age); - - string s1 = printer.PrintToString(person); + .ExcludeProperty(x => x.Height); + var serializedObject = printer.PrintToString(person); + Console.WriteLine(serializedObject); //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - string s2 = person.PrintToString(); - + serializedObject = person.PrintToString(); + Console.WriteLine(serializedObject); //8. ...с конфигурированием - string s3 = person.PrintToString(s => s.Excluding(p => p.Age)); - Console.WriteLine(s1); - Console.WriteLine(s2); - Console.WriteLine(s3); + serializedObject = person.PrintToString(s => s.ExcludeProperty(x => x.Name)); + Console.WriteLine(serializedObject); } } } \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrintingTests.csproj b/ObjectPrintingTests/ObjectPrintingTests.csproj new file mode 100644 index 00000000..42a5621f --- /dev/null +++ b/ObjectPrintingTests/ObjectPrintingTests.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + false + true + ObjectProntingTests + + + + + + + + + + + + + + + + diff --git a/ObjectPrintingTests/ObjectPrinting_Should.cs b/ObjectPrintingTests/ObjectPrinting_Should.cs new file mode 100644 index 00000000..d9e01acc --- /dev/null +++ b/ObjectPrintingTests/ObjectPrinting_Should.cs @@ -0,0 +1,163 @@ +using System.Globalization; +using FluentAssertions; +using NUnit.Framework; +using ObjectPrinting; +using ObjectPrinting.Extensions; + + +namespace ObjectPrintingTests +{ + public class ObjectPrinting_Should + { + private Person person; + [SetUp] + public void CreateDefaultPerson() => + person = new Person { Name = "Alex", Age = 19, Surname = "Vasilyev", Weight = 70.3 }; + + [Test] + public void ExcludeByType() + { + const string notExcepted = nameof(Person.Name) + " = "; + var result = ObjectPrinter.For() + .ExcludeProperty() + .PrintToString(person); + + result.Should().NotContain(notExcepted); + } + + [Test] + public void ExcludeProperty() + { + var notExcepted = nameof(Person.Id) + " = " + person.Id + Environment.NewLine; + var result = ObjectPrinter.For() + .ExcludeProperty(x => x.Id) + .PrintToString(person); + + result.Should().NotContain(notExcepted); + } + + [Test] + public void ProvideCustomSerialization_ForType() + { + var heightExcepted = nameof(Person.Height) + " = " + person.Height.ToString("f0") + Environment.NewLine; + var weightExcepted = nameof(Person.Weight) + " = " + person.Weight.ToString("f0") + Environment.NewLine; + var result = ObjectPrinter.For() + .ChangeSerializationFor() + .To(x => x.ToString("f0")) + .PrintToString(person); + + result.Should().Contain(heightExcepted).And.Contain(weightExcepted); + } + + [Test] + public void ProvideCustomSerialization_ForProperty() + { + var excepted = nameof(Person.Weight) + " = " + person.Weight.ToString("f0") + Environment.NewLine; + var result = ObjectPrinter.For() + .ChangeSerializationFor(t => t.Weight) + .To(x => x.ToString("f0")) + .PrintToString(person); + + result.Should().Contain(excepted); + } + + [Test] + public void ProvideCulture_ForNumberTypes() + { + var excepted = nameof(Person.Weight) + " = " + + person.Weight.ToString(CultureInfo.CurrentCulture) + Environment.NewLine; + var result = ObjectPrinter.For() + .ChangeSerializationFor() + .To(CultureInfo.CurrentCulture) + .PrintToString(person); + + result.Should().Contain(excepted); + } + + [Test] + public void ProvideTrimForStrings() + { + const int length = 5; + var excepted = nameof(Person.Surname) + " = " + person.Surname[..length] + Environment.NewLine; + var result = ObjectPrinter.For() + .ChangeSerializationFor() + .ToTrimmedLength(length) + .PrintToString(person); + + result.Should().Contain(excepted); + } + + + [Test] + public void Work_WhenReferenceCycles() + { + const string excepted = "Cycle, object was already serialized"; + person.Parents = [person]; + person.Friends = [person]; + var result = ObjectPrinter.For().PrintToString(person); + + result.Should().Contain(excepted); + } + + [Test] + public void Print_WhenArray() + { + const string excepted = "Person\r\n\tParents = Person[] {\r\n\t\tPerson" + + "\r\n\t\t\tParents = null\r\n\t\tPerson\r\n\t\t\tParents = null\r\n\t}\r\n"; + person.Parents = [new Person(), new Person()]; + var result = ObjectPrinter.For() + .ExcludeProperty(t => t.SomeDictionary) + .ExcludeProperty(t => t.Friends) + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .PrintToString(person); + + result.Should().Be(excepted); + } + + [Test] + public void Print_WhenList() + { + const string excepted = "Person\r\n\tFriends = List`1 {\r\n\t\tPerson\r\n\t\t\tFriends = null" + + "\r\n\t\tPerson\r\n\t\t\tFriends = null\r\n\t}\r\n"; + person.Friends = [new Person(), new Person()]; + + var result = ObjectPrinter.For() + .ExcludeProperty(t => t.SomeDictionary) + .ExcludeProperty(t => t.Parents) + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .PrintToString(person); + + result.Should().Be(excepted); + } + + [Test] + public void Print_WhenDictionaries() + { + const string expected = "Person\r\n\tSomeDictionary = Dictionary`2 " + + "{\r\n\t\t1 = aboba\r\n\t\t2 = biba\r\n\t}\r\n"; + person.SomeDictionary = new Dictionary + { + { 1, "aboba" }, + { 2, "biba" } + }; + var result = ObjectPrinter.For() + .ExcludeProperty(t => t.Parents) + .ExcludeProperty(t => t.Friends) + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .PrintToString(person); + result.Should().Be(expected); + } + } +} diff --git a/ObjectPrintingTests/Person.cs b/ObjectPrintingTests/Person.cs new file mode 100644 index 00000000..b04119eb --- /dev/null +++ b/ObjectPrintingTests/Person.cs @@ -0,0 +1,15 @@ +namespace ObjectPrintingTests +{ + public class Person + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Surname { get; set; } + public double Height { get; set; } + public int Age { get; set; } + public double Weight { get; set; } + public List Friends { get; set; } + public Person[] Parents { get; set; } + public Dictionary SomeDictionary { get; set; } + } +} diff --git a/fluent-api.sln b/fluent-api.sln index 69c8db9e..2dda777a 100644 --- a/fluent-api.sln +++ b/fluent-api.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping.Tests", "Samp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrintingTests", "ObjectPrintingTests\ObjectPrintingTests.csproj", "{9F4DB591-55F0-401C-96FB-33B98AD69120}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,6 +37,10 @@ Global {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.Build.0 = Release|Any CPU + {9F4DB591-55F0-401C-96FB-33B98AD69120}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F4DB591-55F0-401C-96FB-33B98AD69120}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F4DB591-55F0-401C-96FB-33B98AD69120}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F4DB591-55F0-401C-96FB-33B98AD69120}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 3f8540d4495b57f34464c327c619a452b1a8dd2c Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Tue, 26 Dec 2023 20:43:09 +0500 Subject: [PATCH 2/4] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D1=87=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D0=BA=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/ObjectPrinter.cs | 2 +- ObjectPrinting/PrintingPropertyConfig.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index 3c7867c3..9a19ba1d 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -1,6 +1,6 @@ namespace ObjectPrinting { - public class ObjectPrinter + public static class ObjectPrinter { public static PrintingConfig For() { diff --git a/ObjectPrinting/PrintingPropertyConfig.cs b/ObjectPrinting/PrintingPropertyConfig.cs index 32914a68..4912e39b 100644 --- a/ObjectPrinting/PrintingPropertyConfig.cs +++ b/ObjectPrinting/PrintingPropertyConfig.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Reflection; namespace ObjectPrinting From 7b4cf4b93ffbf91f97d2813e3ffb5b5cefbf70c6 Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Sat, 30 Dec 2023 11:53:02 +0500 Subject: [PATCH 3/4] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D1=87=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D0=BA=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/PrintingConfig.cs | 82 ++++---------------- ObjectPrinting/SerializerHandler.cs | 61 +++++++++++++++ ObjectPrintingTests/ObjectPrinting_Should.cs | 28 +++++-- 3 files changed, 97 insertions(+), 74 deletions(-) create mode 100644 ObjectPrinting/SerializerHandler.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 701381c0..b9496d84 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,23 +1,14 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Text; using ObjectPrinting.Extensions; namespace ObjectPrinting { public class PrintingConfig { - private readonly HashSet finalTypes = new HashSet - { - typeof(int), typeof(double), typeof(float), - typeof(bool), typeof(long), typeof(string), - typeof(DateTime), typeof(TimeSpan), typeof(Guid) - }; - private readonly HashSet excludedMembers = new HashSet(); private readonly HashSet excludedTypes = new HashSet(); private readonly HashSet serializedObjects = new HashSet(); @@ -53,10 +44,10 @@ private string PrintToString(object obj, int nestingLevel) { if (obj == null) return "null" + Environment.NewLine; - - if (finalTypes.Contains(obj.GetType())) + var objType = obj.GetType(); + if (objType.IsPrimitive || objType.IsValueType || objType == typeof(string)) { - if (memberSerializers.TryGetValue(obj.GetType(), out var serializer)) + if (memberSerializers.TryGetValue(objType, out var serializer)) return serializer(obj) + Environment.NewLine; return obj + Environment.NewLine; } @@ -66,62 +57,18 @@ private string PrintToString(object obj, int nestingLevel) return obj switch { - IList collection => SerializeCollection(collection, nestingLevel), - IDictionary dictionary => SerializeDictionary(dictionary, nestingLevel), - _ => SerializeObject(obj, nestingLevel) + IDictionary dictionary => + SerializerHandler.SerializeDictionary(dictionary, nestingLevel, PrintToString), + IEnumerable enumerable => + SerializerHandler.SerializeIEnumerable(enumerable, nestingLevel, PrintToString), + _ => SerializerHandler.SerializeObject(obj, nestingLevel, SerializeMember, IsMemberExcluded) }; } - - private string SerializeCollection(IList collection, int nestingLevel) - { - var sb = new StringBuilder(collection.GetType().Name + " {" + Environment.NewLine); - foreach (var item in collection) - { - sb.Append(new string('\t', nestingLevel + 1)); - sb.Append(PrintToString(item, nestingLevel + 1)); - } - sb.Append(new string('\t', nestingLevel) + '}' + Environment.NewLine); - return sb.ToString(); - } - - private string SerializeDictionary(IDictionary dictionary, int nestingLevel) - { - var sb = new StringBuilder(dictionary.GetType().Name + " {" + Environment.NewLine); - foreach (var key in dictionary.Keys) - { - sb.Append(new string('\t', nestingLevel + 1)); - sb.Append(PrintToString(key, nestingLevel + 1).Trim() + " = "); - sb.Append(PrintToString(dictionary[key], nestingLevel+ 1)); - } - sb.Append(new string('\t', nestingLevel) + '}' + Environment.NewLine); - - return sb.ToString(); - } - - private string SerializeObject(object obj, int nestingLevel) - { - var indent = new string('\t', nestingLevel + 1); - var sb = new StringBuilder(); - var type = obj.GetType(); - sb.AppendLine(type.Name); - foreach (var memberInfo in type.GetMembers(BindingFlags.Public | BindingFlags.Instance) - .Where(t => t.MemberType == MemberTypes.Field || t.MemberType == MemberTypes.Property) - .Where(x => !IsMemberExcluded(x))) - { - sb.Append( - indent + memberInfo.Name + " = " + - SerializeMember(obj, memberInfo, nestingLevel + 1) - ); - } - - return sb.ToString(); - } - private string SerializeMember(object obj, MemberInfo memberInfo, int nestingLevel) + private bool IsMemberExcluded(MemberInfo memberInfo) { - return TryGetCustomSerializer(memberInfo, out var serializer) - ? serializer.Invoke(memberInfo.GetValue(obj)) + Environment.NewLine - : PrintToString(memberInfo.GetValue(obj), nestingLevel); + return excludedMembers.Contains(memberInfo) || + excludedTypes.Contains(memberInfo.GetMemberType()); } private bool TryGetCustomSerializer(MemberInfo memberInfo, out Func serializer) @@ -130,10 +77,11 @@ private bool TryGetCustomSerializer(MemberInfo memberInfo, out Func printToString) + { + var sb = new StringBuilder(collection.GetType().Name + " {" + Environment.NewLine); + foreach (var item in collection) + { + sb.Append(new string('\t', nestingLevel + 1)); + sb.Append(printToString(item, nestingLevel + 1)); + } + + sb.Append(new string('\t', nestingLevel) + '}' + Environment.NewLine); + return sb.ToString(); + } + + public static string SerializeDictionary(IDictionary dictionary, int nestingLevel, + Func printToString) + { + var sb = new StringBuilder(dictionary.GetType().Name + " {" + Environment.NewLine); + foreach (var key in dictionary.Keys) + { + sb.Append(new string('\t', nestingLevel + 1)); + sb.Append(printToString(key, nestingLevel + 1).Trim() + " = "); + sb.Append(printToString(dictionary[key], nestingLevel + 1)); + } + + sb.Append(new string('\t', nestingLevel) + '}' + Environment.NewLine); + + return sb.ToString(); + } + + public static string SerializeObject(object obj, int nestingLevel, + Func serializeMember, Func isMemberExcluded) + { + var indent = new string('\t', nestingLevel + 1); + var sb = new StringBuilder(); + var type = obj.GetType(); + sb.AppendLine(type.Name); + foreach (var memberInfo in type.GetMembers(BindingFlags.Public | BindingFlags.Instance) + .Where(t => t.MemberType == MemberTypes.Field || t.MemberType == MemberTypes.Property) + .Where(x => !isMemberExcluded(x))) + { + sb.Append( + indent + memberInfo.Name + " = " + + serializeMember(obj, memberInfo, nestingLevel + 1) + ); + } + + return sb.ToString(); + } + } +} diff --git a/ObjectPrintingTests/ObjectPrinting_Should.cs b/ObjectPrintingTests/ObjectPrinting_Should.cs index d9e01acc..5a0901cb 100644 --- a/ObjectPrintingTests/ObjectPrinting_Should.cs +++ b/ObjectPrintingTests/ObjectPrinting_Should.cs @@ -102,8 +102,13 @@ public void Work_WhenReferenceCycles() [Test] public void Print_WhenArray() { - const string excepted = "Person\r\n\tParents = Person[] {\r\n\t\tPerson" + - "\r\n\t\t\tParents = null\r\n\t\tPerson\r\n\t\t\tParents = null\r\n\t}\r\n"; + string excepted = "Person" + Environment.NewLine + + "\tParents = Person[] {" + Environment.NewLine + + "\t\tPerson" + Environment.NewLine + + "\t\t\tParents = null" + Environment.NewLine + + "\t\tPerson" + Environment.NewLine + + "\t\t\tParents = null" + Environment.NewLine+ + "\t}" + Environment.NewLine; person.Parents = [new Person(), new Person()]; var result = ObjectPrinter.For() .ExcludeProperty(t => t.SomeDictionary) @@ -121,9 +126,14 @@ public void Print_WhenArray() [Test] public void Print_WhenList() { - const string excepted = "Person\r\n\tFriends = List`1 {\r\n\t\tPerson\r\n\t\t\tFriends = null" + - "\r\n\t\tPerson\r\n\t\t\tFriends = null\r\n\t}\r\n"; - person.Friends = [new Person(), new Person()]; + string excepted = "Person" + Environment.NewLine + + "\tFriends = List`1 {" + Environment.NewLine + + "\t\tPerson" + Environment.NewLine + + "\t\t\tFriends = null" + Environment.NewLine + + "\t\tPerson" + Environment.NewLine + + "\t\t\tFriends = null" + Environment.NewLine + + "\t}" + Environment.NewLine; + person.Friends = [new Person(), new Person()]; var result = ObjectPrinter.For() .ExcludeProperty(t => t.SomeDictionary) @@ -141,13 +151,17 @@ public void Print_WhenList() [Test] public void Print_WhenDictionaries() { - const string expected = "Person\r\n\tSomeDictionary = Dictionary`2 " + - "{\r\n\t\t1 = aboba\r\n\t\t2 = biba\r\n\t}\r\n"; + string expected = "Person" + Environment.NewLine + + "\tSomeDictionary = Dictionary`2 {" + Environment.NewLine + + "\t\t1 = aboba" + Environment.NewLine + + "\t\t2 = biba" + Environment.NewLine + + "\t}" + Environment.NewLine; person.SomeDictionary = new Dictionary { { 1, "aboba" }, { 2, "biba" } }; + var result = ObjectPrinter.For() .ExcludeProperty(t => t.Parents) .ExcludeProperty(t => t.Friends) From 89e938b8c53bd97d0225312657816a32d7953362 Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Sat, 30 Dec 2023 12:48:47 +0500 Subject: [PATCH 4/4] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB,=20=D1=87=D1=82=D0=BE=20=D0=BF=D0=BE=D0=B4=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D0=B5=D1=80=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B2=D0=BD=D1=83=D1=82=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D0=B8=D1=85=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=BC=D0=B5=D0=BD=D1=8F=D0=BB=D0=B0=20=D0=B2?= =?UTF-8?q?=D0=BD=D0=B5=D1=88=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/PrintingConfig.cs | 31 ++++++----- ObjectPrinting/PrintingPropertyConfig.cs | 10 ++-- ObjectPrinting/SerializerHandler.cs | 4 +- ObjectPrintingTests/ObjectPrinting_Should.cs | 56 +++++++++++++++++--- ObjectPrintingTests/Person.cs | 1 + 5 files changed, 76 insertions(+), 26 deletions(-) diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index b9496d84..91f126cf 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; using ObjectPrinting.Extensions; @@ -9,12 +10,12 @@ namespace ObjectPrinting { public class PrintingConfig { - private readonly HashSet excludedMembers = new HashSet(); + private readonly HashSet<(MemberInfo, int)> excludedMembers = new HashSet<(MemberInfo, int)>(); private readonly HashSet excludedTypes = new HashSet(); private readonly HashSet serializedObjects = new HashSet(); - private readonly Dictionary> memberSerializers = - new Dictionary>(); + private readonly Dictionary<(MemberInfo, int), Func> memberSerializers = + new Dictionary<(MemberInfo, int), Func>(); public string PrintToString(TOwner obj) => PrintToString(obj, 0); @@ -26,7 +27,9 @@ public PrintingConfig ExcludeProperty() public PrintingConfig ExcludeProperty(Expression> memberSelector) { - excludedMembers.Add(((MemberExpression)memberSelector.Body).Member); + var body = (MemberExpression)memberSelector.Body; + var nestingLevel = body.Expression.ToString().Count(x => x == '.') + 1; + excludedMembers.Add((body.Member, nestingLevel)); return this; } @@ -37,7 +40,8 @@ public PrintingPropertyConfig ChangeSerializationFor> memberSelector) { var body = (MemberExpression)memberSelector.Body; - return new PrintingPropertyConfig(this, memberSerializers, body.Member); + var nestingLevel = body.Expression.ToString().Count(x => x == '.') + 1; + return new PrintingPropertyConfig(this, memberSerializers, body.Member, nestingLevel); } private string PrintToString(object obj, int nestingLevel) @@ -47,7 +51,8 @@ private string PrintToString(object obj, int nestingLevel) var objType = obj.GetType(); if (objType.IsPrimitive || objType.IsValueType || objType == typeof(string)) { - if (memberSerializers.TryGetValue(objType, out var serializer)) + if (memberSerializers.TryGetValue((objType, nestingLevel), out var serializer) || + memberSerializers.TryGetValue((objType, -1), out serializer) ) return serializer(obj) + Environment.NewLine; return obj + Environment.NewLine; } @@ -65,21 +70,23 @@ private string PrintToString(object obj, int nestingLevel) }; } - private bool IsMemberExcluded(MemberInfo memberInfo) + private bool IsMemberExcluded(MemberInfo memberInfo, int nestingLevel) { - return excludedMembers.Contains(memberInfo) || + return excludedMembers.Contains((memberInfo, nestingLevel)) || excludedTypes.Contains(memberInfo.GetMemberType()); } - private bool TryGetCustomSerializer(MemberInfo memberInfo, out Func serializer) + private bool TryGetCustomSerializer(MemberInfo memberInfo, int nestingLevel, out Func + serializer) { - return memberSerializers.TryGetValue(memberInfo, out serializer) || - memberSerializers.TryGetValue(memberInfo.GetMemberType(), out serializer); + return memberSerializers.TryGetValue((memberInfo, nestingLevel), out serializer) || + memberSerializers.TryGetValue((memberInfo.GetMemberType(), nestingLevel), out serializer)|| + memberSerializers.TryGetValue((memberInfo, -1), out serializer); } private string SerializeMember(object obj, MemberInfo memberInfo, int nestingLevel) { - return TryGetCustomSerializer(memberInfo, out var serializer) + return TryGetCustomSerializer(memberInfo, nestingLevel, out var serializer) ? serializer.Invoke(memberInfo.GetValue(obj)) + Environment.NewLine : PrintToString(memberInfo.GetValue(obj), nestingLevel); } diff --git a/ObjectPrinting/PrintingPropertyConfig.cs b/ObjectPrinting/PrintingPropertyConfig.cs index 4912e39b..58aea434 100644 --- a/ObjectPrinting/PrintingPropertyConfig.cs +++ b/ObjectPrinting/PrintingPropertyConfig.cs @@ -7,21 +7,23 @@ namespace ObjectPrinting public class PrintingPropertyConfig { private readonly MemberInfo memberInfo; + private int nestingLevel; private readonly PrintingConfig printingConfig; - private readonly Dictionary> serializers; + private readonly Dictionary<(MemberInfo, int), Func> serializers; public PrintingPropertyConfig(PrintingConfig printingConfig, - Dictionary> serializers, - MemberInfo memberInfo) + Dictionary<(MemberInfo, int), Func> serializers, + MemberInfo memberInfo, int nestingLevel = -1) { this.printingConfig = printingConfig; this.serializers = serializers; this.memberInfo = memberInfo; + this.nestingLevel = nestingLevel; } public PrintingConfig To(Func serializeRule) { - serializers[memberInfo] = x => serializeRule((TProperty)x); + serializers[(memberInfo, nestingLevel)] = x => serializeRule((TProperty)x); return printingConfig; } } diff --git a/ObjectPrinting/SerializerHandler.cs b/ObjectPrinting/SerializerHandler.cs index 9c2cc446..e685a107 100644 --- a/ObjectPrinting/SerializerHandler.cs +++ b/ObjectPrinting/SerializerHandler.cs @@ -39,7 +39,7 @@ public static string SerializeDictionary(IDictionary dictionary, int nestingLeve } public static string SerializeObject(object obj, int nestingLevel, - Func serializeMember, Func isMemberExcluded) + Func serializeMember, Func isMemberExcluded) { var indent = new string('\t', nestingLevel + 1); var sb = new StringBuilder(); @@ -47,7 +47,7 @@ public static string SerializeObject(object obj, int nestingLevel, sb.AppendLine(type.Name); foreach (var memberInfo in type.GetMembers(BindingFlags.Public | BindingFlags.Instance) .Where(t => t.MemberType == MemberTypes.Field || t.MemberType == MemberTypes.Property) - .Where(x => !isMemberExcluded(x))) + .Where(x => !isMemberExcluded(x, nestingLevel + 1))) { sb.Append( indent + memberInfo.Name + " = " + diff --git a/ObjectPrintingTests/ObjectPrinting_Should.cs b/ObjectPrintingTests/ObjectPrinting_Should.cs index 5a0901cb..0d81c279 100644 --- a/ObjectPrintingTests/ObjectPrinting_Should.cs +++ b/ObjectPrintingTests/ObjectPrinting_Should.cs @@ -10,6 +10,7 @@ namespace ObjectPrintingTests public class ObjectPrinting_Should { private Person person; + [SetUp] public void CreateDefaultPerson() => person = new Person { Name = "Alex", Age = 19, Surname = "Vasilyev", Weight = 70.3 }; @@ -102,13 +103,19 @@ public void Work_WhenReferenceCycles() [Test] public void Print_WhenArray() { - string excepted = "Person" + Environment.NewLine + - "\tParents = Person[] {" + Environment.NewLine + - "\t\tPerson" + Environment.NewLine + - "\t\t\tParents = null" + Environment.NewLine + - "\t\tPerson" + Environment.NewLine + - "\t\t\tParents = null" + Environment.NewLine+ - "\t}" + Environment.NewLine; + var excepted = "Person" + Environment.NewLine + + "\tParents = Person[] {" + Environment.NewLine + + "\t\tPerson" + Environment.NewLine + + "\t\t\tFriends = null" + Environment.NewLine + + "\t\t\tParents = null" + Environment.NewLine + + "\t\t\tSomeDictionary = null" + Environment.NewLine + + "\t\t\tChild = null" + Environment.NewLine + + "\t\tPerson" + Environment.NewLine + + "\t\t\tFriends = null" + Environment.NewLine + + "\t\t\tParents = null" + Environment.NewLine + + "\t\t\tSomeDictionary = null" + Environment.NewLine + + "\t\t\tChild = null" + Environment.NewLine + + "\t}" + Environment.NewLine; person.Parents = [new Person(), new Person()]; var result = ObjectPrinter.For() .ExcludeProperty(t => t.SomeDictionary) @@ -118,6 +125,7 @@ public void Print_WhenArray() .ExcludeProperty() .ExcludeProperty() .ExcludeProperty() + .ExcludeProperty(x => x.Child) .PrintToString(person); result.Should().Be(excepted); @@ -130,8 +138,14 @@ public void Print_WhenList() "\tFriends = List`1 {" + Environment.NewLine + "\t\tPerson" + Environment.NewLine + "\t\t\tFriends = null" + Environment.NewLine + + "\t\t\tParents = null" + Environment.NewLine + + "\t\t\tSomeDictionary = null" + Environment.NewLine + + "\t\t\tChild = null" + Environment.NewLine + "\t\tPerson" + Environment.NewLine + "\t\t\tFriends = null" + Environment.NewLine + + "\t\t\tParents = null" + Environment.NewLine + + "\t\t\tSomeDictionary = null" + Environment.NewLine + + "\t\t\tChild = null" + Environment.NewLine + "\t}" + Environment.NewLine; person.Friends = [new Person(), new Person()]; @@ -143,8 +157,9 @@ public void Print_WhenList() .ExcludeProperty() .ExcludeProperty() .ExcludeProperty() + .ExcludeProperty(x => x.Child) .PrintToString(person); - + result.Should().Be(excepted); } @@ -170,8 +185,33 @@ public void Print_WhenDictionaries() .ExcludeProperty() .ExcludeProperty() .ExcludeProperty() + .ExcludeProperty(x => x.Child) .PrintToString(person); + result.Should().Be(expected); } + + [Test] + public void SerializationForChildDoesNotAffectParent() + { + person.Child = new Person(); + var result = ObjectPrinter.For() + .ChangeSerializationFor(e => e.Child.Age) + .To(s => "ВОЗРАСТ") + .PrintToString(person); + Console.WriteLine(result); + result.Should().Contain("ВОЗРАСТ", LessThan.Twice()); + } + + [Test] + public void ExcludingForChildDoesNotAffectParent() + { + person.Child = new Person(); + var result = ObjectPrinter.For() + .ExcludeProperty(x => x.Child.Age) + .PrintToString(person); + Console.WriteLine(result); + result.Should().Contain("Age", Exactly.Once()); + } } } diff --git a/ObjectPrintingTests/Person.cs b/ObjectPrintingTests/Person.cs index b04119eb..5137dca9 100644 --- a/ObjectPrintingTests/Person.cs +++ b/ObjectPrintingTests/Person.cs @@ -11,5 +11,6 @@ public class Person public List Friends { get; set; } public Person[] Parents { get; set; } public Dictionary SomeDictionary { get; set; } + public Person Child; } }