C#中如何实现对象与固定长度字符串的序列化/反序列化?
C# 固定长度字符串序列化与反序列化实现方案
针对你的需求,C# 没有现成的通用序列化器完全匹配这种固定长度+空格填充的格式,但可以通过以下两种方案实现,兼顾简单性和复用性:
一、手动实现(适合结构固定的简单类)
对于你的Record类,直接编写序列化和反序列化方法是最直观高效的方式:
序列化方法
public static string SerializeRecord(Record record) { // Identifier 固定4位,空值或长度不足补空格,超过则截断 var identifier = record.Identifier?.PadRight(4).Substring(0, 4) ?? new string(' ', 4); // ProcessAbbreviation 固定8位(对应原需求的第5-12位) var processAbbr = record.ProcessAbbreviation?.PadRight(8).Substring(0, 8) ?? new string(' ', 8); // Name 固定4位(对应原需求的第13-16位) var name = record.Name?.PadRight(4).Substring(0, 4) ?? new string(' ', 4); return string.Concat(identifier, processAbbr, name); }
反序列化方法
public static Record DeserializeRecord(string fixedLengthString) { if (fixedLengthString.Length != 16) // 总长度4+8+4=16位 throw new ArgumentException("输入字符串长度不符合固定格式要求"); return new Record { Identifier = fixedLengthString.Substring(0, 4).TrimEnd(), // 去除末尾填充的空格 ProcessAbbreviation = fixedLengthString.Substring(4, 8).TrimEnd(), Name = fixedLengthString.Substring(12, 4).TrimEnd() }; }
这个方案的优势是性能高、逻辑清晰,适合不需要复用的单一类场景。
二、特性驱动的通用实现(适合多类复用)
如果后续有多个类需要类似的固定长度序列化,可以通过自定义特性+反射实现通用工具类,避免重复代码:
1. 定义固定长度特性
[AttributeUsage(AttributeTargets.Property)] public class FixedLengthAttribute : Attribute { public int Length { get; } public FixedLengthAttribute(int length) { Length = length; } }
2. 为Record类属性标记特性
public class Record { [FixedLength(4)] public string Identifier { get; set; } [FixedLength(8)] public string ProcessAbbreviation { get; set; } [FixedLength(4)] public string Name { get; set; } }
3. 通用序列化反序列化工具
public static class FixedLengthSerializer { public static string Serialize<T>(T obj) where T : new() { // 按类中属性定义顺序获取标记了特性的属性 var properties = typeof(T).GetProperties() .Where(p => p.GetCustomAttribute<FixedLengthAttribute>() != null) .OrderBy(p => p.MetadataToken); var result = new StringBuilder(); foreach (var prop in properties) { var lengthAttr = prop.GetCustomAttribute<FixedLengthAttribute>(); var value = prop.GetValue(obj)?.ToString() ?? string.Empty; // 填充空格并截断到指定长度 var fixedValue = value.PadRight(lengthAttr.Length).Substring(0, lengthAttr.Length); result.Append(fixedValue); } return result.ToString(); } public static T Deserialize<T>(string fixedLengthString) where T : new() { var obj = new T(); var properties = typeof(T).GetProperties() .Where(p => p.GetCustomAttribute<FixedLengthAttribute>() != null) .OrderBy(p => p.MetadataToken); int currentIndex = 0; foreach (var prop in properties) { var lengthAttr = prop.GetCustomAttribute<FixedLengthAttribute>(); if (currentIndex + lengthAttr.Length > fixedLengthString.Length) throw new ArgumentException("输入字符串长度不足,无法完成反序列化"); var value = fixedLengthString.Substring(currentIndex, lengthAttr.Length).TrimEnd(); prop.SetValue(obj, value); currentIndex += lengthAttr.Length; } if (currentIndex != fixedLengthString.Length) throw new ArgumentException("输入字符串长度超出预期格式"); return obj; } }
使用示例
// 序列化 var record = new Record { Identifier = "AAAA", ProcessAbbreviation = "BBBB", Name = "CCCC" }; var serializedStr = FixedLengthSerializer.Serialize(record); // 输出结果:"AAAABBBB CCCC" // 反序列化 var deserializedRecord = FixedLengthSerializer.Deserialize<Record>(serializedStr);
这个方案的优势是可复用性强,新增类时只需标记特性即可,缺点是反射会带来轻微的性能开销,对性能要求极高的场景建议用手动实现。
内容的提问来源于stack exchange,提问作者Maik Hasler
相关产品推荐
相关产品推荐

