I have a need to serialize the following type
public class MspMessage {
[DataMember(Order = 1), ProtoMember(1)]
public byte[] Prefix { get; private set; }
[DataMember(Order = 2), ProtoMember(2)]
public MspMessageType MessageType { get; private set; }
[DataMember(Order = 3), ProtoMember(3)]
public int DataLength { get; private set; }
[DataMember(Order = 4), ProtoMember(4)]
public MspCodes MspCode { get; private set; }
[DataMember(Order = 5), ProtoMember(5)]
public byte[] Data { get; private set; }
[DataMember(Order = 6), ProtoMember(6)]
public byte Checksum
{
get; private set;
}
public MspMessage(MspMessageType messageType, MspCodes mspCode, byte[] data) {
Prefix = new byte[] { 36, 77 };
MessageType = messageType;
MspCode = mspCode;
Data = data;
DataLength = data?.Length ?? 0;
if (data == null || data.Length == 0) {
Checksum = (byte)(0 ^ (int)MspCode);
} else {
var checksum = data.Length ^ (int)MspCode;
checksum = data.Aggregate(checksum, (current, t) => current ^ t);
Checksum = (byte)checksum;
}
}
}
Into a simple binary array like this: {36, 77, 60, 0, 1, 1}. I hacked around with BinaryFormatter and protobuf-net, but both produce added output besides the pure contents of the type. What is the most efficient way to go about this? It's my intention to create derived types that will have additional fields. Thanks!
I wound up writing my own IFormatter which essentially is the same thing as a ToByteArray() and FromByteArray(). Thanks!
Related
How I can generete primary key by object's properties?
I need something like hash. But stable hash;
public Object1 {
public Object1(string property1, DateTime property2)
{
Property1 = property1;
Property2 = property2;
StableHashID = GetStableHash();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int StableHashID { get; private set; }
public string Property1 { get; private set; }
public DateTime Property2 { get; private set; }
public int GetStableHash()
{
return ...; // ???
}
}
Guid is enough for you. If you will use Id column just an index, Guid.NewGuid() would generate a great hash for you.
Guid.NewGuid() makes an actual guid with a unique value, what you probably want.
Guid.NewGuid() vs. new Guid()
However, if you find object values after decryption of hash, you can check;
Encrypting & Decrypting a String in C#
Without reflection:
public int GetStableHash()
{
var now = DateTime.Now;
var nowInt = (int) now.Kind;
var nowTicks = (ulong) now.Ticks;
return (((ulong) nowInt) << 62) | nowTicks;
}
With properties: https://stackoverflow.com/a/12393880
EDIT:
But I still think that GUID is good enough - auto-generated, incremental, have benefits with EFCore (eg. Update method), etc.
Fluent API:
public override void Configure(EntityTypeBuilder<Entity> entityBuilder)
{
entityBuilder.Property(entity => entity.Id).HasDefaultValueSql("NEWID()");
}
Or with annotation: [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
You want to generate primary keys based on other properties values, then you need to generate different hash values from these two properties values in GetStableHash method.
public class Object1
{
public Object1(string property1, DateTime property2)
{
Property1 = property1;
Property2 = property2;
StableHashID = GetStableHash();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int StableHashID { get; private set; }
public string Property1 { get; private set; }
public DateTime Property2 { get; private set; }
public int GetStableHash()
{
var result = default(byte[]);
using (var stream = new MemoryStream())
{
using (var writer = new BinaryWriter(stream, Encoding.UTF8, true))
{
writer.Write(Property2.Ticks);
writer.Write(Property1);
writer.Write(Guid.NewGuid().ToString());
}
stream.Position = 0;
using (var hash = SHA256.Create())
{
result = hash.ComputeHash(stream);
}
}
return BitConverter.ToInt32(result, 0);
}
I find another solutions.
public override int GetStableHash()
{
int result = GetDeterministicHashCode(ErrorCode.ToString());
result = (result * 397) ^ (GetDeterministicHashCode(Property1.ToString()));
result = (result * 397) ^ (GetDeterministicHashCode(Property2.ToString()));
return result;
}
// https://stackoverflow.com/questions/5154970/how-do-i-create-a-hashcode-in-net-c-for-a-string-that-is-safe-to-store-in-a
private int GetDeterministicHashCode(string str)
{
unchecked
{
int hash1 = (5381 << 16) + 5381;
int hash2 = hash1;
for (int i = 0; i < str.Length; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ str[i];
if (i == str.Length - 1)
break;
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
}
return hash1 + (hash2 * 1566083941);
}
}
I need to export an ObservableCollection of this class to a binary file:
[StructLayout(LayoutKind.Sequential)]
[XmlRoot("RxFrame")]
public class RxFrame : BaseClass
{
[XmlAttribute]
public string ID { get; set; } = "00000000";
[XmlAttribute]
public int IntVal{ get; set; } = 0;
[XmlAttribute]
public bool IsExtended { get; set; } = false;
public RxFrame()
{
}
}
and this is the function to export the data to binary:
[StructLayout(LayoutKind.Sequential)]
public class BaseClass
{
public BaseClass()
{ }
public virtual byte[] getBinaryData()
{
int size = Marshal.SizeOf(this);
IntPtr ptr = Marshal.AllocHGlobal(size);
byte[] buffer = new byte[Marshal.SizeOf(this)];
Marshal.StructureToPtr(this, ptr, false);
Marshal.Copy(ptr, buffer, 0, Marshal.SizeOf(this));
Marshal.FreeHGlobal(ptr);
return buffer;
}
}
And this is the loop over each element:
using (FileStream mstream = File.Open(filePathParam + "RX.bin", FileMode.Create))
{
BinaryWriter bWriter = new BinaryWriter(mstream, Encoding.UTF8);
bWriter.Write(ListeRxFrames.Count);
foreach (RxFrame rx in ListeRxFrames)
{
bWriter.Write(rx.getBinaryData());
}
bWriter.Close();
}
This is working fine, but I only need to export the IntVal and IsExtended properties of the RxFrame class to binary. How do I do this?
There's no way to ignore a field when marshaling the object. Maybe you need to create a separate class for the data you want to marshal.
I need to export into a binary file an observable collection. This file will be parsed by an embeded software.
This is my class of Led configuration :
[XmlRoot("ConfLed")]
public class LedVals
{
#region Properties
[XmlAttribute]
public int ID { get; set; }
[XmlAttribute]
public string Type { get; set; } = "Trigger";
[XmlAttribute]
public string Binding { get; set; } = "OFF";
[XmlAttribute]
public int Trigger1 { get; set; } = 0;
[XmlAttribute]
public int Trigger2 { get; set; } = 0;
[XmlAttribute]
public string ColorT0 { get; set; } = "#000000";
[XmlAttribute]
public string ColorT1 { get; set; } = "#000000";
[XmlAttribute]
public string ColorT2 { get; set; } = "#000000";
#endregion
public LedVals()
{
}
public LedVals(int idParam, string typeParam, string bindingParam, int trig1Param, int trig2Param, string c0Param, string c1Param, string c2Param)
{
this.ID = idParam;
this.Type = typeParam;
this.Binding = bindingParam;
this.Trigger1 = trig1Param;
this.Trigger2 = trig2Param;
this.ColorT0 = c0Param;
this.ColorT1 = c1Param;
this.ColorT2 = c2Param;
}
}
And this is my serialize function for the observable collection of LedVals class (ListeLedTable) that I need to export:
public void SerializeLedTable(string filePathParam)
{
try
{
using (Stream mstream = File.Open(filePathParam + ".bin", FileMode.Create))
{
BinaryFormatter bin = new BinaryFormatter();
bin.Serialize(mstream, ListeLedTable);
}
}
}
The result is a file with binary values of the class properties and text description of the observable collection structure.
Is there a way to export properties values of a class like this ?
I can use a binaryWriter to write each property of my class in a loop, but I thought there might a simpler solution.
Thank you !
Use Marshal Techniques :
class Program
{
static void Main(string[] args)
{
LedVals ledVals = new LedVals();
int size = Marshal.SizeOf(ledVals); ;
IntPtr ptr = Marshal.AllocHGlobal(size);
byte[] buffer = new byte[Marshal.SizeOf(ledVals)];
Marshal.StructureToPtr(ledVals, ptr, true);
Marshal.Copy(ptr, buffer, 0, Marshal.SizeOf(ledVals));
Marshal.FreeHGlobal(ptr);
FileStream stream = File.OpenWrite("FILENAME");
BinaryWriter bWriter = new BinaryWriter(stream, Encoding.UTF8);
bWriter.Write(buffer);
}
}
[StructLayout(LayoutKind.Sequential)]
public class LedVals
{
public int ID { get; set; }
}
Using .NET binary serialization for any interop is a bad idea.
XML might be a better choice, but embedded devices usually lack the required horsepower to do any XML processing. I don't know what your embedded platform is, but there's a chance you could use Protocol Buffers (Protocol Buffers) to transfer binary data back and forth.
I have three classes:
[ProtoContract]
public class Message
{
[ProtoMember(1)]
public int MethodId { set; get; }
[ProtoMember(2)]
public CustomArgs Arguments { set; get; }
}
[ProtoContract]
public class CustomArgs
{
[ProtoMember(1)]
public int IntVal { set; get; }
[ProtoMember(2)]
public string StrVal { set; get; }
[ProtoMember(3)]
public CycleData Cd { set; get; }
}
[ProtoContract]
public class CycleData
{
[ProtoMember(1)]
public int Id { set; get; }
[ProtoMember(2, AsReference = true)]
public CycleData Owner { set; get; }}
So when I create objects then serialize and deserialize it the Arguments property stay null but orignal object have a value. The sample code is:
static void Main(string[] args)
{
CycleData cd = new CycleData()
{
Id = 5
};
cd.Owner = cd;
CustomArgs a = new CustomArgs()
{
IntVal = 5,
StrVal = "string",
Cd = cd
};
Message oldMsg = new Message()
{
MethodId = 3,
Arguments = a
};
Stream st = new MemoryStream();
ProtoBuf.Serializer.Serialize(st, oldMsg);
var newMsg = ProtoBuf.Serializer.Deserialize<Message>(st);
}
So newMsg.Arguments is null after deserialize. What i do wrong?
You have a simple error. Once you serialize/write to the memstream, the .Pointer remain at the end of the stream. Deserializing immediately after using on the same stream fails because there is nothing to read after that point. Just reset it:
using (Stream st = new MemoryStream())
{
ProtoBuf.Serializer.Serialize(st, oldMsg);
st.Position = 0; // point to start of stream
var newMsg = ProtoBuf.Serializer.Deserialize<Message>(st);
}
I also put the stream in a using block to dispose of it.
I'm currently testing protobuf-net (latest version), but intermittently I'm getting "Sub-message not read correctly" exception while deserializing. So far there's no apparent pattern to reproduce this error, and the data is always the same.
I googled this error and so far people reported this error only when dealing with big data (>20MB), which I'm not doing.
Can anyone point out whether this is a bug (and if it is, any possible solution to fix/circumvent this?), or am I missing some steps? Below is the code I'm using:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ProtoBuf;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const string message = "Cycle {0}: {1:N2} ms - avg: {2:N2} ms - min: {3:N2} - max: {4:N2}";
const int loop = 1000;
var counter = new Stopwatch();
var average = 0d;
var min = double.MaxValue;
var max = double.MinValue;
for (int i = 0;; i++)
{
var classThree = Create();
counter.Reset();
counter.Start();
Parallel.For(0, loop, j =>
{
using (var ms = new MemoryStream())
{
Serializer.Serialize(ms, classThree);
using (var ms2 = new MemoryStream(ms.ToArray()))
{
var des = Serializer.Deserialize<ClassThree>(ms2);
var aaa = des;
}
}
});
counter.Stop();
var elapsed = counter.Elapsed.TotalMilliseconds;
average += elapsed;
min = Math.Min(min, elapsed);
max = Math.Max(max, elapsed);
var currentAverage = average / (i + 1);
Console.Clear();
Console.WriteLine(message, i, elapsed, currentAverage, min, max);
Thread.Sleep(0);
}
}
private static ClassThree Create()
{
var classOne = new ClassSix()
{
// properties
p_i1 = -123,
p_i2 = 456,
p_l1 = -456,
p_l2 = 123,
p_s = "str",
p_f = 12.34f,
p_d = 56.78d,
p_bl = true,
p_dt = DateTime.Now.AddMonths(-1),
p_m = 90.12m,
p_b1 = 12,
p_b2 = -34,
p_c = 'c',
p_s1 = -21,
p_s2 = 43,
p_ts = new TimeSpan(12, 34, 56),
p_id = Guid.NewGuid(),
p_uri = new Uri("http://www.google.com"),
p_ba = new[] { (byte)1, (byte)3, (byte)2 },
p_t = typeof(ClassTwo),
p_sa = new[] { "aaa", "bbb", "ccc" },
p_ia = new[] { 7, 4, 9 },
p_e1 = EnumOne.Three,
p_e2 = EnumTwo.One | EnumTwo.Two,
p_list = new List<ClassFive>(new[]
{
new ClassFive()
{
i = 1,
s = "1"
},
new ClassFive()
{
i = 2,
s = "2"
}
}),
// fields
f_i1 = -123,
f_i2 = 456,
f_l1 = -456,
f_l2 = 123,
f_s = "str",
f_f = 12.34f,
f_d = 56.78d,
f_bl = true,
f_dt = DateTime.Now.AddMonths(-1),
f_m = 90.12m,
f_b1 = 12,
f_b2 = -34,
f_c = 'c',
f_s1 = -21,
f_s2 = 43,
f_ts = new TimeSpan(12, 34, 56),
f_id = Guid.NewGuid(),
f_uri = new Uri("http://www.google.com"),
f_ba = new[] { (byte)1, (byte)3, (byte)2 },
f_t = typeof(ClassTwo),
f_sa = new[] { "aaa", "bbb", "ccc" },
f_ia = new[] { 7, 4, 9 },
f_e1 = EnumOne.Three,
f_e2 = EnumTwo.One | EnumTwo.Two,
f_list = new List<ClassFive>(new[]
{
new ClassFive()
{
i = 1,
s = "1"
},
new ClassFive()
{
i = 2,
s = "2"
}
})
};
var classThree = new ClassThree()
{
ss = "333",
one = classOne,
two = classOne
};
return classThree;
}
}
public enum EnumOne
{
One = 1,
Two = 2,
Three = 3
}
[Flags]
public enum EnumTwo
{
One = 1,
Two = 2,
Three = 4
}
[ProtoContract, ProtoInclude(51, typeof(ClassSix))]
public class ClassOne
{
// properties
[ProtoMember(1)]
public int p_i1 { set; get; }
[ProtoMember(2)]
public uint p_i2 { set; get; }
[ProtoMember(3)]
public long p_l1 { set; get; }
[ProtoMember(4)]
public ulong p_l2 { set; get; }
[ProtoMember(5)]
public string p_s { set; get; }
[ProtoMember(6)]
public float p_f { set; get; }
[ProtoMember(7)]
public double p_d { set; get; }
[ProtoMember(8)]
public bool p_bl { set; get; }
[ProtoMember(9)]
public DateTime p_dt { set; get; }
[ProtoMember(10)]
public decimal p_m { set; get; }
[ProtoMember(11)]
public byte p_b1 { set; get; }
[ProtoMember(12)]
public sbyte p_b2 { set; get; }
[ProtoMember(13)]
public char p_c { set; get; }
[ProtoMember(14)]
public short p_s1 { set; get; }
[ProtoMember(15)]
public ushort p_s2 { set; get; }
[ProtoMember(16)]
public TimeSpan p_ts { set; get; }
[ProtoMember(17)]
public Guid p_id { set; get; }
[ProtoMember(18)]
public Uri p_uri { set; get; }
[ProtoMember(19)]
public byte[] p_ba { set; get; }
[ProtoMember(20)]
public Type p_t { set; get; }
[ProtoMember(21)]
public string[] p_sa { set; get; }
[ProtoMember(22)]
public int[] p_ia { set; get; }
[ProtoMember(23)]
public EnumOne p_e1 { set; get; }
[ProtoMember(24)]
public EnumTwo p_e2 { set; get; }
[ProtoMember(25)]
public List<ClassFive> p_list { set; get; }
// fields
[ProtoMember(26)]
public int f_i1 = 0;
[ProtoMember(27)]
public uint f_i2 = 0;
[ProtoMember(28)]
public long f_l1 = 0L;
[ProtoMember(29)]
public ulong f_l2 = 0UL;
[ProtoMember(30)]
public string f_s = string.Empty;
[ProtoMember(31)]
public float f_f = 0f;
[ProtoMember(32)]
public double f_d = 0d;
[ProtoMember(33)]
public bool f_bl = false;
[ProtoMember(34)]
public DateTime f_dt = DateTime.MinValue;
[ProtoMember(35)]
public decimal f_m = 0m;
[ProtoMember(36)]
public byte f_b1 = 0;
[ProtoMember(37)]
public sbyte f_b2 = 0;
[ProtoMember(38)]
public char f_c = (char)0;
[ProtoMember(39)]
public short f_s1 = 0;
[ProtoMember(40)]
public ushort f_s2 = 0;
[ProtoMember(41)]
public TimeSpan f_ts = TimeSpan.Zero;
[ProtoMember(42)]
public Guid f_id = Guid.Empty;
[ProtoMember(43)]
public Uri f_uri = null;
[ProtoMember(44)]
public byte[] f_ba = null;
[ProtoMember(45)]
public Type f_t = null;
[ProtoMember(46)]
public string[] f_sa = null;
[ProtoMember(47)]
public int[] f_ia = null;
[ProtoMember(48)]
public EnumOne f_e1 = 0;
[ProtoMember(49)]
public EnumTwo f_e2 = 0;
[ProtoMember(50)]
public List<ClassFive> f_list = null;
}
[ProtoContract]
public class ClassSix : ClassOne
{
}
[ProtoContract]
public class ClassTwo
{
}
[ProtoContract]
public interface IClass
{
[ProtoMember(1)]
string ss
{
set;
get;
}
[ProtoMember(2)]
ClassOne one
{
set;
get;
}
}
[ProtoContract]
public class ClassThree : IClass
{
[ProtoMember(1)]
public string ss { set; get; }
[ProtoMember(2)]
public ClassOne one { set; get; }
[ProtoMember(3)]
public ClassSix two { set; get; }
}
[ProtoContract]
public class ClassFour
{
[ProtoMember(1)]
public string ss { set; get; }
[ProtoMember(2)]
public ClassOne one { set; get; }
}
[ProtoContract]
public class ClassFive
{
[ProtoMember(1)]
public int i { set; get; }
[ProtoMember(2)]
public string s { set; get; }
}
}
Updated to rev. 669 and so far haven't encounter the error again. So I'm reporting this as fixed for now.