Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions ObjectPrinting/ObjectExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace ObjectPrinting;

public static class ObjectExtensions
{
public static string PrintToString<T>(this T obj)
{
return ObjectPrinter.For<T>().PrintToString(obj);
}
}
1 change: 1 addition & 0 deletions ObjectPrinting/ObjectPrinting.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
Expand Down
175 changes: 163 additions & 12 deletions ObjectPrinting/PrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
using System;
using System.Collections;
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<TOwner>
{
private readonly HashSet<Type> excludedTypes = new();
private readonly HashSet<PropertyInfo> excludedFields = new();
private readonly HashSet<object> proccesedObjects = new();
private readonly Dictionary<Type, Func<object, string>> alternativeSerializeTypes = new();
private readonly Dictionary<PropertyInfo, Func<object, string>> alternativeSerializeProperties = new();
private readonly Dictionary<Type, CultureInfo> alternativeCulture = new();
private readonly Dictionary<PropertyInfo, int> lengthOfProperties = new();

private readonly Type[] finalTypes =
{
typeof(int), typeof(double), typeof(float), typeof(string),
typeof(DateTime), typeof(TimeSpan), typeof(Guid)
};

public string PrintToString(TOwner obj)
{
return PrintToString(obj, 0);
Expand All @@ -14,28 +33,160 @@ public string PrintToString(TOwner obj)
private string PrintToString(object obj, int nestingLevel)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Что-то не так с алгоритмом: у нас есть метод CheckSerializationConditions и логика в нём частично повторяется тут. Мб как-то можно более проще сделать?

{
//TODO apply configurations
var indentation = new string('\t', nestingLevel + 1);

var specialSerialization = TryGetSerializationString(obj, nestingLevel);
if (specialSerialization != null)
return specialSerialization;

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(property + substring + Environment.NewLine);
continue;
}

string serializedObj;
if (alternativeSerializeProperties.TryGetValue(propertyInfo, out var propertyFunc))
serializedObj = property + propertyFunc(propertyInfo.GetValue(obj)) + Environment.NewLine;
else
serializedObj = property + PrintToString(propertyInfo.GetValue(obj), nestingLevel + 1);

sb.Append(serializedObj);
}

return sb.ToString();
}


private string TryGetSerializationString(object? obj, int nestingLevel)
{
if (obj == null)
return "null" + Environment.NewLine;
if (excludedTypes.Contains(obj.GetType()))
return string.Empty;
if (proccesedObjects.Contains(obj))
return "Cyclic reference" + Environment.NewLine;
if (alternativeSerializeTypes.TryGetValue(obj.GetType(), out var serializeFunc))
return serializeFunc(obj) + Environment.NewLine;

var finalTypes = new[]
{
typeof(int), typeof(double), typeof(float), typeof(string),
typeof(DateTime), typeof(TimeSpan)
};
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;
}

var identation = new string('\t', nestingLevel + 1);
private string PrintCollection(IEnumerable collection, int nestingLevel)
{
var sb = new StringBuilder();
var type = obj.GetType();
sb.AppendLine(type.Name);
foreach (var propertyInfo in type.GetProperties())
var indentation = new string('\t', nestingLevel + 1);

sb.Append(indentation + collection.GetType().Name + "{" + Environment.NewLine);
int index = 0;
foreach (var item in collection)
{
sb.Append(identation + propertyInfo.Name + " = " +
PrintToString(propertyInfo.GetValue(obj),
nestingLevel + 1));
sb.Append(indentation + "\t[" + index + "] = ");
sb.Append(GetStringWithNestingLevel(item, nestingLevel + 2).Trim());
sb.Append(Environment.NewLine);
index++;
}

sb.Append(indentation + "}");
return sb.ToString();
}

private string PrintDictionary(IDictionary dictionary, int nestingLevel)
{
var sb = new StringBuilder();
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);
var value = GetStringWithNestingLevel(element.Value, nestingLevel + 2).Trim();
sb.Append(key + " : " + value + Environment.NewLine);
}

sb.Append(indentation + "}");
return sb.ToString();
}

private string GetStringWithNestingLevel(object obj, int nestingLevel)
{
var indentation = new string('\t', nestingLevel + 1);
if (finalTypes.Contains(obj.GetType()))
return indentation + obj;
var result = PrintToString(obj, nestingLevel);
return Environment.NewLine + indentation + result;
}

public PrintingConfig<TOwner> Exclude<T>()
{
excludedTypes.Add(typeof(T));
return this;
}

public PrintingConfig<TOwner> Exclude<T>(Expression<Func<TOwner, T>> func)
{
var propertyInfo = GetPropertyInfo(func);
excludedFields.Add(propertyInfo);
return this;
}

public PropertyPrintingConfig<TOwner, TPrintType> Printing<TPrintType>()
{
return new PropertyPrintingConfig<TOwner, TPrintType>(this, null);
}

public PropertyPrintingConfig<TOwner, TPropType> Printing<TPropType>(
Expression<Func<TOwner, TPropType>> func)
{
var propertyInfo = GetPropertyInfo(func);
return new PropertyPrintingConfig<TOwner, TPropType>(this, propertyInfo);
}

private PropertyInfo GetPropertyInfo<T>(Expression<Func<TOwner, T>> 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;
}

internal void AddSerializedProperty<T>(PropertyInfo propertyInfo, Func<T, string> serializeFunc)
{
alternativeSerializeProperties[propertyInfo] = o => serializeFunc((T) o);
}

internal void AddLengthOfProperty(PropertyInfo propertyInfo, int length)
{
lengthOfProperties[propertyInfo] = length;
}

internal void AddAlternativeCulture(Type type, CultureInfo cultureInfo)
{
alternativeCulture[type] = cultureInfo;
}

internal void AddSerializedType<T>(Func<T, string> serializeFunc)
{
alternativeSerializeTypes[typeof(T)] = o => serializeFunc((T) o);
}
}
}
41 changes: 41 additions & 0 deletions ObjectPrinting/PropertyPrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Globalization;
using System.Reflection;

namespace ObjectPrinting;

public class PropertyPrintingConfig<TOwner, TPropType>
{
internal PrintingConfig<TOwner> PrintingConfig { get; }
internal readonly PropertyInfo? PropertyInfo;


public PropertyPrintingConfig(PrintingConfig<TOwner> printingConfig, PropertyInfo propertyInfo)
{
PrintingConfig = printingConfig;
PropertyInfo = propertyInfo;
}

public PrintingConfig<TOwner> Using(Func<TPropType, string> newSerialize)
{
return PropertyInfo == null ? SetSerializedType(newSerialize) : SetSerializedProp(newSerialize);
}

public PrintingConfig<TOwner> Using(CultureInfo cultureInfo)
{
PrintingConfig.AddAlternativeCulture(typeof(TPropType), cultureInfo);
return PrintingConfig;
}

private PrintingConfig<TOwner> SetSerializedProp(Func<TPropType, string> newSerialize)
{
PrintingConfig.AddSerializedProperty(PropertyInfo!, newSerialize);
return PrintingConfig;
}

private PrintingConfig<TOwner> SetSerializedType(Func<TPropType, string> newSerialize)
{
PrintingConfig.AddSerializedType(newSerialize);
return PrintingConfig;
}
}
15 changes: 15 additions & 0 deletions ObjectPrinting/PropertyPrintingConfigExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;


namespace ObjectPrinting;

public static class PropertyPrintingConfigExtensions
{
public static PrintingConfig<TOwner> TrimmedToLength<TOwner>(
this PropertyPrintingConfig<TOwner, string> propertyConfig,
int maxLen)
{
propertyConfig.PrintingConfig.AddLengthOfProperty(propertyConfig.PropertyInfo, maxLen);
return propertyConfig.PrintingConfig;
}
}
27 changes: 0 additions & 27 deletions ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs

This file was deleted.

12 changes: 0 additions & 12 deletions ObjectPrinting/Tests/Person.cs

This file was deleted.

Loading