Cinchoo ETL -XML Writer
适用于.NET的简单XML写入器
目录
- 引言
- 要求
- “Hello World!”示例
- 写入所有记录
- 手动写入记录
- 自定义XML记录
- 自定义XML字段
- 回调机制
- 自定义
- 使用动态对象
- 异常
- 使用 MetadataType 注解
- 配置选项
- ToText 辅助方法
- Writing DataReader 辅助方法
- Writing DataTable 辅助方法
- 高级主题 (Advanced Topics)
- 历史
1. 引言
ChoETL是一个用于.NET的开源ETL(提取、转换和加载)框架。它是一个基于代码的库,用于从多个源提取数据,进行转换,并将其加载到您在.NET环境中的数据仓库中。您可以立即将数据存储在您的数据仓库中。
本文讨论了使用ChoETL
框架提供的XmlWriter
组件。它是一个简单的实用程序类,用于将XML数据保存到文件中。
更新:相应的XmlReader
文章可以在此处找到。
特点
- 遵循XML标准文件规则。优雅地处理包含逗号和换行符的数据字段。
- 除了逗号,还可以使用大多数分隔字符,包括制表符分隔的字段。
- 生成文件时支持特定文化的日期、货币和数字格式。
- 支持不同的字符编码。
- 在写入文件时提供对日期、货币、枚举、布尔值、数字格式的精细控制。
- 详细而健壮的错误处理,让您能够快速查找和修复问题。
- 缩短您的开发时间。
2. 要求
此框架库是用 C# 编写的,使用 .NET 4.5 Framework。
3. “Hello World!” 示例
- 打开 VS.NET 2013 或更高版本
- 创建一个示例 VS.NET (.NET Framework 4.5) 控制台应用程序项目
- 使用Nuget命令通过Package Manager Console安装ChoETL:
Install-Package ChoETL
- 使用
ChoETL
命名空间
让我们从一个简单的例子开始,生成下面包含两个节点的XML文件。
列表3.1 示例XML数据文件
<Employees>
<Employee>
<Id>1</Id>
<Name>Tom</Name>
</Employee>
<Employee>
<Id>1</Id>
<Name>Mark</Name>
</Employee>
</Employees>
有多种方法可以在最小设置下创建XML文件。
3.1. 快速写入 - 数据优先方法
这是零配置且最快的方法,可以立即创建XML文件。无需类型化的POCO对象。下面的示例代码展示了如何使用动态对象生成示例XML文件。
列表3.1.1 将对象列表写入XML文件
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoXmlWriter("Emp.xml").WithXPath("Employees/Employee"))
{
parser.Write(objs);
}
在上面的示例中,我们将对象列表一次性传递给XmlWriter
,将它们写入XML文件。
列表3.1.2 将每个对象写入XML文件
using (var parser = new ChoXmlWriter("Emp.xml").WithXPath("Employees/Employee"))
{
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
parser.Write(item);
dynamic rec1 = new ExpandoObject();
rec1.Id = 2;
rec1.Name = "Jason";
parser.Write(item);
}
在上面的示例中,我们控制构造,并将每个单独的记录传递给XmlWriter
,使用Write
重载生成XML文件。
3.2. 代码优先方法
这是另一种零配置方法,可以使用类型化的POCO类生成XML文件。首先,定义一个简单的POCO类,使其与底层XML文件布局匹配。
图 3.2.1 简单的 POCO 实体类
public partial class EmployeeRecSimple
{
public int Id { get; set; }
public string Name { get; set; }
}
在上面,POCO类定义了两个与示例XML文件模板匹配的属性。
列表3.2.2 保存到XML文件
List<EmployeeRecSimple> objs = new List<EmployeeRecSimple>();
EmployeeRecSimple rec1 = new EmployeeRecSimple();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
EmployeeRecSimple rec2 = new EmployeeRecSimple();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoXmlWriter<EmployeeRecSimple>
("Emp.xml").WithXPath("Employees/Employee"))
{
parser.Write(objs);
}
上面的示例展示了如何从类型化的POCO类对象创建XML文件。
3.3. 配置优先方法
在此模型中,我们定义了XML配置,其中包含所有必要的参数以及生成示例XML文件所需的XML列。
列表3.3.1 定义XML配置
ChoXmlRecordConfiguration config = new ChoXmlRecordConfiguration();
config.XmlRecordFieldConfigurations.Add(new ChoXmlRecordFieldConfiguration("Id"));
config.XmlRecordFieldConfigurations.Add(new ChoXmlRecordFieldConfiguration("Name"));
在上面,该类定义了两个与示例XML文件模板匹配的XML属性。
列表3.3.2 不使用POCO对象生成XML文件
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 2;
rec2.Name = "Tom";
objs.Add(rec2);
using (var parser = new ChoXmlWriter("Emp.xml", config).WithXPath("Employees/Employee"))
{
parser.Write(objs);
}
上面的示例代码展示了如何使用预定义的XML配置设置从动态对象列表生成XML文件。在XmlWriter
构造函数中,我们指定了XML配置对象,以便在创建文件时遵循XML布局模式。如果XML列的名称或数量存在任何不匹配,将报告错误并停止写入过程。
列表3.3.3 使用POCO对象保存XML文件
List<EmployeeRecSimple> objs = new List<EmployeeRecSimple>();
EmployeeRecSimple rec1 = new EmployeeRecSimple();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
EmployeeRecSimple rec2 = new EmployeeRecSimple();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoXmlWriter<EmployeeRecSimple>
("Emp.xml", config).WithXPath("Employees/Employee"))
{
parser.Write(objs);
}
上面的示例代码展示了如何使用XML配置对象从POCO对象列表生成XML文件。在XmlWriter
构造函数中,我们指定了XML配置对象。
这是一种组合方法,用于定义POCO实体类并声明性地附加XML配置参数。id
是必需列,name
是可选值列,默认值为"XXXX
"。如果name
不存在,它将采用默认值。
清单 3.4.1 定义 POCO 对象
public class EmployeeRec
{
[ChoXmlElementRecordField]
[Required]
public int? Id
{
get;
set;
}
[ChoXmlElementRecordField]
[DefaultValue("XXXX")]
public string Name
{
get;
set;
}
}
上面的代码说明了如何使用生成XML文件所需的属性定义POCO对象。首先,为每个记录字段定义属性,并使用ChoXmlElementRecordFieldAttribute
使其符合XML记录映射。Id
是必需属性。我们使用RequiredAttribute
对其进行修饰。Name
使用DefaultValueAttribute
给定默认值。这意味着如果对象中未设置Name
值,XmlWriter
会将默认值“XXXX”输出到文件中。
要将属性定义为XML属性,请使用ChoXmlAttributeRecordFieldAttribute
。
它非常简单,可以立即保存XML数据。
列表3.4.2 使用POCO对象保存XML文件
List<EmployeeRec> objs = new List<EmployeeRec>();
EmployeeRec rec1 = new EmployeeRec();
rec1.Id = 10;
rec1.Name = "Mark";
objs.Add(rec1);
EmployeeRec rec2 = new EmployeeRec();
rec2.Id = 200;
rec2.Name = "Lou";
objs.Add(rec2);
using (var parser = new ChoXmlWriter<EmployeeRec>
("Emp.xml").WithXPath("Employees/Employee"))
{
parser.Write(objs);
}
我们首先创建一个ChoXmlWriter
对象的新实例。就是这样。所有从对象生成XML数据的繁重工作都由写入器在后台完成。
默认情况下,XmlWriter
在保存XML文件时会发现并使用默认配置参数。这些参数可以根据您的需求进行覆盖。以下部分将为您提供每个配置属性的深入详细信息。
4. 写入所有记录
这就像设置POCO对象以匹配XML文件结构,构造对象列表并将其传递给XmlWriter
的Write
方法一样简单。这将通过一次调用将整个对象列表写入XML文件。
列表4.1 写入XML文件
List<EmployeeRec> objs = new List<EmployeeRec>();
//Construct and attach objects to this list
...
using (var parser = new ChoXmlWriter<EmployeeRec>
("Emp.xml").WithXPath("Employees/Employee"))
{
parser.Write(objs);
}
或
列表4.2 写入XML文件流
List<EmployeeRec> objs = new List<EmployeeRec>();
//Construct and attach objects to this list
...
using (var tx = File.OpenWrite("Emp.xml"))
{
using (var parser =
new ChoXmlWriter<EmployeeRec>(tx).WithXPath("Employees/Employee"))
{
parser.Write(objs);
}
}
这种模型使您的代码优雅、整洁、易于阅读和维护。
5. 手动写入记录
这是一种替代方法,用于在POCO对象以非连接方式构造的情况下将每个单独的记录写入XML文件。
列表5.1 写入XML文件
var writer = new ChoXmlWriter<EmployeeRec>("Emp.xml");
writer.WithXPath("Employees/Employee");
EmployeeRec rec1 = new EmployeeRec();
rec1.Id = 10;
rec1.Name = "Mark";
writer.Write(rec1);
EmployeeRec rec2 = new EmployeeRec();
rec1.Id = 11;
rec1.Name = "Top";
writer.Write(rec2);
6. 自定义XML记录
使用ChoXmlRecordObjectAttribute
,您可以声明性地自定义POCO实体对象。
列表6.1 为每个记录自定义POCO对象
[ChoXmlRecordObject(Encoding = "Encoding.UTF32",
ErrorMode = ChoErrorMode.IgnoreAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All)]
public class EmployeeRec
{
[ChoXmlElementRecordField]
public int Id { get; set; }
[ChoXmlElementRecordField]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
以下是用于对文件上的XML加载操作进行自定义的可用属性。
XPath
- 可选。用于生成根元素和子元素的XPath表达式。通常,表达式的格式为“RootName/ElementName
”。例如,“Employees/Employee
”。如果未指定,“Root
”将用作根元素,“XElement
”将用于每个对象。EOLDelimiter
- 不适用Culture
- 用于读取和写入的文化信息。IgnoreEmptyLine
- 不适用Comments
- 不适用QuoteChar
- 不适用QuoteAllFields
- 不适用Encoding
- XML文件的编码。ColumnCountStrict
- 此标志指示如果XML字段配置与数据对象成员不匹配,是否应抛出异常。ColumnOrderStrict
- 不适用BufferSize
- 当读取器从StreamWriter
读取时使用的内部缓冲区的大小。ErrorMode
- 此标志指示在写入过程中是否应抛出异常,并且预期的字段未能写入。可以按属性覆盖。可能的值是IgnoreAndContinue
- 忽略错误,跳过记录并继续处理下一条。ReportAndContinue
- 如果 POCO 实体是IChoNotifyRecordWrite
类型,则向其报告错误ThrowAndStop
- 抛出错误并停止执行
IgnoreFieldValueMode
- 不适用ObjectValidationMode
- 一个标志,告知读取器关于记录对象要执行的验证类型。可能的值是Off
- 不执行对象验证。(默认)MemberLevel
- 在每个XML属性写入文件之前执行的验证。ObjectLevel
- 在所有POCO属性写入文件之前执行的验证。
7. 自定义XML字段
对于每个XML列,您可以使用ChoXmlElementRecordFieldAttribute/ChoXmlAttributeRecordFieldAttribute
在POCO实体属性中指定映射。
列表7.1 为XML列自定义POCO对象
[ChoXmlFileHeader]
public class EmployeeRec
{
[ChoXmlElementRecordField]
public int Id { get; set; }
[ChoXmlElementRecordField]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
以下是您可以为每个属性添加自定义的可用成员
FieldName
- XML元素/属性名称。如果未指定,将使用对象属性名称。FillChar
- 当列值大小小于列大小时使用的填充字符。默认为“ ”,不进行填充。FieldValueJustification
- 列值对齐方式。默认为左对齐。FieldValueTrimOption
- 不适用。Truncate
- 此标志告诉写入器如果XML列值超过列大小,则截断该值。默认为false
。Size
- XML列值的大小。QuoteField
- 不适用ErrorMode
- 此标志指示在写入过程中是否应抛出异常,并且预期的字段未能转换和写入。可能的值是IgnoreAndContinue
- 忽略错误,继续加载记录的其他属性。ReportAndContinue
- 如果 POCO 实体是IChoRecord
类型,则向其报告错误。ThrowAndStop
- 抛出错误并停止执行。
IgnoreFieldValueMode
- 不适用
7.1. 默认值
任何POCO实体属性都可以使用System.ComponentModel.DefaultValueAttribute
指定默认值。当XML值为null
时(通过IgnoreFieldValueMode
控制)用于写入的值。
7.2. 后备值
任何POCO实体属性都可以使用ChoETL.ChoFallbackValueAttribute
指定备用值。当XML值无法转换为文本时,该值用于设置属性。Fallback
值仅在ErrorMode
为IgnoreAndContinue
或ReportAndContinue
时设置。
7.3. 类型转换器
大多数基本类型会自动转换为字符串/文本并保存到XML文件。如果XML字段的值未自动转换为文本值,您可以指定自定义/内置.NET转换器将值转换为文本。这些可以是IValueConverter
或TypeConverter
转换器。
用于将/格式化属性值转换为文本的方法是IValueConverter.ConvertBack()
或TypeConvert.ConvertTo()
。
列表7.3.1 指定类型转换器
[ChoXmlFileHeader]
public class EmployeeRec
{
[ChoXmlElementRecordField]
[ChoTypeConverter(typeof(IntConverter))]
public int Id { get; set; }
[ChoXmlElementRecordField]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
列表7.3.2 IntConverter实现
public class IntConverter : IValueConverter
{
public object Convert
(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack
(object value, Type targetType, object parameter, CultureInfo culture)
{
int intValue = (int)value;
return intValue.ToString("D4");
}
}
在上面的示例中,我们定义了自定义IntConverter
类。并展示了如何使用前导零格式化“Id
”XML属性。
7.4. 验证
XmlWriter
利用System.ComponentModel.DataAnnotations
和Validation Block
验证属性来为POCO实体的各个字段指定验证规则。有关可用DataAnnotations
验证属性的列表,请参阅MSDN网站。
图 7.4.1 在 POCO 实体中使用验证属性
[ChoXmlFileHeader]
[ChoXmlRecordObject(Encoding = "Encoding.UTF32",
ErrorMode = ChoErrorMode.IgnoreAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All,
ThrowAndStopOnMissingField = false)]
public partial class EmployeeRec
{
[ChoXmlElementRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoXmlElementRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
}
在上面的示例中,将Range
验证属性用于Id
属性。将Required验证属性用于Name
属性。当Configuration.ObjectValidationMode
设置为ChoObjectValidationMode.MemberLevel
或ChoObjectValidationMode.ObjectLevel
时,XmlWriter
在将数据保存到文件之前对它们执行验证。
在某些情况下,您可能希望获得控制权并在 POCO 实体类中执行手动**自验证**。这可以通过继承 POCO 对象并实现 IChoValidatable
接口来实现。
图 7.4.2 在 POCO 实体上手动验证
[ChoXmlFileHeader]
[ChoXmlRecordObject(Encoding = "Encoding.UTF32",
ErrorMode = ChoErrorMode.IgnoreAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All,
ThrowAndStopOnMissingField = false)]
public partial class EmployeeRec : IChoValidatable
{
[ChoXmlElementRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoXmlElementRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool TryValidate
(object target, ICollection<ValidationResult> validationResults)
{
return true;
}
public bool TryValidateFor
(object target, string memberName,
ICollection<ValidationResult> validationResults)
{
return true;
}
public void Validate(object target)
{
}
public void ValidateFor(object target, string memberName)
{
}
}
上面的示例展示了如何在 POCO 对象中实现自定义自验证。
IChoValidatable
接口公开以下方法
TryValidate
- 验证整个对象,如果所有验证都通过,则返回true
。否则,返回false
。Validate
- 验证整个对象,如果验证未通过,则抛出异常。TryValidateFor
- 验证对象的特定属性,如果所有验证都通过,则返回true
。否则返回false
。ValidateFor
- 验证对象的特定属性,如果验证未通过则抛出异常。
8. 回调机制
XmlWriter
开箱即用,提供行业标准的XML数据文件生成功能,可满足大多数需求。如果生成过程无法满足您的任何需求,您可以使用XmlWriter
提供的回调机制来处理此类情况。为了参与回调机制,POCO实体对象或DataAnnotation
的MetadataType
类型对象必须继承IChoNotifyRecordWrite
接口。
提示:从此接口方法中引发的任何异常都将被忽略。
IChoNotifyRecordWrite
公开了以下方法
BeginWrite
- 在XML文件写入开始时调用EndWrite
- 在XML文件写入结束时调用BeforeRecordWrite
- 在XML记录写入之前引发AfterRecordWrite
- 在XML记录写入之后引发RecordWriteError
- 在写入XML记录时发生错误时引发BeforeRecordFieldWrite
- 在XML列值写入之前引发AfterRecordFieldWrite
- 在XML列值写入之后引发RecordFieldWriteError
- 在写入XML列值时发生错误时引发
列表8.1 直接POCO回调机制实现
[ChoXmlFileHeader]
[ChoXmlRecordObject(Encoding = "Encoding.UTF32",
ErrorMode = ChoErrorMode.IgnoreAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All,
ThrowAndStopOnMissingField = false)]
public partial class EmployeeRec : IChoNotifyrRecordWrite
{
[ChoXmlElementRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoXmlElementRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool AfterRecordFieldWrite
(object target, int index, string propName, object value)
{
throw new NotImplementedException();
}
public bool AfterRecordWrite(object target, int index, object source)
{
throw new NotImplementedException();
}
public bool BeforeRecordFieldWrite
(object target, int index, string propName, ref object value)
{
throw new NotImplementedException();
}
public bool BeforeRecordWrite(object target, int index, ref object source)
{
throw new NotImplementedException();
}
public bool BeginWrite(object source)
{
throw new NotImplementedException();
}
public void EndWrite(object source)
{
throw new NotImplementedException();
}
public bool RecordFieldWriteError
(object target, int index, string propName, object value, Exception ex)
{
throw new NotImplementedException();
}
public bool RecordWriteError
(object target, int index, object source, Exception ex)
{
throw new NotImplementedException();
}
}
列表8.2 基于MetaDataType的回调机制实现
[ChoXmlFileHeader]
[ChoXmlRecordObject(Encoding = "Encoding.UTF32",
ErrorMode = ChoErrorMode.IgnoreAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All,
ThrowAndStopOnMissingField = false)]
public class EmployeeRecMeta : IChoNotifyRecordWrite
{
[ChoXmlElementRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoXmlElementRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool AfterRecordFieldWrite
(object target, int index, string propName, object value)
{
throw new NotImplementedException();
}
public bool AfterRecordWrite(object target, int index, object source)
{
throw new NotImplementedException();
}
public bool BeforeRecordFieldWrite
(object target, int index, string propName, ref object value)
{
throw new NotImplementedException();
}
public bool BeforeRecordWrite(object target, int index, ref object source)
{
throw new NotImplementedException();
}
public bool BeginWrite(object source)
{
throw new NotImplementedException();
}
public void EndWrite(object source)
{
throw new NotImplementedException();
}
public bool RecordFieldWriteError
(object target, int index, string propName, object value, Exception ex)
{
throw new NotImplementedException();
}
public bool RecordWriteError(object target, int index, object source, Exception ex)
{
throw new NotImplementedException();
}
}
[MetadataType(typeof(EmployeeRecMeta))]
public partial class EmployeeRec
{
[ChoXmlElementRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoXmlElementRecordField]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
}
8.1 BeginWrite
此回调在XML文件写入开始时调用一次。source
是XML文件流对象。在此处,您有机会检查流,返回true
以继续XML生成。返回false
以停止生成。
列表 10.1.1 BeginWrite 回调示例
public bool BeginWrite(object source)
{
StreamReader sr = source as StreamReader;
return true;
}
8.2 EndWrite
此回调在XML文件生成结束时调用一次。source
是XML文件流对象。在此处,您有机会检查流,对流执行任何后续步骤。
列表 10.2.1 EndWrite 回调示例
public void EndWrite(object source)
{
StreamReader sr = source as StreamReader;
}
8.3 BeforeRecordWrite
此回调在每个POCO记录对象写入XML文件之前调用。target
是POCO记录对象的实例。index
是文件中的行索引。source
是XML记录行。在此处,您有机会检查POCO对象,并在需要时生成XML记录行。
提示:如果您想跳过记录而不写入,请将 source 设置为 null
。
提示:如果您想控制XML记录行生成,请将源设置为有效的XML记录行文本。
返回 true
继续加载过程,否则返回 false
停止过程。
列表8.3.1 BeforeRecordWrite回调示例
public bool BeforeRecordWrite(object target, int index, ref object source)
{
source = "1,Raj";
return true;
}
8.4 AfterRecordWrite
此回调在每个POCO记录对象写入XML文件之后调用。target
是POCO记录对象的实例。index
是文件中的行索引。source
是XML记录行。在此处,您有机会对记录行执行任何后续操作。
返回 true
继续加载过程,否则返回 false
停止过程。
列表8.4.1 AfterRecordWrite回调示例
public bool AfterRecordWrite(object target, int index, object source)
{
string line = source as string;
return true;
}
8.5 RecordWriteError
此回调在写入POCO记录对象时遇到错误时调用。target
是POCO记录对象的实例。index
是文件中的行索引。source
是XML记录行。ex
是异常对象。在此处,您有机会处理异常。此方法仅在Configuration.ErrorMode
为ReportAndContinue
时调用。
返回 true
继续加载过程,否则返回 false
停止过程。
列表8.5.1 RecordWriteError回调示例
public bool RecordLoadError(object target, int index, object source, Exception ex)
{
string line = source as string;
return true;
}
8.6 BeforeRecordFieldWrite
此回调在每个XML记录列写入XML文件之前调用。target
是POCO记录对象的实例。index
是文件中的行索引。propName
是XML记录属性名称。value
是XML列值。在此处,您有机会检查XML记录属性值并执行任何自定义验证等。
返回 true
继续加载过程,否则返回 false
停止过程。
列表8.6.1 BeforeRecordFieldWrite回调示例
public bool BeforeRecordFieldWrite
(object target, int index, string propName, ref object value)
{
return true;
}
8.7 AfterRecordFieldWrite
此回调在每个XML记录列值写入XML文件之后调用。target
是POCO记录对象的实例。index
是文件中的行索引。propName
是XML记录属性名称。value
是XML列值。任何后续字段操作都可以在此处执行,例如计算其他属性、验证等。
返回 true
继续加载过程,否则返回 false
停止过程。
列表8.7.1 AfterRecordFieldWrite回调示例
public bool AfterRecordFieldWrite
(object target, int index, string propName, object value)
{
return true;
}
8.8 RecordWriteFieldError
此回调在写入XML记录列值时遇到错误时调用。target
是POCO记录对象的实例。index
是文件中的行索引。propName
是XML记录属性名称。value
是XML列值。ex
是异常对象。在此处,您有机会处理异常。此方法仅在XmlReader
执行以下两步序列之后调用
XmlWriter
查找每个XML属性的FallbackValue
值。如果存在,它会尝试使用它进行写入。- 如果
FallbackValue
值不存在且Configuration.ErrorMode
指定为ReportAndContinue
,则将执行此回调。
返回 true
继续加载过程,否则返回 false
停止过程。
列表8.8.1 RecordFieldWriteError回调示例
public bool RecordFieldWriteError
(object target, int index, string propName, object value, Exception ex)
{
return true;
}
9. 自定义
XmlWriter
自动检测并从POCO实体加载配置设置。在运行时,您可以在XML生成之前自定义和调整这些参数。XmlWriter
公开Configuration
属性,它是ChoXmlRecordConfiguration
对象。使用此属性,您可以执行自定义。
列表9.1 运行时自定义XmlWriter
class Program
{
static void Main(string[] args)
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoXmlWriter("Emp.xml").WithXPath("Employees/Employee"))
{
parser.Configuration.ColumnCountStrict = true;
parser.Write(objs);
}
}
}
10. 使用动态对象
到目前为止,本文解释了使用带有POCO对象的XmlWriter
。XmlWriter
还支持在没有POCO实体对象的情况下生成XML文件。它利用了.NET动态特性。下面的示例展示了如何使用动态对象生成XML流。XML模式由第一个对象确定。如果在动态对象成员值中发现不匹配,将引发错误并停止生成过程。
下面的示例展示了这一点
列表10.1 从动态对象生成XML文件
class Program
{
static void Main(string[] args)
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoXmlWriter("Emp.xml").WithXPath("Employees/Employee"))
{
parser.Configuration.ColumnCountStrict = true;
parser.Write(objs);
}
}
}
11. 异常
XmlReader
在不同情况下抛出不同类型的异常
ChoParserException
- XML文件损坏且解析器无法恢复。ChoRecordConfigurationException
- 指定了任何无效的配置设置,将引发此异常。ChoMissingRecordFieldException
- XML列缺少属性,将引发此异常。
12. 使用MetadataType Annotation
Cinchoo ETL与数据注释的MetadataType
模型配合得更好。它是一种将MetaData
类附加到数据模型类的方法。在此关联类中,您提供数据模型中没有的其他元数据信息。它的作用是在不修改类的情况下向类添加属性。您可以向一个将拥有所有属性的类添加此属性,该属性接受单个参数。当POCO类由自动工具(如Entity Framework、MVC等)自动生成时,这很有用。这就是第二个类发挥作用的原因。您可以在不触及生成文件的情况下添加新内容。此外,通过将关注点分离到多个类中,这促进了模块化。
有关更多信息,请在 MSDN 上搜索。
列表12.1 MetadataType Annotation用法示例
[MetadataType(typeof(EmployeeRecMeta))]
public class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
}
[ChoXmlFileHeader]
[ChoXmlRecordObject(Encoding = "Encoding.UTF32", ErrorMode = ChoErrorMode.ThrowAndStop,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All, ThrowAndStopOnMissingField = false,
ObjectValidationMode = ChoObjectValidationMode.MemberLevel)]
public class EmployeeRecMeta : IChoNotifyRecordWrite, IChoValidatable
{
[ChoXmlElementRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, 1, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoXmlElementRecordField]
[StringLength(1)]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool AfterRecordFieldWrite
(object target, int index, string propName, object value)
{
throw new NotImplementedException();
}
public bool AfterRecordWrite(object target, int index, object source)
{
throw new NotImplementedException();
}
public bool BeforeRecordFieldWrite
(object target, int index, string propName, ref object value)
{
throw new NotImplementedException();
}
public bool BeforeRecordWrite(object target, int index, ref object source)
{
throw new NotImplementedException();
}
public bool BeginWrite(object source)
{
throw new NotImplementedException();
}
public void EndWrite(object source)
{
throw new NotImplementedException();
}
public bool RecordFieldWriteError
(object target, int index, string propName, object value, Exception ex)
{
throw new NotImplementedException();
}
public bool RecordWriteError(object target, int index, object source, Exception ex)
{
throw new NotImplementedException();
}
public bool TryValidate
(object target, ICollection<ValidationResult> validationResults)
{
return true;
}
public bool TryValidateFor
(object target, string memberName, ICollection<ValidationResult> validationResults)
{
return true;
}
public void Validate(object target)
{
}
public void ValidateFor(object target, string memberName)
{
}
}
在上面,EmployeeRec
是数据类。它仅包含领域特定的属性和操作。将其标记为一个非常简单的类。
我们将验证、回调机制、配置等分离到元数据类型类 EmployeeRecMeta
中。
13. 配置选择
如果POCO实体类是自动生成的类或通过库公开,或者它是一个密封类,则它会限制您以声明方式将XML模式定义附加到它。在这种情况下,您可以选择以下选项之一来指定XML布局配置。
- 手动配置
- 自动映射配置
- 附加
MetadataType
类
我将向您展示如何为每种方法配置以下 POCO 实体类。
列表13.1 密封POCO实体类
public sealed class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
}
13.1 手动配置
从头开始定义一个全新的配置对象,并将所有必要的XML字段添加到ChoXmlConfiguration.XmlRecordFieldConfigurations
集合属性。此选项为您提供了更大的灵活性来控制XML解析的配置。但缺点是可能出现错误,并且如果XML文件布局很大,则难以管理它们。
列表13.1.1 手动配置
ChoXmlRecordConfiguration config = new ChoXmlRecordConfiguration();
config.XmlFileHeaderConfiguration.HasHeaderRecord = true;
config.ThrowAndStopOnMissingField = true;
config.XmlRecordFieldConfigurations.Add(new ChoXmlRecordFieldConfiguration("Id"));
config.XmlRecordFieldConfigurations.Add(new ChoXmlRecordFieldConfiguration("Name"));
13.2 自动映射配置
这是一种替代方法,并且是一种错误更少的方法,可以自动映射POCO实体类的XML列。
首先,为 EmployeeRec
POCO 实体类定义一个模式类,如下所示:
列表13.2.1 自动映射类
public class EmployeeRecMap
{
[ChoXmlElementRecordField]
public int Id { get; set; }
[ChoXmlElementRecordField]
public string Name { get; set; }
}
然后,您可以使用ChoXmlRecordConfiguration.MapRecordFields
方法自动映射XML列。
列表13.2.2 使用自动映射配置
ChoXmlRecordConfiguration config = new ChoXmlRecordConfiguration();
config.MapRecordFields<EmployeeRecMap>();
foreach (var e in new ChoXmlReader<EmployeeRec>
("Emp.xml", config).WithXPath("Employees/Employee"))
Console.WriteLine(e.ToString());
13.3 附加MetadataType类
这是另一种为POCO实体对象附加MetadataType
类的方法。前一种方法只关注XML列的自动映射。其他配置属性,如属性转换器、解析器参数、默认/备用值等,未考虑在内。
此模型通过定义MetadataType
类并声明性地指定XML配置参数来考虑所有因素。当您的POCO实体是密封的而非部分类时,这很有用。此外,它是配置POCO实体XML解析的一种有利且错误较少的方法。
列表13.3.1 定义MetadataType类
[ChoXmlFileHeader()]
[ChoXmlRecordObject(Encoding = "Encoding.UTF32",
ErrorMode = ChoErrorMode.ReportAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All, ThrowAndStopOnMissingField = false,
ObjectValidationMode = ChoObjectValidationMode.MemberLevel)]
public class EmployeeRecMeta : IChoNotifyRecordWrite, IChoValidatable
{
[ChoXmlElementRecordField]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, 1, ErrorMessage = "Id must be > 0.")]
//[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoXmlElementRecordField]
[StringLength(1)]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool AfterRecordFieldWrite
(object target, int index, string propName, object value)
{
throw new NotImplementedException();
}
public bool AfterRecordWrite(object target, int index, object source)
{
throw new NotImplementedException();
}
public bool BeforeRecordFieldWrite
(object target, int index, string propName, ref object value)
{
throw new NotImplementedException();
}
public bool BeforeRecordWrite(object target, int index, ref object source)
{
throw new NotImplementedException();
}
public bool BeginWrite(object source)
{
throw new NotImplementedException();
}
public void EndWrite(object source)
{
throw new NotImplementedException();
}
public bool RecordFieldWriteError
(object target, int index, string propName, object value, Exception ex)
{
throw new NotImplementedException();
}
public bool RecordWriteError(object target, int index, object source, Exception ex)
{
throw new NotImplementedException();
}
public bool TryValidate
(object target, ICollection<ValidationResult> validationResults)
{
return true;
}
public bool TryValidateFor
(object target, string memberName, ICollection<ValidationResult> validationResults)
{
return true;
}
public void Validate(object target)
{
}
public void ValidateFor(object target, string memberName)
{
}
}
列表13.3.2 附加MetadataType类
//Attach metadata
ChoMetadataObjectCache.Default.Attach<EmployeeRec>(new EmployeeRecMeta());
using (var tx = File.OpenWrite("Emp.xml"))
{
using (var parser =
new ChoXmlWriter<EmployeeRec>(tx).WithXPath("Employees/Employee"))
{
parser.Write(objs);
}
}
14. ToText 辅助方法
这是一个巧妙的辅助方法,用于从对象列表生成XML格式的输出。它帮助您在测试环境中运行和尝试不同的选项,以快速查看XML输出。
static void ToTextTest()
{
List<EmployeeRec> objs = new List<EmployeeRec>();
EmployeeRec rec1 = new EmployeeRec();
rec1.Id = 10;
rec1.Name = "Mark";
objs.Add(rec1);
EmployeeRec rec2 = new EmployeeRec();
rec2.Id = 200;
rec2.Name = "Lou";
objs.Add(rec2);
Console.WriteLine(ChoXmlWriter.ToText(objs, "Employees/Employee"));
}
15. Writing DataReader 辅助方法
此辅助方法允许您从ADO.NET DataReader
创建XML文件/流。
static void WriteDataReaderTest()
{
string connString = @"Data Source=(localdb)\v11.0;
Initial Catalog=TestDb;Integrated Security=True";
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Members", conn);
IDataReader dr = cmd.ExecuteReader();
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser =
new ChoXmlWriter(writer, config).WithXPath("Employees/Employee"))
{
parser.Write(dr);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
16. Writing DataTable 辅助方法
此辅助方法允许您从ADO.NET DataTable
创建XML文件/流。
static void WriteDataTableTest()
{
string connString = @"Data Source=(localdb)\v11.0;
Initial Catalog=TestDb;Integrated Security=True";
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Members", conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser =
new ChoXmlWriter(writer, config).WithXPath("Employees/Employee"))
{
parser.Write(dt);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
17. 高级主题
17.1 覆盖转换器格式规范
Cinchoo ETL自动解析并将每个XML列值无缝转换为相应XML列的底层数据类型。大多数基本的.NET类型都无需任何设置即可自动处理。
这是通过 ETL 系统中的两个关键设置实现的
ChoXmlRecordConfiguration.CultureInfo
- 表示有关特定文化的信息,包括文化的名称、书写系统和所使用的日历,以及访问提供常见操作信息(如日期格式和字符串排序)的特定于文化的对象的权限。默认为“en-US
”。ChoTypeConverterFormatSpec
- 它是一个全局格式说明符类,包含所有固有.NET类型的格式说明符。
在本节中,我将讨论如何根据解析需求更改每个 .NET 内在数据类型的默认格式规范。
ChoTypeConverterFormatSpec
是一个单例类,其实例通过“Instance”静态成员公开。它是线程本地的,意味着每个线程都会保留单独的实例副本。
为每种固有类型提供了两组格式规范成员,一组用于加载,另一组用于写入值,布尔、Enum
、DataTime
类型除外。这些类型在加载和写入操作中只有一个成员。
通过ChoTypeConverterFormatSpec
指定每个固有数据类型格式规范将影响系统范围,即,通过设置ChoTypeConverterFormatSpec.IntNumberStyle = NumberStyles.AllowParentheses
,将影响XML对象的所有整数成员以允许括号。如果您想覆盖此行为并控制特定XML数据成员从全局系统范围设置处理其自己的唯一XML值解析,可以通过在XML字段成员级别指定TypeConverter
来实现。有关更多信息,请参阅第7.4节。
列表17.1.1 ChoTypeConverterFormatSpec成员
public class ChoTypeConverterFormatSpec
{
public static readonly ThreadLocal<ChoTypeConverterFormatSpec> Instance =
new ThreadLocal<ChoTypeConverterFormatSpec>(() => new ChoTypeConverterFormatSpec());
public string DateTimeFormat { get; set; }
public ChoBooleanFormatSpec BooleanFormat { get; set; }
public ChoEnumFormatSpec EnumFormat { get; set; }
public NumberStyles? CurrencyNumberStyle { get; set; }
public string CurrencyFormat { get; set; }
public NumberStyles? BigIntegerNumberStyle { get; set; }
public string BigIntegerFormat { get; set; }
public NumberStyles? ByteNumberStyle { get; set; }
public string ByteFormat { get; set; }
public NumberStyles? SByteNumberStyle { get; set; }
public string SByteFormat { get; set; }
public NumberStyles? DecimalNumberStyle { get; set; }
public string DecimalFormat { get; set; }
public NumberStyles? DoubleNumberStyle { get; set; }
public string DoubleFormat { get; set; }
public NumberStyles? FloatNumberStyle { get; set; }
public string FloatFormat { get; set; }
public string IntFormat { get; set; }
public NumberStyles? IntNumberStyle { get; set; }
public string UIntFormat { get; set; }
public NumberStyles? UIntNumberStyle { get; set; }
public NumberStyles? LongNumberStyle { get; set; }
public string LongFormat { get; set; }
public NumberStyles? ULongNumberStyle { get; set; }
public string ULongFormat { get; set; }
public NumberStyles? ShortNumberStyle { get; set; }
public string ShortFormat { get; set; }
public NumberStyles? UShortNumberStyle { get; set; }
public string UShortFormat { get; set; }
}
下面的示例展示了如何使用XmlReader
加载包含“se-SE
”(瑞典)文化特定数据的XML数据流。此外,输入提要附带包含括号的“EmployeeNo
”值。为了使加载成功,我们必须将ChoTypeConverterFormatSpec.IntNumberStyle
设置为NumberStyles.AllowParenthesis
。
列表17.1.2 在代码中使用ChoTypeConverterFormatSpec
static void FormatSpecDynamicTest()
{
ChoTypeConverterFormatSpec.Instance.DateTimeFormat = "d";
ChoTypeConverterFormatSpec.Instance.BooleanFormat = ChoBooleanFormatSpec.YOrN;
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoXmlWriter(writer).WithXPath("Employees/Employee"))
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
17.2 货币支持
Cinchoo ETL提供ChoCurrency
对象以在XML文件中读取和写入货币值。ChoCurrency
是一个包装类,用于以十进制类型保存货币值,并支持在XML加载期间以文本格式序列化它们。
列表17.2.1 在动态模型中使用货币成员
static void CurrencyDynamicTest()
{
ChoTypeConverterFormatSpec.Instance.CurrencyFormat = "C2";
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoXmlWriter(writer).WithXPath("Employees/Employee"))
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
上面的示例展示了如何使用动态对象模型输出货币值。由于货币输出将包含千位逗号分隔符,这将导致无法生成XML文件。为了克服这个问题,我们指定写入器引用所有字段。
附注:货币值的格式由XmlReader
通过ChoRecordConfiguration.Culture
和ChoTypeConverterFormatSpec.CurrencyFormat
确定。
下面的示例展示了如何在POCO实体类中使用ChoCurrency
XML字段。
列表17.2.2 在POCO模型中使用货币成员
public class EmployeeRecWithCurrency
{
public int Id { get; set; }
public string Name { get; set; }
public ChoCurrency Salary { get; set; }
}
static void CurrencyPOCOTest()
{
List<EmployeeRecWithCurrency> objs = new List<EmployeeRecWithCurrency>();
EmployeeRecWithCurrency rec1 = new EmployeeRecWithCurrency();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);
EmployeeRecWithCurrency rec2 = new EmployeeRecWithCurrency();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoXmlWriter<EmployeeRecWithCurrency>
(writer).WithXPath("Employees/Employee"))
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
17.3 枚举支持
Cinchoo ETL隐式处理从XML文件解析/写入enum
列值。如果您想精细控制这些值的解析,可以通过ChoTypeConverterFormatSpec.EnumFormat
全局指定它们。默认为ChoEnumFormatSpec.Value
。
FYI,更改此值将影响整个系统。
有三种可能的值可以使用
ChoEnumFormatSpec.Value
-Enum
值用于解析。ChoEnumFormatSpec.Name
-Enum
键名用于解析。ChoEnumFormatSpec.Description
- 如果每个enum
键都用DescriptionAttribute
修饰,则其值将用于解析。
列表17.3.1 在解析期间指定枚举格式规范
public enum EmployeeType
{
[Description("Full Time Employee")]
Permanent = 0,
[Description("Temporary Employee")]
Temporary = 1,
[Description("Contract Employee")]
Contract = 2
}
static void EnumTest()
{
ChoTypeConverterFormatSpec.Instance.EnumFormat = ChoEnumFormatSpec.Description;
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
rec1.Status = EmployeeType.Permanent;
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
rec2.Status = EmployeeType.Contract;
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoXmlWriter(writer).WithXPath("Employees/Employee"))
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
17.4 布尔支持
Cinchoo ETL隐式处理从XML文件解析/写入布尔XML列值。如果您想精细控制这些值的解析,可以通过ChoTypeConverterFormatSpec.BooleanFormat
全局指定它们。默认值为ChoBooleanFormatSpec.ZeroOrOne
。
FYI,更改此值将影响整个系统。
有四种可能的值可以使用
ChoBooleanFormatSpec.ZeroOrOne
- '0
' 表示false
。'1
' 表示true
。ChoBooleanFormatSpec.YOrN
- 'Y
' 表示true
,'N
' 表示false
。ChoBooleanFormatSpec.TrueOrFalse
- 'True
' 表示true
,'False
' 表示false
。ChoBooleanFormatSpec.YesOrNo
- 'Yes
' 表示true
,'No
' 表示false
。
列表17.4.1 在解析期间指定布尔格式规范
static void BoolTest()
{
ChoTypeConverterFormatSpec.Instance.BooleanFormat = ChoBooleanFormatSpec.YOrN;
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
rec1.Status = EmployeeType.Permanent;
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
rec2.Status = EmployeeType.Contract;
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoXmlWriter(writer).WithXPath("Employees/Employee"))
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
17.5 日期时间支持
Cinchoo ETL隐式处理使用系统文化或自定义文化从XML文件解析/写入日期时间XML列值。如果您想精细控制这些值的解析,可以通过ChoTypeConverterFormatSpec.DateTimeFormat
全局指定它们。默认值为“d
”。
FYI,更改此值将影响整个系统。
您可以使用任何有效的标准或自定义datetime
.NET格式规范来解析文件中的datetime
XML值。
列表17.5.1 在解析期间指定日期时间格式规范
static void DateTimeDynamicTest()
{
ChoTypeConverterFormatSpec.Instance.DateTimeFormat = "MMM dd, yyyy";
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoXmlWriter(writer).WithXPath("Employees/Employee"))
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
上面的示例展示了如何生成自定义datetime
值到XML文件。
18. 历史
- 2017年3月7日:初始版本