65.9K
CodeProject 正在变化。 阅读更多。
Home

Cinchoo ETL -XML Writer

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2017年3月4日

CPOL

22分钟阅读

viewsIcon

9423

适用于.NET的简单XML写入器

目录

  1. 引言
  2. 要求
  3. “Hello World!”示例
    1. 快速写入 - 数据优先方法
    2. 代码优先方法
    3. 配置优先方法
  4. 写入所有记录
  5. 手动写入记录
  6. 自定义XML记录
  7. 自定义XML字段
    1. 默认值
    2. ChoFallbackValue
    3. 类型转换器
    4. 验证
  8. 回调机制
    1. BeginWrite
    2. EndWrite
    3. BeforeRecordWrite
    4. AfterRecordWrite
    5. RecordWriteError
    6. BeforeRecordFieldWrite
    7. AfterRecordFieldWrite
    8. RecordWriteFieldError
  9. 自定义
  10. 使用动态对象
  11. 异常
  12. 使用 MetadataType 注解
  13. 配置选项
    1. 手动配置
    2. 自动映射配置
    3. 附加 MetadataType 类
  14. ToText 辅助方法
  15. Writing DataReader 辅助方法
  16. Writing DataTable 辅助方法
  17. 高级主题 (Advanced Topics)
    1. 覆盖转换器格式规范
    2. 货币支持
    3. 枚举支持
    4. 布尔值支持
    5. 日期时间支持
  18. 历史

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安装ChoETLInstall-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配置对象。

3.4. 代码优先声明式配置

这是一种组合方法,用于定义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文件结构,构造对象列表并将其传递给XmlWriterWrite方法一样简单。这将通过一次调用将整个对象列表写入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值仅在ErrorModeIgnoreAndContinueReportAndContinue时设置。

7.3. 类型转换器

大多数基本类型会自动转换为字符串/文本并保存到XML文件。如果XML字段的值未自动转换为文本值,您可以指定自定义/内置.NET转换器将值转换为文本。这些可以是IValueConverterTypeConverter转换器。

用于将/格式化属性值转换为文本的方法是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.DataAnnotationsValidation 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.MemberLevelChoObjectValidationMode.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实体对象或DataAnnotationMetadataType类型对象必须继承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.ErrorModeReportAndContinue时调用。

返回 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对象的XmlWriterXmlWriter还支持在没有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 系统中的两个关键设置实现的

  1. ChoXmlRecordConfiguration.CultureInfo - 表示有关特定文化的信息,包括文化的名称、书写系统和所使用的日历,以及访问提供常见操作信息(如日期格式和字符串排序)的特定于文化的对象的权限。默认为“en-US”。
  2. ChoTypeConverterFormatSpec - 它是一个全局格式说明符类,包含所有固有.NET类型的格式说明符。

在本节中,我将讨论如何根据解析需求更改每个 .NET 内在数据类型的默认格式规范。

ChoTypeConverterFormatSpec 是一个单例类,其实例通过“Instance”静态成员公开。它是线程本地的,意味着每个线程都会保留单独的实例副本。

为每种固有类型提供了两组格式规范成员,一组用于加载,另一组用于写入值,布尔、EnumDataTime类型除外。这些类型在加载和写入操作中只有一个成员。

通过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.CultureChoTypeConverterFormatSpec.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,更改此值将影响整个系统。

有三种可能的值可以使用

  1. ChoEnumFormatSpec.Value - Enum 值用于解析。
  2. ChoEnumFormatSpec.Name - Enum 键名用于解析。
  3. 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,更改此值将影响整个系统。

有四种可能的值可以使用

  1. ChoBooleanFormatSpec.ZeroOrOne - '0' 表示 false。'1' 表示 true
  2. ChoBooleanFormatSpec.YOrN - 'Y' 表示 true,'N' 表示 false
  3. ChoBooleanFormatSpec.TrueOrFalse - 'True' 表示 true,'False' 表示 false
  4. 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日:初始版本
© . All rights reserved.