固定大小格式化程序






4.94/5 (17投票s)
用于声明式解析和创建固定大小原始数据(
引言
如今,应用程序/系统以众所周知的[数据格式](例如 XML/JSON 等)传输数据。然而,在过去,系统使用的是专有数据格式,其中数据生产者和使用者之间就预定义了固定大小的原始结构,并将其写入平面文件或在网络上传输。
令人惊讶的是,不久前,当我需要硬件设备通过 HTTP 传输与服务器通信时,我决定采用这种“老派”做法——以最小的消息大小。
我提出这个框架,它通过声明式的方式帮助开发人员创建和解析这种类型的数据,而不是硬编码,从而以最少的工作量实现美观、可维护且经过验证的有效代码。
尽管它原本计划用于处理少量数据,但我还做了一些工作,使其也能处理平面文件。
Using the Code
再简单不过了,您所要做的就是
- 引用 DLL 并添加适当的
using
语句。 - 定义一个表示原始数据的类(推荐 POCO),并用
FixedSizeContract
属性进行装饰。 - 用实际表示数据结构的属性装饰这些类属性。
- 调用
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 日:首次发布