如何从JSON创建具有不同对象类型的列表

时间:2019-01-02 13:36:51

标签: c# asp.net json

我想从json创建一个不同对象类型的列表(具有相同的父对象)。

我有一些型号

public class Quote
{
    public virtual ICollection<QuoteLine> QuoteLines { get; set; }
}

public class QuoteLine
{
    public int Order { get; set; }
}

public class A : QuoteLine
{
    public string PropertyA { get; set; }
}

public class B : QuoteLine
{
    public string PropertyB { get; set; }
}

引号包含引号行集合,可以是A或B类型

我可以做这样的事情

Quote quote = new Quote{
    QuoteLines = new List<QuoteLine>{
        new A { Order = 1, PropertyA = "propA"},
        new B { Order = 2, PropertyB = "propB"},
    }
}

我想换一种方式 我有一个这样的Json(从HttpClient.GetAsync()返回)

{"quoteLines":[
    {"order":1,"propertyA":"propA"},
    {"order":2,"propertyB":"propB"}
]}

当我将其反序列化为Quote时,我失去了children属性。 我知道这是因为我有一个类型为QuoteLine的集合,因此不会反序列化children属性。但是我正在寻找一种保留所有信息的方法。

我正在打的电话:

    public async Task<Quote> GetById(int id)
    {
        Quote quote = null;

        HttpResponseMessage response = await httpClient.GetAsync(new StringBuilder(controllerName).Append("/").Append(id).ToString());
        if (response.IsSuccessStatusCode)
        {
            quote = await response.Content.ReadAsAsync<Quote>();
        }

        return quote;
    }

感谢您的帮助。

3 个答案:

答案 0 :(得分:2)

您需要为此目的在JsonSerializerSettings中使用TypeNameHandling。

JsonSerializerSettings settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All
};
var resultJson = JsonConvert.SerializeObject(quote,settings);
var resultobject = JsonConvert.DeserializeObject<Quote>(resultJson,settings);

验证输出

foreach(var item in resultobject.QuoteLines)
{
    switch(item)
    {
        case A itemA :
            Console.WriteLine($"Type A: {itemA.Order}, {itemA.PropertyA}");
            break;
        case B itemB :
            Console.WriteLine($"Type A: {itemB.Order}, {itemB.PropertyB}");
            break;
        default:
            Console.WriteLine("Error");
            break;
    }
}

输出

Type A: 1, propA
Type A: 2, propB

答案 1 :(得分:0)

如果不是必须使用这种特定的类层次结构,我建议仅保留一个类QuoteLine,将所有字段放在其中,并摆脱类A和B。因此代码看起来像这样:

public class QuoteLine
{
    public int Order { get; set; }
    public string PropertyA { get; set; }
    public string PropertyB { get; set; }
}

这样,json中不存在的字段将为null,您可以在使用前检查它们。

答案 2 :(得分:0)

我找到了一个更好的解决方案,它不会在json中公开名称空间和应用程序组装。

仅在客户端应用中。 我添加了一个自定义的JsonConverter:

public abstract class JsonCreationConverter<T> : JsonConverter
    where T : class
{
    public override bool CanWrite
    {
        get
        {
            return false;
        }
    }

    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }


    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader == null) throw new ArgumentNullException("reader");
        if (serializer == null) throw new ArgumentNullException("serializer");
        if (reader.TokenType == JsonToken.Null)
            return null;

        JObject jObject = JObject.Load(reader);
        T target = Create(objectType, jObject);
        serializer.Populate(jObject.CreateReader(), target);
        return target;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

对于每个派生的实体:

public class QuoteLineJsonConverter : JsonCreationConverter<QuoteLine>
{
    protected override QuoteLine Create(Type objectType, JObject jObject)
    {
        if (jObject == null) throw new ArgumentNullException("jObject");
        string discriminator = jObject.GetValue("Discriminator", StringComparison.OrdinalIgnoreCase)?.Value<string>();
        if (discriminator != null)
        {

            switch (discriminator)
            {
                case "A":
                    return new A();
                case "B":
                    return new B();
                default:
                    return new QuoteLine();
            }
        }
        else
        {
            return new QuoteLine();
        }
    }
}

我只需要向模型添加数据属性即可触发自定义转换器

[JsonConverter(typeof(QuoteLineJsonConverter))]
public class QuoteLine
{
    public int Order { get; set; }
}

对jquery ajax请求非常重视,您需要将数据作为Json发送以调用自定义JsonConverter。

        $.ajax({
            url: "myUrl",
            type: "post",
            cache: false,
            data: JSON.stringify(data),
            dataType: "json",
            contentType: "application/json; charset=utf-8"
        });