Skip to content

Instantly share code, notes, and snippets.

@wipiano
Last active February 15, 2018 10:22
Show Gist options
  • Save wipiano/787b600ab8af86951fb9b5e5236f982b to your computer and use it in GitHub Desktop.
Save wipiano/787b600ab8af86951fb9b5e5236f982b to your computer and use it in GitHub Desktop.
convert object to csv
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ObjectToCsv
{
internal class Program
{
public static void Main(string[] args)
{
var rows = new[]
{
new SampleObj1()
{
Name = "taro",
Age = 12,
Memo = "memomoe",
Score = 55.232,
},
new SampleObj1()
{
Name = "hanako",
Age = 24,
Memo = "hanakomemo",
Score = 80,
},
};
foreach (var row in CsvFormatter<SampleObj1>.ToCsv(rows))
{
Console.WriteLine(row);
}
}
}
public sealed class SampleObj1
{
public string Name { get; set; }
[Title("age")]
public int Age { get; set; }
[Ignore]
public string Memo { get; set; }
[Title("score")]
[Format("##.00")]
public double Score { get; set; }
}
public class CsvFormatter<T>
{
private static readonly CsvFormatter<T> Instance = new CsvFormatter<T>();
private readonly IReadOnlyList<CsvField> _fields;
private readonly string _title;
private CsvFormatter()
{
_fields = CsvField.Create<T>();
_title = string.Join(",", _fields.Where(f => !f.Ignored).Select(f => f.GetCsvTitle()));
}
public static string GetTitle() => Instance._title;
public static string ToCsvRow(T obj) => string.Join(",", Instance._fields.Where(f => !f.Ignored).Select(f => f.GetCsvString(obj)));
public static IEnumerable<string> ToCsv(IEnumerable<T> objs)
{
yield return GetTitle();
foreach (var obj in objs)
{
yield return ToCsvRow(obj);
}
}
}
internal sealed class CsvField
{
public string Name { get; }
public string Format { get; }
public bool Ignored { get; }
public PropertyInfo PropInfo { get; }
private static readonly Dictionary<string, string> s_escapeExpressions = new Dictionary<string, string>()
{
{"\\", "\\\\"},
{"\"", "\\\""}
};
public CsvField(string name, string format, bool ignored, PropertyInfo propertyInfo)
{
Name = name;
Format = format;
Ignored = ignored;
PropInfo = propertyInfo;
}
public string GetCsvString(object instance)
{
string ToCsvString(string s)
{
foreach (var e in s_escapeExpressions)
{
s = s.Replace(e.Key, e.Value);
}
return $@"""{s}""" ?? string.Empty;
}
object value = PropInfo.GetValue(instance);
if (value == null)
{
return string.Empty;
}
return ToCsvString(string.IsNullOrEmpty(Format) ? value.ToString() : string.Format($"{{0:{Format}}}", PropInfo.GetValue(instance)));
}
public string GetCsvTitle() => Name;
public static CsvField[] Create<T>()
{
return typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public)
.Select(info =>
{
string title = (Attribute.GetCustomAttribute(info, typeof(TitleAttribute)) as TitleAttribute)?.Title ?? info.Name;
string format = (Attribute.GetCustomAttribute(info, typeof(FormatAttribute)) as FormatAttribute)?.Format;
bool ignore = (Attribute.GetCustomAttribute(info, typeof(IgnoreAttribute)) as IgnoreAttribute) != null;
return new CsvField(title, format, ignore, info);
})
.ToArray();
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class TitleAttribute : Attribute
{
public string Title { get; }
public TitleAttribute(string title)
{
Title = title;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class FormatAttribute : Attribute
{
public string Format { get; }
public FormatAttribute(string format)
{
Format = format;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class IgnoreAttribute : Attribute
{
public IgnoreAttribute()
{
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment