我正在用 C# 编写一个程序,用于保存和加载特定结构中的对象列表,如下所示:
SIGNATURE\r\n
SIZE\r\n
OBJECT_DATA\r\n
\r\n
SIZE\r\n
OBJECT_DATA\r\n
\r\n
Signature
是一个原始字符串,其中包含私钥,用于将我的数据文件与其他文件分开。
SIZE
是一个整数值,表示 OBJECT_DATA
使用的字节数。
OBJECT_DATA
是一个包含我的数据的类。
我在 Stackoverflow 中找到了很多解决方案,几乎所有解决方案都使用
BinaryFormatter
,这是不安全的,不应该使用。
我还查看了protobuf-net,但我仍然很困惑如何将其应用于我的问题。
我认为解决方案的序列化和反序列化阶段将如下所示:
序列化(假设输入/输出文件是二进制文件):
1. Open output file and write the signature.
2. Loop over the source object list
- With each item, calculate the size of item and write it to output file.
- Write object data to binary file. (I'm still confusing in this step)
- Write "\r\n" to mark this is the end of the item.
反序列化
1. Open input file and read the Signature
2. If the Signature fit with my secret key:
- Loop until end of file
- Read the SIZE value.
- Read object and create instance of it's class.
我提到的
OBJECT_DATA
是一个类的实例:
public abstract class IShape
{
public State Configuration { get; set; }
public abstract string Name { get; }
public List<Point> Points { get; set; } = [];
public abstract UIElement Draw();
public abstract IShape Clone();
public BitmapImage Preview { get; set; }
}
State Configuration
的结构:
public class State
{
public DoubleCollection StrokeDashArray { get; set; }
public SolidColorBrush Stroke { get; set; }
public SolidColorBrush Fill { get; set; }
public double StrokeThickness { get; set; }
}
我认为 Protobuf-net 是将对象序列化为
Stream
的好方法,但我的类包含 System.Windows.Media
中的一些值,我不确定是否可以使用 Protobuf-net 对其进行序列化。
我通过将
IShape
和 State
组合到 POCO 类中来解决我自己的问题。
public class ShapeProtocol
{
public string Name { get; set; }
public string StartX { get; set; }
public string StartY { get; set; }
public string EndX { get; set; }
public string EndY { get; set; }
public string StrokeThickness { get; set; }
public string Stroke { get; set; }
public string? StrokeDashArray { get; set; }
public string? Fill { get; set; }
public ShapeProtocol() { }
public ShapeProtocol(IShape shape)
{ /* Constructor */ }
public static IShape ConvertBack(ShapeProtocol protocol)
{ /*...*/ }
}
然后我实现了一个
ShapeSerializer
:
public static class ShapeSerializer
{
public static string Serialize(IShape shape)
{
var shapeProtocol = new ShapeProtocol(shape);
var properties = from p in shapeProtocol.GetType().GetProperties()
where p.GetValue(shapeProtocol, null) != null
select p.Name + "=" + p.GetValue(shapeProtocol, null);
// this string will be the OBJECT_DATA I mentioned.
// the result will look like this: Name=Ellipse;StartX=240;StartY=48.2;EndX=411.3;EndY=140;StrokeThickness=1;Stroke=#FFDC143C
return String.Join(";", properties.ToArray());
}
public static IShape Deserialize(string rawData)
{
var _raw = rawData.Split(';').ToDictionary(p => p.Split("=")[0], p => p.Split("=")[1]);
var deserialized = new ShapeProtocol();
var _properties = typeof(ShapeProtocol).GetProperties();
foreach (var property in _properties)
{
try
{
var value = _raw[property.Name];
property.SetValue(deserialized, value, null);
}
catch { /* assume that value is null */ }
}
return ShapeProtocol.ConvertBack(deserialized);
}
}
然后我只是使用
FileStream
来存储/加载具有文件结构的数据,这是这个问题的最开始。
向@JonSkeet 和@MarcGravell 表示感谢。你们俩都是解决这个问题的重要灵感来源。