Table of Contents

Custom Data Serialization in 2sxc 17+

Serialization is usually converting data to JSON in WebAPI scenarios. This guide should explain how it works, and what you can do to modify the behavior.

Basics

By default, all classes are serialized using the default JSON serializer. This means that properties etc. are simply serialized as expected, typically with camelCase.

Example for a Product Class

class Product {
  public int Id { get; set; }
  public Guid Guid { get; set; }
  public string Name { get; set; }
  public decimal Price { get; set; }
  public Category Category { get; set; }
  public IEnumerable<Tag> Tags { get; set; }
}

class Category {
  public int Id { get; set; }
  public Guid Guid { get; set; }
  public string Name { get; set; }
  public string Description { get; set; }
}

class Tag {
  public int Id { get; set; }
  public Guid Guid { get; set; }
  public string Name { get; set; }
}

would be serialized as

{
  "id": 123,
  "guid": "12345678-1234-1234-1234-1234567890ab",
  "name": "My Product",
  "price": 12.34,
  "category": {
    "id": 456,
    "guid": "12345678-1234-1234-1234-1234567890ab",
    "name": "My Category",
    "description": "My Description"
  },
  "tags": [
    {
      "id": 789,
      "guid": "12345678-1234-1234-1234-1234567890ab",
      "name": "Tag 1"
    },
    {
      "id": 101112,
      "guid": "12345678-1234-1234-1234-1234567890ab",
      "name": "Tag 2"
    }
  ]
}

This is the standard behavior, and there are various reasons why you may want to change this.

Total Control Strategy: Custom Conversion DTO Classes

This strategy basically creates DTO (Data Transfer Objects) which are then serialized. This is the most flexible, but also the most work.

This example assumes you don't want to include the details of the Tag/Category, but mainly just the reference - because you would want to put the details in a separate call.

class ProductDto {
  public ProductDto(Product product) {
    Id = product.Id;
    // Guid = product.Guid;
    Name = product.Name;
    Price = product.Price;
    Category = new CategoryDto(product.Category);
    Tags = product.Tags.Select(t => new TagDto(t));
  }
  public int Id { get; set; }
  // public Guid Guid { get; set; }
  public string Name { get; set; }
  public decimal Price { get; set; }
  public CategoryDto Category { get; set; }
  public IEnumerable<TagDto> Tags { get; set; }
}

class CategoryDto {
  public CategoryDto(Category category) {
    Id = category.Id;
    // Guid = category.Guid;
    Name = category.Name;
    // Description = category.Description;
  }
  public int Id { get; set; }
  // public Guid Guid { get; set; }
  public string Name { get; set; }
  // public string Description { get; set; }
}

class TagDto {
  public TagDto(Tag tag) {
    Id = tag.Id;
    // Guid = tag.Guid;
    Name = tag.Name;
  }
  public int Id { get; set; }
  // public Guid Guid { get; set; }
  public string Name { get; set; }
}

This would then serialize to JSON as

{
  "id": 123,
  "name": "My Product",
  "price": 12.34,
  "category": {
    "id": 456,
    "name": "My Category"
  },
  "tags": [
    {
      "id": 789,
      "name": "Tag 1"
    },
    {
      "id": 101112,
      "name": "Tag 2"
    }
  ]
}

Simpler JsonIgnore Strategy

If you just want to ignore some properties, you can use the [JsonIgnore] attribute. This takes two steps, because you can't use it on the original class, but you can use it on a derived class.

Assume the following /AppCode/Data/Product.Generated.cs Copilot Auto-Generated file, which you shouldn't touch:

namespace AppCode.Data
{
  public partial class Product : ZagProduct { }
}

namespace AppCode.Data.AutoGenerated
{
  public class ZagProduct: Custom.Data.CustomItem
  {
    public int Id { get; set; }
    public Guid Guid { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public Category Category { get; set; }
    public IEnumerable<Tag> Tags { get; set; }
  }
}

And this would be the manually created /AppCode/Data/Product.cs file:

using System.Text.Json.Serialization;
namespace AppCode.Data
{
  // This would be the class we can modify
  public partial class Product : ZagProduct {
    // ignore the guid for JSON
    [JsonIgnore]
    public new Guid Guid => base.Guid;

    // ignore the category for JSON - but only if null
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public new Category Category => base.Category;

    // ignore the tags for JSON
    [JsonIgnore]
    public new IEnumerable<Tag> Tags => base.Tags;

    // custom tag info, but just the reference info
    // this would be serialized as an array of { id: 123, name: "Tag 1" }
    public object TagReferences => base.Tags.Select(t => new { t.Id, t.Name });
  }
}

This would serialize to JSON like this:

{
  "id": 123,
  "name": "My Product",
  "price": 12.34,
  "category": {
    "id": 456,
    "guid": "12345678-1234-1234-1234-1234567890ab",
    "name": "My Category",
    "description": "My Description"
  },
  "tagReferences": [
    {
      "id": 789,
      "name": "Tag 1"
    },
    {
      "id": 101112,
      "name": "Tag 2"
    }
  ]
}

History

  • Introduced in v17.03