Search code examples
c#.net-6.0system.text.json

Serialize and Deserialize generic objects in ASP.NETCore .NET 6 C# App


This is my requirement:

A Page object, it has a string property Name and a generic list of Field object. A Field object has the following public string field Name and a public generic field Value. The Calue can be of type string, int (..and more in the feature). When it's a int value, it should have a min and max value of type int. When it's a string value, it should have a maxLength value of type int.

I would like to serialize this Page object in Json, and deserialize this back to Page object.

This is what I have tried:

public abstract class Field
{
    public string Name { get; set; }
    public abstract FieldType Type { get; }
}

public enum FieldType
{
    Text,
    Number,
}

public class StringField : Field
{
    public override FieldType Type => FieldType.Text;

    public string Value { get; set; }
    public int? MaxLength { get; set; }
}

public class NumberField : Field
{
    public override FieldType Type => FieldType.Number;

    public int Value { get; set; }
    public int? Min { get; set; }
    public int? Max { get; set; }
}


public class Page
{
    public string Name { get; set; }
    public IEnumerable<Field> Fields { get; set; }
}

[ApiController]
[Route("[controller]")]
public class DemoController : ControllerBase
{
    private readonly ILogger<DemoController> _logger;

    public DemoController(ILogger<DemoController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IActionResult Get()
    {
        var page = new Page
        {
            Name = "TestPage",
            Fields = new List<Field>
            {
                new StringField
                {
                    Name = "Textfield",
                    MaxLength = 10,
                    Value = "DefaultValue"
                },
                new NumberField
                {
                    Name = "NumberField",
                    Value = 5,
                    Min = 0,
                    Max = 10,
                }
            }
        };

        return Ok(page);
    }

    [HttpPost]
    public ActionResult Post([FromBody] Page value)
    {
        return Ok(value);
    }

But the Value are not serialized and de-serialized correctly. I'm free to choose any nuget package and change de model.

This is the output of the Get method:

{
    "name": "TestPage",
    "fields": [
        {
            "name": "Textfield",
            "type": 0
        },
        {
            "name": "NumberField",
            "type": 1
        }
    ]

}


Solution

  • As mentioned in the comment by @JonasH, if you're using System.Text.Json you'll need to use Polymorphic serialization to preserve the type information.

    [JsonDerivedType(typeof(StringField), typeDiscriminator: "StringField")]
    [JsonDerivedType(typeof(NumberField), typeDiscriminator: "NumberField")]
    public abstract class Field
    {
        public string Name { get; set; }
        public abstract FieldType Type { get; }
    }
    

    Here's a fiddle that illustrates this in action: https://dotnetfiddle.net/fm7Cdv

    An extra field "$type" is added to your JSON that stores what type the object is. This is slightly redundant with your Type property, so you could consider removing it.

    {
      "name": "TestPage",
      "fields": [
        {
          "$type": "StringField",
          "type": 0,
          "value": "DefaultValue",
          "maxLength": 10,
          "name": "Textfield"
        },
        {
          "$type": "NumberField",
          "type": 1,
          "value": 5,
          "min": 0,
          "max": 10,
          "name": "NumberField"
        }
      ]
    }