.net 反序列化复杂JSON结构

daupos2t  于 2023-05-01  发布在  .NET
关注(0)|答案(2)|浏览(153)

我正在开发一个从API获取数据的C#应用程序。来自API的响应具有包含节点Data的块,该节点在结构上发生了变化,并且仅共享一个属性Type。我用的是Newtonsoft。如何将Data节点反序列化为基于Type属性的确切类?因此,如果我创建一个items数组,当我查询数组时,我可以打开Type属性,并知道哪个对象Map到它。
API的JSON响应如下所示:

{
    "nextLink": "NextLink",
    "value": [
        {
            "Block": {
                "BlockName": "Name",
                "BlockClass": "Class",
                "IsVisibile": true,
                "Data": {
                    "Type": "Footer",
                    "Values": [
                        { 
                            "Property1": "Property1",
                            "Property2": "Property2"
                        }
                    ],
                    "Show": false
                }
            }
        },
        {
            "Block": {
                "BlockName": "Name",
                "BlockClass": "Class",
                "IsVisibile": true,
                "Data": {
                    "Type": "Header",
                    "Title": "Main title",
                    "Subtitle": "Subtitle"
                }
            }
        },
        {
            "Block": {
                "BlockName": "Name",
                "BlockClass": "Class",
                "IsVisibile": true,
                "Data": {
                    "Type": "Body",
                    "Information": "Info",
                    "AdditionalText": "More text",
                    "Projects": [
                        "Project1",
                        "Project2"
                    ]
                }
            }
        }
    ]
}

这些是我根据API响应定义的类:

public class Blocks
{
    public string NextLink { get; set; }
    public Response[] Value { get; set; }
}

public class Response
{
    public Block Block { get; set; }
}

public class Block
{
    public string BlockName { get; set; }
    public string BlockClass { get; set; }
    public bool IsVisible { get; set; }
    public Data Data { get; set; }
}

public class Data 
{
    public string Type { get; set; }
    ???
}

public class FooterType
{
    public bool Show { get; set; }
    public object[] Values { get; set; }
}

public class HeaderType 
{
    public string Title { get; set; }
    public string Subtitle { get; set; }
}

public class BodyType
{
    public string Information { get; set; }
    public string AdditionalText { get; set; }
    public object[] Projects { get; set; }
}

如果我将响应反序列化为特定的Type(如下所示),它当然适用于JSON中的该节点,但不是所有节点。

public class Data 
{
    public string Type { get; set; }
    public string Title { get; set; }
    public string Subtitle { get; set; }
}

如何将JSON中的每个项反序列化为它Map到的特定类型?

zf9nrax1

zf9nrax11#

您可以通过编写自己的转换器来完成这一点,该转换器正在进行类选择
本例使用Newtonsoft作为反序列化库。Json.Net

public class DataConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType) => typeof(Data).IsAssignableFrom(objectType); 

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var obj = JObject.Load(reader);
            // Read the target Type from the json data object
            var dataType = (string)obj["Type"];
            var item = new Data();
            // Determine what is the correct sub class for your data
            switch(dataType)
            {
                case "Header": 
                    item = new HeaderType();
                    break;
                case "Body": 
                    item = new BodyType();
                    break;
                case "Footer": 
                    item = new FooterType();
                    break;
            }
            // Populate the instance with the data provided in json
            serializer.Populate(obj.CreateReader(), item);
            return item;
        }

        public override bool CanWrite => false;
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotSupportedException();
    }

下面是您的案例in dotnetfiddle的工作示例

// Then just use it in your ´JsonConvert.DeserializeObject´ method
var blocks = JsonConvert.DeserializeObject<Blocks>(json, new DataConverter());
kxkpmulp

kxkpmulp2#

最简单的方法是创建多态类并使用JSON属性构造函数

Root root = JsonConvert.DeserializeObject<Root>(json);

public class Root
{
    public string nextLink { get; set; }
    public List<Item> value { get; set; }
}
public class Item
{
    public Block Block { get; set; }
}
public class Block
{
    public string BlockName { get; set; }
    public string BlockClass { get; set; }
    public bool IsVisibile { get; set; }
    public DataBase Data { get; set; }
    
    [JsonConstructor]
    public Block (JObject Data)
    {
        this.Data = (DataBase)Data.ToObject(Type.GetType((string)Data["Type"]));
    }
    public Block() {}
}

public class DataBase
{
    public string Type { get; set;}
    //public string Type { get { return this.GetType().Name; } }
}

public class Footer : DataBase
{
    public List<Properties> Values { get; set; }
    public bool Show { get; set; }
}

public class Header : DataBase
{
    public string Title { get; set; }
    public string Subtitle { get; set; }
}

public class Body : DataBase
{
    public string Information { get; set; }
    public string AdditionalText { get; set; }
    public List<string> Projects { get; set; }
}

public class Properties
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
}

但是如果你有很多这种类型的json,你可以创建一个json属性转换器,而不是在构造函数中添加一行

Root root = JsonConvert.DeserializeObject<Root>(json);

public class DataPropertyConverter : JsonConverter<DataBase>
{
    public override DataBase ReadJson(JsonReader reader, Type objectType, DataBase existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var jObj = JObject.Load(reader);
         return (DataBase)jObj.ToObject(Type.GetType((string)jObj["Type"]));
    }
    
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, DataBase value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

public class Block
{
    public string BlockName { get; set; }
    public string BlockClass { get; set; }
    public bool IsVisibile { get; set; }

    [JsonConverter(typeof(DataPropertyConverter))]
    public DataBase Data { get; set; }
}

相关问题