HomeDev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Deserialize Content Management API

Describes the deserialization process in Optimizely Content Management API and how to customize it.

When receiving a request, the Optimizely Content Management API first deserializes the data from the request body into models and then handles the request.

The Content Management API defines some input models (ContentApiCreateModel, ContentApiPatchModel), which plays a role as data transform objects for deserialization. Data is transformed into those models before being converted to IContent and saved to the database. For example, the diagram below illustrates the scenario of creating a content item.

The workflow follows the following steps:

  • You request to Post a content endpoint with an input model in JSON format.
  • The server accepts the request and transforms the JSON data into ContentApiCreateModel. Some metadata fields are mapped automatically, and the custom properties are converted to PropertyModel.
  • Before saving the content, the ContentApiCreateModel is migrated to the IContent. The list of PropertyModel is converted to the PropertyData, then added to the IContent.Property.

The flow is the same for all endpoints.

Customize the deserialization

If you define a custom PropertyData in your model, you need to register a custom PropertyModel and a custom converter to determine which PropertyData corresponds to which PropertyModel. For example, you have a custom property PropertyStringList, which inherits the PropertyLongString:

[PropertyDefinitionTypePlugIn(Description = "A property for list of strings", DisplayName = "String List")]
public class PropertyStringList: PropertyLongString {
  protected String Separator = "\n";

  public String[] List {
    get {
      return (String[]) Value;

  public override Type PropertyValueType {
    get {
      return typeof (String[]);

  public override object SaveData(PropertyDataCollection properties) {
    return LongString;

  public override object Value {
    get {
      var value = base.Value as string;

      if (value == null) {
        return null;

      return value.Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
    set {
      if (value is String[]) {
        var s = String.Join(Separator, value as String[]);
        base.Value = s;
      } else {
        base.Value = value;

For Content Management API to handle this custom property, you need to register a corresponding StringListPropertyModel:

public class StringListPropertyModel: PropertyModel < IList < string > , PropertyStringList > {
  internal StringListPropertyModel(): this(new PropertyStringList()) {}

  public StringListPropertyModel(PropertyStringList propertyStringList): base(propertyStringList) {
    Value = propertyStringList.List;

And a StringListPropertyDataValueConverter that implements the IPropertyDataValueConverter:

[ServiceConfiguration(typeof (IPropertyDataValueConverter))]
[PropertyDataValueConverter(new Type[] {
  typeof (StringListPropertyModel)
internal class StringListPropertyDataValueConverter: IPropertyDataValueConverter {
  ///<inheritdoc />
  public object Convert(IPropertyModel propertyModel, PropertyData propertyData) {
    if (propertyModel is null) {
      throw new ArgumentNullException(nameof(propertyModel));

    return ((StringListPropertyModel) propertyModel).Value;

Data format

Flatten configuration controls whether the deserialized representation of properties should only include the value of all properties rather than both value and property type (for POST, PUT, or PATCH requests). This should be carefully noted because it impacts the data format sent to endpoints.

For information about the flattened model, see Flatten.