Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically ignore data members from getting serialized

We have an existing WCF service which uses several DataContracts. We want to modify the serialization based on the device, so that when accessed from mobile devices, the service should serialize only some important data members(not all)

We have 2 options here

  1. Create separate operation and data contracts for different types of devices
  2. Mess with the actual xml serialization and suppress creating unnecessary elements based on the device

We don't want to go with the first option since it introduces a lot of redundant code problems in the future

Small research showed that we need to use IXmlSerializable and override the readXML() and writeXML() methods. But at the same time, I have seen somewhere that DataContract and IXmlSerializable should not be used together

Any example to mess with actual serialization is greatly appreciated .

[DataContract]
public class TokenMessage
{
    string tokenValue;
    string extraValue;
    [DataMember]
    public string Token
    {
        get { return tokenValue; }
        set { tokenValue = value; }
    }
    [DataMember]
    public string Extra 
    {
        get { return extraValue; }
        set { extraValue = value; }
    }
}

Now when i access the service which returns a typical TokenMessage data contract, from a mobile device, i don't want the "Extra" data member to be serialized i.e. When I supply a different argument to the operation contract, it should be able to serialize some/all the data members(depending on the action)

PS: For now please ignore the device detection part. Lets assume we have an argument in the operation contract, which helps us identify the device

like image 931
Surya KLSV Avatar asked Jan 13 '14 12:01

Surya KLSV


2 Answers

I'm not convinced that some variant of @Pranav Singh's answer isn't a better design, but that's not your question...

As you mentioned in a comments attributes in .NET are static by design. This means dynamically adding/removing [DataMember] isn't a good option. It is possible. There are options like using Reflection.Emit to recreate the instance with the meta data changes (see all the answers to Can attributes be added dynamically in C#?) but all of those routes are complicated.

I see two reasonable options:

1) Implement an IParameterInspector for the service. In the AfterCall() method you could inspect and alter the parameters being returned to the client before they are serialized. There is some work to use reflection to dynamically determine the parameter types and set their values, but its not complicated. This is the better design that enables reuse of the behavior across many contracts or services. Carlos Figueira's blog is the best source for WCF extension examples.

2) Use the [OnSerializing] and [OnSerialized] events. In the [DataContract] you could temporarily alter what the properties are returning during serialization. The events are actually designed to enable initialization and as such this solution is a bit of a hack. This solution is also not thread safe. But it does keep the code contained to the DataContract class and solves the problem quickly (and I think you are looking for quick).

Solution #2 mights look something like:

[DataContract]
public class TokenMessage
{
    string tokenValue;
    string extraValue;

    bool enableExtraValue = true;

    [DataMember]
    public string Extra 
    {
        get { 
                if (enableExtraValue) 
                      return extraValue;
                return null; 
            }
        set { extraValue = value; }
    }

    [OnSerializing()]
    internal void OnSerializingMethod(StreamingContext context)
    {
        enableExtraValue = false;
    }

    [OnSerialized()]
    internal void OnSerializedMethod(StreamingContext context)
    {
        enableExtraValue = true;
    }
}

Solution #2 is a quick fix (which is what I think you are looking for).

Solution #1 is the better design.

like image 195
ErnieL Avatar answered Oct 30 '22 16:10

ErnieL


Try using IgnoreDataMemberAttribute

like image 41
PushCode Avatar answered Oct 30 '22 14:10

PushCode