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/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/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a9e08211..91f126cf 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,41 +1,94 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; -using System.Text; +using System.Linq.Expressions; +using System.Reflection; +using ObjectPrinting.Extensions; namespace ObjectPrinting { public class PrintingConfig { - public string PrintToString(TOwner obj) + 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<(MemberInfo, int), Func> memberSerializers = + new Dictionary<(MemberInfo, int), Func>(); + + public string PrintToString(TOwner obj) => PrintToString(obj, 0); + + public PrintingConfig ExcludeProperty() { - return PrintToString(obj, 0); + excludedTypes.Add(typeof(TProperty)); + return this; + } + + public PrintingConfig ExcludeProperty(Expression> memberSelector) + { + var body = (MemberExpression)memberSelector.Body; + var nestingLevel = body.Expression.ToString().Count(x => x == '.') + 1; + excludedMembers.Add((body.Member, nestingLevel)); + return this; + } + + public PrintingPropertyConfig ChangeSerializationFor() => + new PrintingPropertyConfig(this, memberSerializers, typeof(TProperty)); + + public PrintingPropertyConfig ChangeSerializationFor( + Expression> memberSelector) + { + var body = (MemberExpression)memberSelector.Body; + var nestingLevel = body.Expression.ToString().Count(x => x == '.') + 1; + return new PrintingPropertyConfig(this, memberSerializers, body.Member, nestingLevel); } private string PrintToString(object obj, int nestingLevel) { - //TODO apply configurations if (obj == null) return "null" + Environment.NewLine; - - var finalTypes = new[] + var objType = obj.GetType(); + if (objType.IsPrimitive || objType.IsValueType || objType == typeof(string)) { - typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) - }; - if (finalTypes.Contains(obj.GetType())) + if (memberSerializers.TryGetValue((objType, nestingLevel), out var serializer) || + memberSerializers.TryGetValue((objType, -1), out serializer) ) + return serializer(obj) + Environment.NewLine; return obj + Environment.NewLine; + } - var identation = new string('\t', nestingLevel + 1); - var sb = new StringBuilder(); - var type = obj.GetType(); - sb.AppendLine(type.Name); - foreach (var propertyInfo in type.GetProperties()) + if (!serializedObjects.Add(obj)) + return "Cycle, object was already serialized." + Environment.NewLine; + + return obj switch { - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); - } - return sb.ToString(); + IDictionary dictionary => + SerializerHandler.SerializeDictionary(dictionary, nestingLevel, PrintToString), + IEnumerable enumerable => + SerializerHandler.SerializeIEnumerable(enumerable, nestingLevel, PrintToString), + _ => SerializerHandler.SerializeObject(obj, nestingLevel, SerializeMember, IsMemberExcluded) + }; + } + + private bool IsMemberExcluded(MemberInfo memberInfo, int nestingLevel) + { + return excludedMembers.Contains((memberInfo, nestingLevel)) || + excludedTypes.Contains(memberInfo.GetMemberType()); + } + + private bool TryGetCustomSerializer(MemberInfo memberInfo, int nestingLevel, out Func + 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, nestingLevel, out var serializer) + ? serializer.Invoke(memberInfo.GetValue(obj)) + Environment.NewLine + : PrintToString(memberInfo.GetValue(obj), nestingLevel); } } -} \ No newline at end of file +} diff --git a/ObjectPrinting/PrintingPropertyConfig.cs b/ObjectPrinting/PrintingPropertyConfig.cs new file mode 100644 index 00000000..58aea434 --- /dev/null +++ b/ObjectPrinting/PrintingPropertyConfig.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace ObjectPrinting +{ + public class PrintingPropertyConfig + { + private readonly MemberInfo memberInfo; + private int nestingLevel; + private readonly PrintingConfig printingConfig; + private readonly Dictionary<(MemberInfo, int), Func> serializers; + + public PrintingPropertyConfig(PrintingConfig printingConfig, + 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, nestingLevel)] = x => serializeRule((TProperty)x); + return printingConfig; + } + } +} diff --git a/ObjectPrinting/SerializerHandler.cs b/ObjectPrinting/SerializerHandler.cs new file mode 100644 index 00000000..e685a107 --- /dev/null +++ b/ObjectPrinting/SerializerHandler.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace ObjectPrinting +{ + public static class SerializerHandler + { + public static string SerializeIEnumerable(IEnumerable collection, int nestingLevel, + 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, nestingLevel + 1))) + { + sb.Append( + indent + memberInfo.Name + " = " + + serializeMember(obj, memberInfo, nestingLevel + 1) + ); + } + + return sb.ToString(); + } + } +} 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..0d81c279 --- /dev/null +++ b/ObjectPrintingTests/ObjectPrinting_Should.cs @@ -0,0 +1,217 @@ +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() + { + 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) + .ExcludeProperty(t => t.Friends) + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty(x => x.Child) + .PrintToString(person); + + result.Should().Be(excepted); + } + + [Test] + public void Print_WhenList() + { + string excepted = "Person" + Environment.NewLine + + "\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()]; + + var result = ObjectPrinter.For() + .ExcludeProperty(t => t.SomeDictionary) + .ExcludeProperty(t => t.Parents) + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty() + .ExcludeProperty(x => x.Child) + .PrintToString(person); + + result.Should().Be(excepted); + } + + [Test] + public void Print_WhenDictionaries() + { + 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) + .ExcludeProperty() + .ExcludeProperty() + .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 new file mode 100644 index 00000000..5137dca9 --- /dev/null +++ b/ObjectPrintingTests/Person.cs @@ -0,0 +1,16 @@ +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; } + public Person Child; + } +} 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