Santhosh Shanmugam
Santhosh Shanmugam

Reputation: 31

Issue with WCF type serialization

I'm facing a strange issue with WCF serialization. My project has a type named RefEntity which has a property of another type named RefDomain. A WCF service defines two methods, one takes type RefEntity as parameter, and another takes type RefDomain as parameter.

The method with the RefDomain parameter doesn't seem to serialize the object as all the properties have null value on the service side. Whereas, the method with RefEntity parameter (and RefDomain as a member) works as expected (RefDomain member is serialized as expected).

What am I missing here?

public class RefDomain
{
    private string name;
    private RefDomain parent;
    private Type entityType;

    public string Name
    {
        get => this.name;
        protected set => this.name = value;
    }

    public RefDomain Parent
    {
          get => this.parent;
          protected set => this.parent = value;
    }

    public Type RefEntityType
    {
          get => this.entityType;
          protected set => this.entityType = value;
    }
}

Upvotes: 0

Views: 331

Answers (2)

dbc
dbc

Reputation: 116544

Your properties have protected setters, and DataContractSerializer will only serialize properties that are fully public, unless marked with data contract attributes.

Once you apply the necessary [DataContract] and [DataMember] attributes, you will discover a second problem, namely that DataContractSerializer cannot serialize values of type System.Type. (Demo fiddle #1 here). To serialize your Type RefEntityType property, you will need to use some sort of surrogate for Type that can be serialized, for instance a string.

Thus the following version of RefDomain can be serialized via the data contract serializer:

public class RefDomain
{
    private string name;
    private RefDomain parent;
    private Type entityType;

    [DataMember]
    public string Name
    {
        get => this.name;
        protected set => this.name = value;
    }

    [DataMember]
    public RefDomain Parent
    {
          get => this.parent;
          protected set => this.parent = value;
    }

    public Type RefEntityType
    {
          get => this.entityType;
          protected set => this.entityType = value;
    }

    [DataMember(Name = "RefEntityType")]
    string RefEntityTypeString 
    {
        get
        {
            return RefEntityType?.FullName;
        }
        set
        {
            // Note that simply deserializing a type supplied over the wire will make your application vulnerable to type injection attacks,
            // in which an attacker tricks you into constructing a type that effects an attack when constructed, deserialized or disposed.  
            // This is a known vulnerability with Json.NET's TypeNameHandling.None.  See for details
            // https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf
            // https://stackoverflow.com/questions/39565954/typenamehandling-caution-in-newtonsoft-json/
            // https://stackoverflow.com/questions/49038055/external-json-vulnerable-because-of-json-net-typenamehandling-auto
            var type = value == null ? null : Type.GetType(value);
            // Check the type against a whitelist here?
            RefEntityType = type;
        }
    }
    
    // Your class was had no way to set the protected properties, so I added a parameterized constructor.
    public RefDomain() { }
    public RefDomain(string name, RefDomain parent, Type entityType) => (this.name, this.parent, this.entityType) = (name, parent, entityType);
}

Notes:

  1. Deserializing a type supplied over the wire will make your application vulnerable to attack gadget deserialization attacks, in which an attacker tricks your app into constructing a type that effects an attack when constructed, deserialized or disposed. This is a known vulnerability with Json.NET's TypeNameHandling. See for details:

    If your app ever constructs an instance of your RefEntityType, you app may become vulnerable to such attacks. To prevent this, you could check the deserialized type against a whitelist of allowed types.

  2. In your question, you ask why the partially public properties of RefDomain are serialized when it is serialized as a member of some other root object RefEntity. There is not enough information in your question to explain why this is happening. Perhaps the endpoint where the RefEntity is serialized is using a has subclassed RefDomain, or uses a serialization surrogate, or has set DataContractSerializerSettings.SerializeReadOnlyTypes = true.

    But regardless of why partially public properties RefDomain properties are serialized when inside RefEntity, if you want those properties serialized in all contexts, it is correct and sufficient to mark RefDomain with [DataContract] and the properties with [DataMember]

Demo fiddle #2 here.

Upvotes: 1

Jiayao
Jiayao

Reputation: 568

As far as I know, user-defined types used in wcf need to be tagged with the Data Contract attribute, and members need to be tagged with the Data Member attribute.

You can either mark RefDomain as a Data Contract attribute or create a separate class to hold data from RefDomain and pass it through wcf.

Upvotes: 0

Related Questions