13

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

Surya KLSV
  • 1,260
  • 4
  • 18
  • 26

4 Answers4

3

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.

Community
  • 1
  • 1
ErnieL
  • 5,773
  • 1
  • 23
  • 27
1

Try using IgnoreDataMemberAttribute

PushCode
  • 1,419
  • 3
  • 15
  • 31
1

There is a approach, but I think this will require extra DataContract to be generated but still no need for separate operation and data contracts for different types of devices. It can classic implementation to run-time polymorphism. I am just giving idea:

Say you have a generic DataContract like :

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

}

Other device specific contracts can inherit TokenMessage as base class like:

[DataContract]
public class Extra:TokenMessage
{
  [DataMember]
  public string Extra 
  {
    get ;set;
  }
}

[DataContract]
public class Extra2:TokenMessage
{
  [DataMember]
  public string Extra2 
  {
    get ;set;
  }
}

Now at run-time as you say you know an argument in the operation contract, which helps us identify the device. Say based on device type, you can instantiate base class with derived class like:

TokenMessage tm= new Extra();

OR

TokenMessage tm= new Extra2();

So at run-time you will decide which device contract will be part of genric response.

Note: Adding KnownType will generate the separate xsd within wsdl for all known types within base class, but saves serialization for data at run-time as this should depend on actual inheritance chosen.

Pranav Singh
  • 17,079
  • 30
  • 77
  • 104
-2

In your model add a property 'ShouldSerializeYOUR_PROPERTY_NAME', set it to false when you do not want the property serialized. See more here: http://msdn.microsoft.com/en-us/library/system.windows.dependencyobject.shouldserializeproperty(v=vs.110).aspx

lcryder
  • 486
  • 2
  • 8
  • You mean to say - "add ShouldSerializeToken and ShouldSerializeExtra properties and just set them to false? " Will WCF automatically detect this ?? – Surya KLSV Jan 13 '14 at 13:35
  • No luck with the above method :( – Surya KLSV Jan 13 '14 at 13:41
  • 2
    `ShouldSerialize` relates to WPF, I don't know how it would help with your WCF service. – Sven Grosen Jan 13 '14 at 13:42
  • 1
    setting EmitDefaultValue to false and explicitly setting the properties to default values is one way of stopping data members from getting serialized. However my class members are not just basic data types. They have a lot of custom classes as properties which are getting serialized – Surya KLSV Jan 13 '14 at 13:48