Skip to content

Serialization

Nathan Baulch edited this page Jun 16, 2014 · 1 revision

This library supports the serialization of POCOs, anonymous types, dictionaries and dynamic (DLR) objects to and from the JSON and Atom formats. The untyped SDataResource and SDataCollection types are the default in-memory data containers and JSON is the default wire format.

Note: The DLR was introduced .NET 4.0 so the .NET 2.0 and 3.5 versions of this library don't support dynamic resources.

The following example reads a contact using each of the four typing approaches in parallel and outputs the full name returned for each.

var client = new SDataClient("http://example.com/sdata/slx/dynamic/-/")
    {
        UserName = "admin",
        Password = "password"
    };
var task1 = client.GetAsync<Contact>("CDEMOA000001");
var task2 = client.GetAsync("CDEMOA000001", "contacts");
var task3 = client.GetAsync<dynamic>("CDEMOA000001", "contacts");
var task4 = client.GetAsync("CDEMOA000001", "contacts",
                            new {FullName = default(string)});
var contact1 = await task1;
var contact2 = await task2;
var contact3 = await task3;
var contact4 = await task4;
Console.WriteLine(contact1.FullName);
Console.WriteLine(contact2["FullName"]);
Console.WriteLine(contact3.FullName);
Console.WriteLine(contact4.FullName);

CRUD requests involving POCO resources don't need to specify a path if the POCO type is decorated with the SDataPath attribute.

POCO Identifier Naming

Sometimes client identifier names differ from those used on the server. For example, the server might use camel case while the client POCO model uses pascal case, a generally accepted naming convention used in .NET. A combination of convention based and attribute based naming can be used to map between POCO identifiers and their serialized names.

The convention approach uses an INamingScheme object that applies a rule to all identifiers such as pascal case or camel case. This helps to keep the client POCO model free from excessive boilerplate attributes which can be tedious to create and maintain. The following example uses the camel case naming scheme while fetching an attachment and outputs the file name.

client.NamingScheme = NamingScheme.CamelCase;
var attachment = await client.GetAsync<Attachment>("EDEMOA000001");
Console.WriteLine(attachment.FileName);

The attribute approach involves explicitly declaring identifier names by applying data contract attributes to classes and properties.

Note: Data contracts were introduced in .NET 3.5 so the equivalent XML serialization attributes must be used in the .NET 2.0 version of this library.

The following example fetches a POCO group object which uses a DataMember attribute to declare the DisplayName property name.

var group = await client.GetAsync<Group>("PDEMOA000001");
Console.WriteLine(group.DisplayName);

[SDataPath("groups")]
public class Group
{
    [DataMember(Name = "displayName")]
    public string DisplayName { get; set; }
}

Unlike Atom, resource names aren't included in JSON payloads so there's no need to specify a type name using the DataContract attribute when using this format.

These two naming approaches can be used in tandem, where the naming scheme covers the majority of cases and attributes are used for any exceptions. Alternatively the data model can be kept clear of any attributes by creating a custom naming scheme that maintains the naming rules and exceptions in a centralized location.

Properties can be ignored by applying the IgnoreDataMember attribute.

Protocol Properties

Protocol properties such as $key can be mapped to POCO properties using the SDataProtocolProperty attribute. If the default constructor is used then the mapped protocol property will be inferred from the property name. The following example POCO explicitly maps its Id property to $key and implicitly maps its Descriptor and ETag properties to $descriptor and $etag respectively.

[SDataPath("contacts")]
public class Contact
{
    [SDataProtocolProperty(SDataProtocolProperty.Key)]
    public string Id { get; set; }
    [SDataProtocolProperty]
    public string Descriptor { get; set; }
    [SDataProtocolProperty]
    public string ETag { get; set; }

    public override string ToString()
    {
        return Descriptor ?? Id ?? base.ToString();
    }
}

Creating a property mapped to $etag is necessary to enable concurrency handling during resource updates. This property is used to keep a copy of the ETag received when the resource was last read.

Structure Inference

There are a few scenarios in JSON and Atom where the client is forced to guess the correct structure of nested objects and collections in the payload. One notable scenario is the difference between resource collections and simple arrays in JSON. The default structure when serializing resource collections is to nest the array in the $resources property of a wrapper object. If the server isn't expecting this wrapper object then the JsonSimpleArray attribute can be applied to the collection property and a simple array will be written instead. Similarly the SDataCollection type has a static Create method with a simple array flag when working with untyped data.

Clone this wiki locally