Overriding what's serialized on a per property basis

三世轮回 提交于 2019-12-14 02:29:29

问题


1)

I'm using protobuf-net to sync two objects through network, as I'm able to track changes made between two sync I can only send what's changed instead of the whole object.

So basically I'd like to override all nullable properties serialization based on a 'dirty' flag.

Is there a "nice" way to do that? (I can live with a thread local bool that modify the behavior of my properties getter, but well..).

2) (bonus question)

I went on and off of serializing the "real" object or a generic DTO object. But I got stucked on the DTO where typically I wanted to serializea KVPs of <int, object>, which would be the property id and its data, which can be as complexe as a Dictionary<string, Hashset<int>> for instance, which protobuf-net transports nicely if you know the type, but not if it's declared as an Object.

Some advises would be appreciated, I looked on the web and 2) seems to be a dead-end if I deal with nested generic collections made of basic types. 1) could work just fine if I can alter the behavior, but still, a DTO would have been perfect because it fullfills the need to transport changes and not "full object"...


回答1:


protobuf-net supports the standard ShouldSerialize* pattern; so for a property Foo, you can add:

private bool ShouldSerializeFoo() {
    return /* true to serialize, false to omit */
}

Note that on some platforms the method needs to be public, due to meta-programming / reflection permissions. Note also that for nullable properties, you can also simply return null if you don't want them to be serialized.

I don't really understand what your second question is trying to do; can you clarify?




回答2:


Re: #2 Bonus Question

I do not completely understand, but parts of it sounds quite similar to something I implemented as a workaround for the issue of serializing obfuscated Types. The solution also allows a way to serialize things like Point, Size, Font and Image.

Rather than serializing the Class Type, the "trick" I used was to extract the original property type and string value to a TypeValue pair primitive. These are stored as a Dictionary<T, TypeValuePair> which can be used to reconstruct a class object. Protobuf-net, then just has to serialize a regular Dictionary.

The result for some class types can be a Dictionary<String, <Dictionary<T, TypeValuePair>> which sounds similar to something you mentioned. You do end up performing type conversions, but it is not difficult (VB, because I can't compose C# on the fly nearly as well or as fast):

' from TVP ctor
Sub New(v As Object)       ' thing to convert
    ' convert whatever to string:
    Dim conv As TypeConverter = TypeDescriptor.GetConverter(v.GetType)

    _value = conv.ConvertToInvariantString(v)
    ...

   ' original type is stored as an enum  (eg GenericTypes.Font)
   _type = GenericConverter.GetGenericTypeCode(v)
End Sub

There is a little more to it, a few things like Enum and Bitmap have to be handled differently, but only slightly. Create a collection of these for your Class/Type and submit that to ProtoBuf.


I split up the process to convert the string property values back to the proper type. The TVP does the actual conversion, but the consumer does the casting. The TValue property of TVP recreates the original data type, but returns an Object:

   Public ReadOnly Property TValue As Object
      Get
         Dim R As Object = Nothing

         Select Case _type
             Case GenericTypes.TInt32
                R = Int32.Parse(_value) 

            Case GenericTypes.TFont
                R = TypeDescriptor.GetConverter(GetType(System.Drawing.Font)) _
                          .ConvertFromInvariantString(_value)

            Case etc
         End Select

         Return R
      End Get
    End Property

I have a class to house the Dictionary<T, TypeValuePair> and provide some functions like this one:

    Public Function GetTItem(Of TT)(key As T) As TT
        If _col.ContainsKey(key) Then
            ' cast the object return to requested Type (TT)
            Return CType(_col(key).TValue, TT)
        Else
            Return Nothing
        End If
    End Function

T is just the T in Dictionary<T, TypeValuePair> to allow the collection to use a string or int key. TT could be passed to TValue, if you prefer. GetTItem acts to cast it back to the requested type by the original class which does know the Type of its own properties, so there are no dubious assumptions being made:

' get item enum.N and cast to Int32:
_ziggys = myThing.GetTItem(Of Int32)(PropIndexEnum.Ziggys)

As I said, I am not sure this will help, but your situation sounded very familiar. Any class object you want to serialize just needs to be able to cough up its constituent properties in serializable form rather than trying to submit Object to the serializer. Of course, this is all overhead which might be too much in high volume situations, but it can work.



来源:https://stackoverflow.com/questions/23802063/overriding-whats-serialized-on-a-per-property-basis

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!