我有一种奇怪的情况发生,我不太了解。
我有一个'数据集'类,其中包含有关监视浮标的各种元数据,包括“传感器”列表。
每个电流'sensorstate'。
每个'sensorstate'都有一些关于它的元数据(时间戳,更改原因等),但最重要的是它有Dictionary<DateTime,float>
个值。
这些传感器通常有超过50k的数据点(相当于15分钟的数据读数),所以我想找到一些比默认的.NET BinaryFormatter
更快的序列化速度,因此设置{{} 3}}会快速地序列化。
不幸的是,我的问题发生在反序列化时,我的值字典引发了一个异常,因为已经有一个项目添加了相同的键,并且我可以让它反序列化的唯一方法是启用'OverwriteList'但我有点不确定为什么在序列化时没有任何重复的键(它是字典),为什么在反序列化时有重复的键?这也会带来数据完整性问题。
任何帮助解释这一点都将受到高度赞赏。
(另一方面,在给出ProtoMember属性id时,它们是否需要对类或整个项目是唯一的?我正在寻找与protobuf-net一起使用的无损压缩建议,因为文件是变得非常大)
编辑:
我刚刚将我的资源放在GitHub上,这是有问题的课程
Protobuf-net(注意:它目前有OverwriteList = true以使其适用于其他开发)
以下是SensorState
的示例我已经尝试过使用SkipContructor标志,但即使设置为true,它也会获得异常,除非对于值字典,OverwriteList也是如此。
答案 0 :(得分:2)
如果OverwriteList
修复它,那么它建议给我默认情况下字典中有一些数据,可能是通过构造函数或类似的。如果它确实来自构造函数,则可以使用[ProtoContract(SkipConstructor=true)]
禁用它。
如果我误解了上述内容,如果可能的话,可以通过可重复的示例进行说明。
关于id,它们只需要在每种类型中都是唯一的,并且建议保持它们小(由于标签的“varint”编码,小键比大键“更便宜”)。
如果你想真正减小尺寸,我实际上也会建议查看数据的内容。例如,你说这是15分钟的读数...好吧,我猜有偶尔的差距,但你可以做,例如:
Block (class)
Start Time (DateTime)
Values (float[])
并且每个连续的15分钟值都有一个Block
(这里的假设是每个值在最后一个之后是15,否则启动一个新块)。因此,您要存储多个Block
实例来代替单个字典。这有以下优点:
DateTime
值要少得多
[ProtoMember({key}, IsPacked = true)]
)来做到这一点 - 注意它只适用于几种基本数据类型(不是子对象)如果数据有很多字符串,您可以尝试GZIP / DEFLATE。你当然可以尝试这两种方式,但是如果没有大量的字符串数据,我会谨慎地期望从压缩中获得额外的额外费用。
作为基于提供的(CSV)数据文件的更新,处理字典没有固有的问题 - 如图所示:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ProtoBuf;
class Program
{
static void Main()
{
var data = new Data
{
Points =
{
{new DateTime(2009,09,1,0,0,0), 11.04F},
{new DateTime(2009,09,1,0,15,0), 11.04F},
{new DateTime(2009,09,1,0,30,0), 11.01F},
{new DateTime(2009,09,1,0,45,0), 11.01F},
{new DateTime(2009,09,1,1,0,0), 11F},
{new DateTime(2009,09,1,1,15,0), 10.98F},
{new DateTime(2009,09,1,1,30,0), 10.98F},
{new DateTime(2009,09,1,1,45,0), 10.92F},
{new DateTime(2009,09,1,2,00,0), 10.09F},
}
};
var ms = new MemoryStream();
Serializer.Serialize(ms, data);
ms.Position = 0;
var clone =Serializer.Deserialize<Data>(ms);
Console.WriteLine("{0} points:", clone.Points.Count);
foreach(var pair in clone.Points.OrderBy(x => x.Key))
{
float orig;
data.Points.TryGetValue(pair.Key, out orig);
Console.WriteLine("{0}: {1}", pair.Key, pair.Value == orig ? "correct" : "FAIL");
}
}
}
[ProtoContract]
class Data
{
private readonly Dictionary<DateTime, float> points = new Dictionary<DateTime, float>();
[ProtoMember(1)]
public Dictionary<DateTime, float> Points { get { return points; } }
}
答案 1 :(得分:1)
这是我道歉的地方,因为它曾暗示它与我自己所做的代码有任何关系。虽然我在这里疯狂支持protobuf和Marc Gravell支持protobuf-net的团队但它的速度非常快。
发生的事情发生在Sensor类我有一些逻辑,永远不会让一些属性永远不为空。
[ProtoMember(12)]
public SensorState CurrentState
{
get { return (_currentState == null) ? RawData : _currentState; }
set { _currentState = value; }
}
[ProtoMember(16)]
public SensorState RawData
{
get { return _rawData ?? (_rawData = new SensorState(this, DateTime.Now, new Dictionary<DateTime, float>(), "", true, null)); }
private set { _rawData = value; }
}
虽然这很有效,因为当我使用这些属性时,它会扰乱序列化过程。
简单的解决方法是改为标记基础对象以进行序列化。
[ProtoMember(16)]
private SensorState _rawData;
[ProtoMember(12)]
private SensorState _currentState;