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

固定大小格式化程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (17投票s)

2010年5月10日

CPOL

3分钟阅读

viewsIcon

39573

downloadIcon

715

用于声明式解析和创建固定大小原始数据( 扁平文件) 的框架

引言

如今,应用程序/系统以众所周知的[数据格式](例如 XML/JSON 等)传输数据。然而,在过去,系统使用的是专有数据格式,其中数据生产者和使用者之间就预定义了固定大小的原始结构,并将其写入平面文件或在网络上传输。

令人惊讶的是,不久前,当我需要硬件设备通过 HTTP 传输与服务器通信时,我决定采用这种“老派”做法——以最小的消息大小。

我提出这个框架,它通过声明式的方式帮助开发人员创建和解析这种类型的数据,而不是硬编码,从而以最少的工作量实现美观、可维护且经过验证的有效代码。

尽管它原本计划用于处理少量数据,但我还做了一些工作,使其也能处理平面文件。

Using the Code

再简单不过了,您所要做的就是

  1. 引用 DLL 并添加适当的 using 语句。
  2. 定义一个表示原始数据的类(推荐 POCO),并用 FixedSizeContract 属性进行装饰。
  3. 用实际表示数据结构的属性装饰这些类属性。
  4. 调用 FixedSizeStreamReader FixedSizeStreamWritter 来为您完成工作。
/// <summary>
/// This class will be represented with a fixed size string
/// </summary>
[FixedSizeContract]
public class Student
{
    [FixedSizeMember(Order = 0, Length = 20, 
	PaddingMode = PaddingMode.Right, PaddingChar = ' ')]
    public string Name { get; set; }

    [FixedSizeMember(Order = 1, Length = 10, 
	PaddingMode = PaddingMode.Left, PaddingChar = '0')]
    public string ID { get; set; }

    /// <summary>
    /// This property uses the default integer formatter
    /// </summary>
    [NumericFixedSizeMember(Order =2, Length = 3, NumberStyles = NumberStyles.Number)]
    public int Weight { get; set; }

    /// <summary>
    /// This property demonstrate the use of 
    /// a custom formatter. - therefore the use of formattername
    /// </summary>
    [NumericFixedSizeMember(Order = 3, Length = 3, 
	NumberStyles = NumberStyles.Number, FormatterName = "MyCustomContainerName")]
    public int Height { get; set; }

    [BooleanFixedSizeMember(Order = 4, Length = 1, TrueValue = "T", Falsevalue = "F")]
    public bool Good { get; set; }

    [FixedSizeMember(Order = 5, Length = 8, Format = "yyyyMMdd")]
    public DateTime BirthDate { get; set; }

    /// <summary>
    /// This property demonstrates the use of a CustomFixedSizeMemberAttribute
    /// </summary>
    [SpecialNumber(Order = 6, Length = 3,AddtionalStuff =123)]
    public int MyProperty { get; set; }
}
private void SaveFictiousData(string path)
{
    Student[] students = new Student[]
    {
        new Student{Name="Udi Peretz",Height=176,ID="12345678",
		BirthDate=new DateTime(1973,10,10),Weight=78},
        new Student{Name="Ziv Bar",Height=172,ID="78948152",
		BirthDate=new DateTime(1948,12,29),Weight=72},
        new Student{Name="Sion Cohen",Height=178,ID="15559875",
		BirthDate=new DateTime(1967,6,5),Weight=80}
    };

    using (FixedSizeStreamWriter<Student> writer = 
	new FixedSizeStreamWriter<Student>(path, false, Encoding.GetEncoding(1255)))
    {
        writer.Write(students);
        writer.Close();
    }
}
private List<Student> ReadFromFile(string path, out double avg)
{
    using (FixedSizeStreamReader<Student> reader = 
	new FixedSizeStreamReader<Student>(path, Encoding.UTF8))
    {
        var result = reader.ReadToEnd().ToList();
        avg = result.Average(x => x.Height);    
        return result;
    }
}

上面的代码演示了如何定义数据结构(布局)以及如何将对象读写到文件中。

特点

框架关于如何格式化和定位数据的指令通过 DataMember 属性提供。在这些指令中包含以下功能:

属性布局

您应该始终声明属性的顺序,否则属性将按照它们在代码中出现的顺序进行定位。payload 属性将始终假定为最后一个。

组成

如果一个对象是另一个对象的复合体,那么该对象将完全作为简单属性进行布局,之后将格式化其余的属性。

继承

基类属性将始终放置在子类之前。

长度与填充

对于每个属性,您将设置其长度。您可以定义填充行为(如果需要),甚至可以让填充 char 继承自基类 FixedSizeContract

载荷数据

您可以允许最后一个数据字段(属性)不使用固定长度。fw 允许您将单个属性标记为 payload ——该属性将不使用填充或任何长度限制。

标题

您可以为文件中的第一个记录定义不同的类型,甚至可以将报头记录设置在单独的文件中。

LINQ

由于所有数据都表示为 CLR 对象,因此可以使用您想要的任何容器进行查询。

性能问题

无。尽管大量使用了反射,但我努力将其仅用于一次图/模式构建阶段——因此没有引入性能问题。

可扩展性

该框架支持多个可扩展点。

首先,您可以添加自己的 ICustomFormatter 并注册其类型。在这种情况下,每当调用特定类型时。但是,您可以通过命名为同一类型使用多个格式化程序。

您还可以通过继承 CustomFixedSizedMemberAttribute 来创建自己的 DataMember 属性,这样您就可以为数据结构提供附加的特性。

<configuration>
    <configSections>
        <section name="FixedSizeFormatting" 
		type ="SC.Utilities.FixedSizeFormatting.Configuration.FSConfiguration,
		SC.Utilities.FixedSizeFormatting"/>
    </configSections>
    <FixedSizeFormatting>
    <Containers>
        <Container Name="MyCustomContainerName">
            <Formatters>
                <Formatter TypeFullName="System.Int32" 
		FormatWith="JerusalemSchool.SpecialInt32Formatter,JerusalemSchool" 
		DefaultFormatString="test"/>
            </Formatters>
        </Container>
    </Containers>
    </FixedSizeFormatting>
</configuration>

框架的自定义配置部分允许您定义一个自定义格式化程序,该格式化程序将在标记了 FormatterName 的每个属性上调用。

[FixedSizeMember(Order = 0, Length = 5, 
	PaddingMode = PaddingMode.Right, PaddingChar = ' ' , 
	FormatterName="MyCustomContainerName")]
public int IntValue { get; set; }

如果有时间,我希望发布一篇关于“扩展固定大小格式化框架”的文章——但现在,请相信我,一切都已准备就绪!

限制与已知问题

该框架不支持循环引用,对象不能持有对其自身的类型引用。请使用简单对象!

下一步

  • 添加能力,使框架的行为可以基于配置(config 和 XML 映射文件)而不是代码来声明。
  • 基于 FW 创建一个序列化程序。
  • 使用相应的通道调度程序扩展 WCF,以使用固定大小格式化代替 XML——我猜它会模仿 HTTP REST 在 WCF 上的实现方式。

历史

  • 2010 年 5 月 10 日:首次发布
© . All rights reserved.