0
votes

How to serialize a generic(T) object which can hold any type of data (int / string / DateTime) using Protobuf-net. Following is my code

[ProtoContract]
public class E1DataRow
{
  [ProtoMember(1)]
  public List<NameValue> NameValues { get; set; }

  public E1DataRow()
  {
    NameValues = new List<NameValue>();
  }

  public void AddNameValue(string name, object obj, Type type)
  {
      NameValues.Add(new NameValue { Name = name, Value = obj, ValueType = type });
  }
}


[ProtoContract]
public class NameValue
{
  [ProtoMember(1)]
  public string Name { get; set; }
  [ProtoMember(2)]
  public object Value { get; set; }
  [ProtoMember(3)]
  public Type ValueType { get; set; }
}

serialization code

var e1DataRows = new List<E1DataRow>();
/*
Code to add Data rows to e1DataRows
e1DataRow.AddNameValue(column.ColumnName, value, column.TypeOfColumn);
*/
using (var stream = File.OpenWrite(path))
{
  Serializer.Serialize(stream, e1DataRows);
}
[ProtoMember(2, DynamicType = true)]
public object Value { get; set; }

Above code throws following error (DynamicType = true) ProtoMemberAttribute.DynamicType' is obsolete: 'Reference-tracking and dynamic-type are not currently implemented in this build; they may be reinstated later; this is partly due to doubts over whether the features are adviseable, and partly over confidence in testing all the scenarios (it takes time; that time hasn't get happened); feedback is invited'

It would be great if you can help with how to serialize a List using Protobug-net. Thanks...

2
Looks like Protobuf-net handles generic types quite well. Try converting public class NameValue to public class NameValue<T> or use the existing KeyValuePair<TKey,TValue> instead.DerSchnitz
@schnitz77 Thanks for your comments. Problem with public class NameValue<T> is that then I can not have List<NameValue> with a different type (i guess). KeyValuePair<TKey, TValue> is a good idea but would it cost me hashing operation, every time i add a new key. Basically, I am trying to convert a dataset (SqlReader) into a simple POCO class for serialization.Ojas Maru

2 Answers

1
votes

You may want to look at https://github.com/dotarj/protobuf-net-data - this isn't affiliated with protobuf-net (different authors etc), but it uses protobuf-net to perform serialization of DataTable and data-readers, so it might do what you want ready-made.


As for implementing it yourself:

protobuf-net does not support object (or dynamic, which is just a fancy way of spelling object), fundamentally. There are ways of working around this, essentially similar to the oneof handling in protobuf - i.e. something like (in protobuf terms):

message Foo {
    oneof payload {
        string payload_string = 1;
        bool payload_bool = 2;
        int32 payload_int32 = 3;
        float payload_float = 4;
        // etc
    }
}

This is pretty easy to put together in protobuf-net thanks to "conditional serialization", which means you could do something like:

public object Value { get; set; }

[ProtoMember(1)]
public string ValueString
{
    get => (string)Value;
    set => Value = value;
}
public bool ShouldSerializeValueString()
    => Value is string;

[ProtoMember(2)]
public string ValueBoolean
{
    get => (bool)Value;
    set => Value = value;
}
public bool ShouldSerializeValueBoolean()
    => Value is string;

// etc
0
votes

If on c# >= 4 you might want to try the following:

[ProtoContract]
public class E1DataRow
{
    [ProtoMember(1)]
    public List<NameValue<dynamic>> NameValues { get; set; }

    public E1DataRow()
    {
        NameValues = new List<NameValue<dynamic>>();
    }

    public void AddNameValue(string name, dynamic obj)
    {
        NameValues.Add(new NameValue<dynamic> { Name = name, Value = obj });
    }
}

public class NameValue<T>
{
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public T Value { get; set; }
    [ProtoMember(3)]
    public Type ValueType { get { return Value.GetType(); } }
}

Not sure if Protobuf-net likes the List<NameValue<dynamic>>, can't test it as of now.

ProtoMember(3) at ValueType possibly is not necessary as of being readonly anyways.