反序列化 JSON 具有混合值 System.Text.JSON 的数组

Deserialize JSON array which has mixed values System.Text.JSON

我正在尝试创建一个在 .net core 3.1 中呈现的页面,该页面基于 JSON 呈现页面。

如何反序列化 post 末尾的 JSON?

我试图反序列化它,但是它不起作用,因为我丢失了每个组件的数据, 因为页面 class 有一个 List<Component> - 但我需要它是一个包含不同组件的列表。

页面模型:

public class Page
    {
        public int id { get; set; }
        public string pagename { get; set; }
        public string metatitle { get; set; }
        public string metadescription { get; set; }
        public string created_at { get; set; }
        public string updated_at { get; set; }
        public List<Component> components { get; set; }
    }

    public class Pages
    {
        public List<Page> pages { get; set; }
    }

组件模型:

public class Component
    {
        public string component { get; set; }
        public int id { get; set; }
    }

一个组件:

public class Title : Component
    {
        public string component { get; set; }
        public int id { get; set; {
        public string titletext { get; set; }
    }

这是JSON:

{
      "id":1,
      "pagename":"home",
      "metatitle":"no title",
      "metadescription":"no meta",
      "created_at":"2020-05-31T16:35:52.084Z",
      "updated_at":"2020-05-31T16:35:52.084Z",
      "components":[
         {
            "component":"components.titletext",
            "id":1,
            "titletext":"hello"
         },
         {
            "component":"components.section",
            "id":2,
            "titletext":"hello",
            "descriptiontext":"its a beatiful day in lost santos",
            "buttonlink":"/go"
         },
         {
            "component":"components.cta",
            "id":3,
            "sectiontitle":"hello",
            "buttonlink":"/go",
            "buttontext":"click me"
         }
      ]
   }

如果您不想像这样将所有属性添加到 Component class:

public class Component
{
    public string component { get; set; }
    public int id { get; set; }
    public string titletext { get; set; }
    public string sectiontitle { get; set; }
    public string buttonlink { get; set; }
    public string descriptiontext { get; set; }
}

例如,您将需要编写自定义 JsonConverter(不是非常高效的实现,但可以与您的 json 一起使用,并且您不需要手动解析每个字段):

public class ComponentConverter : JsonConverter<Component>
{
    public override Component Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using (var doc = JsonDocument.ParseValue(ref reader))
        {
            var type = doc.RootElement.GetProperty(@"component").GetString();
            switch(type)
            {
                case "components.titletext": 
                    return JsonSerializer.Deserialize<Title>(doc.RootElement.GetRawText());
                // other types handling
                default: return JsonSerializer.Deserialize<Component>(doc.RootElement.GetRawText());
            }
        }
    }

    public override void Write(Utf8JsonWriter writer, Component value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

public class Component
{
    public string component { get; set; }
    public int id { get; set; }
}

public class Title : Component
{
    public string titletext { get; set; }
}

以及用法示例:

var json = @"[
     {
        ""component"":""components.titletext"",
        ""id"":1,
        ""titletext"":""hello""
     },
     {
""component"":""components.section"",
        ""id"":2,
        ""titletext"":""hello"",
        ""descriptiontext"":""its a beatiful day in lost santos"",
        ""buttonlink"":""/go""
     },
     {
""component"":""components.cta"",
        ""id"":3,
        ""sectiontitle"":""hello"",
        ""buttonlink"":""/go"",
        ""buttontext"":""click me""
     }
  ]";
var deserializeOptions = new JsonSerializerOptions();
deserializeOptions.Converters.Add(new ComponentConverter());
JsonSerializer.Deserialize<List<Component>>(json, deserializeOptions).Dump();

也不要将此转换器用作 JsonConverterAttribute 的参数,因为它会在 Whosebug 中结束。

如果您 want/need 完全分离不相关的 类 您可以使用不使用转换器的技术:

var ays = new List<A>();
var bees = new List<B>();

using var doc = JsonDocument.Parse(json);
foreach (var block in doc.RootElement.EnumerateArray())
{
    switch (block.GetProperty("component").GetString())
    {
        case "typeA": ays.Add(Deserialise<A>(block)); break;
        case "typeB": bees.Add(Deserialise<B>(block)); break;
        // ... case 
        //default: ...
    }
}

var composite = new
{
    As = ays,
    Bs = bees 
};

// This is OK, but if you need to speed it up, please have a look at
// 
static T Deserialise<T>(JsonElement e) => JsonSerializer.Deserialize<T>(e.GetRawText(), options: new JsonSerializerOptions { PropertyNameCaseInsensitive = true });