当前位置:网站首页>0x05 - 综合示例,导出 CSV
0x05 - 综合示例,导出 CSV
2020-12-14 23:27:40 【Newbe36524】
现在,我们来完成一个稍微复杂一点的场景用例。
将实体导出为 CSV 文件
为了使下文的示例更加符合生产实际,我们在这里引入一个具体的场景。
我们需要将实体导出为 CSV 文件。
CSV 文件一般包含两个部分。
第一个部分是文件的头部。头部中包含了每一列的列名。
第二个部分是内容部分。内容部分每行都是一条记录。
不论是头部还是内容部分每个属性之间都使用逗号进行分隔。
这里我们使用前文使用到的 OrderInfo
进行演示:
public class OrderInfo
{
public int OrderId { get; set; }
public string Buyer { get; set; }
public decimal TotalPrice { get; set; }
} |
则导出的 CSV 样例如下:
OrderId,Buyer,TotalPrice
1,yueluo,99999
2,newbe36524,36524
3,traceless,123456 |
分析实现思路
这个业务场景的实现方式可以非常多样化,此处我们简要将逻辑分为以下部分:
- 使用 object visitor 访问
OrderInfo
的所有属性 - 将所有属性传递给 CSV Writer 进行输出
实现 CSV 写入器
首先,我们先添加第 2 步所需要的 CSV 写入器:
public interface ICsvWriter
{
ICsvWriter WriteHeader(string header);
ICsvWriter FinishHead();
ICsvWriter WriteCell(string cell);
ICsvWriter FinishRow();
}
public class CsvWriter : ICsvWriter
{
public string Separator { get; set; } = ",";
private readonly TextWriter _writer;
public CsvWriter(
TextWriter writer)
{
_writer = writer;
}
private bool _firstHead = true;
public ICsvWriter WriteHeader(string header)
{
if (_firstHead)
{
_firstHead = false;
}
else
{
_writer.Write(Separator);
}
_writer.Write(header);
return this;
}
public ICsvWriter FinishHead()
{
_writer.WriteLine();
return this;
}
private bool _firstCell = true;
public ICsvWriter WriteCell(string cell)
{
if (_firstCell)
{
_firstCell = false;
}
else
{
_writer.Write(Separator);
}
_writer.Write(cell);
return this;
}
public ICsvWriter FinishRow()
{
_firstCell = true;
_writer.WriteLine();
return this;
}
} |
有了这样一个基础的 CsvWriter
, 我们便可以首先来生成一个样例中的表格:
var sb = new StringBuilder();
var csvWriter = new CsvWriter(new StringWriter(sb));
csvWriter.WriteHeader("OrderId")
.WriteHeader("Buyer")
.WriteHeader("TotalPrice")
.FinishHead()
.WriteCell("1")
.WriteCell("yueluo")
.WriteCell("99999")
.FinishRow()
.WriteCell("2")
.WriteCell("newbe36524")
.WriteCell("36524")
.FinishRow()
.WriteCell("3")
.WriteCell("traceless")
.WriteCell("123456")
.FinishRow();
Console.WriteLine(sb.ToString()); |
输出表头
现在,我们使用第一个 object visitor 来调用上文的 CsvWriter 来输出表头:
var sb = new StringBuilder();
var csvWriter = new CsvWriter(new StringWriter(sb));
default(OrderInfo)
.V()
.WithExtendObject<OrderInfo, CsvWriter>()
.ForEach((name, value, w) => w.WriteHeader(name))
.Run(new OrderInfo(), csvWriter);
csvWriter.FinishHead();
Console.WriteLine(sb.ToString()); |
这样我就会得到如下的结果:
OrderId,Buyer,TotalPrice |
输出表行
现在,我们在增加一个 object visitor 来输出表的每行内容:
var rowWriter = default(OrderInfo)
.V()
.WithExtendObject<OrderInfo, CsvWriter>()
.ForEach((name, value, w) => w.WriteCell(value != null ? value.ToString() : ""))
.Cache();
var orders = new List<OrderInfo>
{
new OrderInfo
{
OrderId = 1,
Buyer = "yueluo",
TotalPrice = 99999M
},
new OrderInfo
{
OrderId = 2,
Buyer = "newbe36524",
TotalPrice = 36524M
},
new OrderInfo
{
OrderId = 3,
Buyer = "traceless",
TotalPrice = 123456M
}
};
foreach (var order in orders)
{
rowWriter.Run(order, csvWriter);
csvWriter.FinishRow();
}
Console.WriteLine(sb.ToString()); |
这样就会得到如下的内容:
1,yueluo,99999
2,newbe36524,36524
3,traceless,123456 |
这正是我们期望的表中的行数据。
创建 CsvExtensions
现在,我们将以上的表头和表行的相关逻辑进行整合,将他们全部都添加到一个 CsvExtensions 的类型中。并且增加对于 IEnumerable<T>
的扩展方法,这样在进行调用时就会更加简单。这将仿照先前 FormatToStringExtensions
中的做法。
public static class CsvExtensions
{
public static string ToCsv<T>(this IEnumerable<T> items)
where T : new()
{
var re = CsvFilerHelper<T>.Instance.ToCsv(items);
return re;
}
private static class CsvFilerHelper<T>
where T : new()
{
internal static readonly ICsvHelper Instance = new CsvHelper();
public interface ICsvHelper
{
string ToCsv(IEnumerable<T> items);
}
private class CsvHelper : ICsvHelper
{
private readonly ICachedObjectVisitor<T, CsvWriter> _headerWriter;
private readonly ICachedObjectVisitor<T, CsvWriter> _bodyWriter;
public CsvHelper()
{
_headerWriter = default(T)
.V()
.WithExtendObject<T, CsvWriter>()
.ForEach((name, value, w) => w.WriteHeader(name))
.Cache();
_bodyWriter = default(T)
.V()
.WithExtendObject<T, CsvWriter>()
.ForEach((name, value, w) => w.WriteCell(value != null ? value.ToString() : ""))
.Cache();
}
public string ToCsv(IEnumerable<T> items)
{
var sb = new StringBuilder();
var csvWriter = new CsvWriter(new StringWriter(sb));
_headerWriter.Run(new T(), csvWriter);
csvWriter.FinishHead();
foreach (var item in items)
{
_bodyWriter.Run(item, csvWriter);
csvWriter.FinishRow();
}
var re = sb.ToString();
return re;
}
}
}
} |
与先前的 FormatToStringExtensions
一样,此处采用的是泛型静态类配合扩展方法的形式来创建帮助方法。不同的是,在这个示例中存在两个 object visitor。因此多考虑抽象了一个 ICsvHelper
和实现类来实现复杂逻辑的聚合。
输出时跳过特定的列
我们希望在输出 CSV 的时候跳过一些特定的列,这就需要对属性进行过滤。
我们增加一个新的 Attribute:
public class IgnoreAttribute : Attribute
{
} |
然后将这个 Attribute 标记在不希望输出的属性上:
public class OrderInfo
{
public int OrderId { get; set; }
[Ignore]
public string Buyer { get; set; }
public decimal TotalPrice { get; set; }
} |
然后,我们只要在 object visitor 中忽略这些被标记为 Ingore 的属性即可。
其中核心的修改如下:
static bool Filter(PropertyInfo p) => p.GetCustomAttribute<IgnoreAttribute>() == null;
_headerWriter = default(T)
.V()
.WithExtendObject<T, CsvWriter>()
.FilterProperty(Filter)
.ForEach((name, value, w) => w.WriteHeader(name))
.Cache();
_bodyWriter = default(T)
.V()
.WithExtendObject<T, CsvWriter>()
.FilterProperty(Filter)
.ForEach((name, value, w) => w.WriteCell(value != null ? value.ToString() : ""))
.Cache(); |
这样我们在输出 CSV 时也就不存在这一列了:
OrderId,TotalPrice
1,99999
2,36524
3,123456 |
总结
我们通过一个简单的生产实例来理解 object visitor 的用法。实际生产问题会比这个更加复杂。开发者可以在生产实际中进行尝试,强化理解。
发布说明
- Newbe.ObjectVisitor 0.4.4 发布,模型验证器上线
- Newbe.ObjectVisitor 0.3.7 发布,自动生成 FluentAPI
- Newbe.ObjectVisitor 0.2.10 发布,更花里胡哨
- Newbe.ObjectVisitor 0.1.4 发布,初始版本
使用样例
- Newbe.ObjectVisitor 样例 1
- 0x01 - 我的第一个 Object Visitor
- 0x02 - 创建并缓存 Object Visitor
- 0x03-ForEach 全面观
- 0x04 - 过滤属性
- 0x05 - 综合示例,导出 CSV
开发文档可能随版本发生变化,查看最新的开发文档需移步 http://cn.ov.newbe.pro
番外分享
GitHub 项目地址:https://github.com/newbe36524/Newbe.ObjectVisitor
Gitee 项目地址:https://gitee.com/yks/Newbe.ObjectVisitor
- 本文作者: newbe36524
- 本文链接: https://www.newbe.pro/Newbe.ObjectVisitor/005-csv-helper/
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
版权声明
本文为[Newbe36524]所创,转载请带上原文链接,感谢
https://my.oschina.net/newbe36524/blog/4807179
边栏推荐
- OPTIMIZER_TRACE详解
- 使用Consul实现服务发现:instance-id自定义
- OPTIMIZER_ Trace details
- Using consult to realize service discovery: instance ID customization
- Summary of common string algorithms
- Summary of common algorithms of linked list
- Linked blocking Queue Analysis of blocking queue
- 构建者模式(Builder pattern)
- Builder pattern
- Newbe.ObjectVisitor 样例 1
猜你喜欢
-
Newbe.ObjectVisitor Example 1
-
Farewell to runaway
-
LeetCode Algorithm 0060 - Permutation Sequence (Medium)
-
编程基础 - 栈的应用 - 混洗(Stack Shuffling)
-
LeetCode Algorithm 0060 - Permutation Sequence (Medium)
-
Fundamentals of programming stack shuffling
-
【色卡】常用色谱简析,中国传统颜色卡,代码附RBG,HC
-
[color card] brief analysis of commonly used chromatograms, Chinese traditional color cards, code with RBG, HC
-
MongoDB 副本集之入门篇
-
Introduction to mongodb replica set
随机推荐
- My name is mongodb, don't understand me. After reading my story, you will get started!
- roboguide破解安装教程
- Roboguide cracking installation tutorial
- The transformation of town street intelligent street lamp under the industrial intelligent gateway
- Remote smoke monitoring of environmental protection data acquisition instrument under Internet of things
- JS实现鼠标移入DIV随机变换颜色
- Flutter 页面中的异常处理ErrorWidget
- Exception handling errorwidget in fluent page
- Bolt's practice of route management of flutter (page decoupling, process control, function expansion, etc.)
- C语言系统化精讲 重塑你的编程思想 打造坚实的开发基础
- Skywalking系列博客6-手把手教你编写Skywalking插件
- Skywalking series blog 7 - dynamic configuration
- Skywalking series blog 6 - help you write skywalking plug-in
- 博客主机_自动申请续期免费证书
- Blog host_ Automatic renewal of free certificate
- 0x05 - synthesis example, export to CSV
- 0x02 - create and cache object visitors
- flutter圆形或线型进度条
- flutter给滚动内容添加粘性header组件
- Fluent round or linear progress bar
- Fluent adds sticky header components to scrolling content
- Typora uses latex to insert mathematical formulas
- 配电自动化终端dtu
- How to write a thesis opening report
- 基于C的PHP快速IP解析扩展,IP检测
- Based on C PHP fast IP resolution extension, IP detection
- 点击平滑滚动效果
- Click smooth scrolling effect
- HighGo Database触发器使用案例(APP)
- Use case of highgo database trigger (APP)
- ES6之Map对象
- Flutter 最常出现的错误
- Flutter's most common mistakes
- 捕获 flutter app的崩溃日志并上报
- Capture and report the crash log of the flutter app
- SQL Server递归查询在Highgo DB中实现 (APP)
- Implementation of SQL Server recursive query in highgo dB (APP)
- 关于browserslist配置项
- About browserlist configuration items
- FTK1000使用视频一招搞定多模光损耗