如何使用json.net自定义反序列化为对象 [英] How to custom deserialize into an object with json.net

查看:62
本文介绍了如何使用json.net自定义反序列化为对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 http://json2csharp.com/生成了一个代表我的JSON的csharp类,该类工作了太好了.但是,在使用第三方api一段时间后,我意识到他们的响应并不总是发送相同类型的数据.我正在查询其票证数据的API,并且每个票证都有一个SellPrice.有时他们将SellPrice作为具有货币(字符串)和金额(双精度)的Money对象返回,有时将它们作为双精度发送回去.因此,我试图找到一种优雅地处理此问题的方法,以便始终设置我的SellPrice对象的Amount.这是当他们发送Money对象作为SellPrice时工作的代码的简短版本

I used http://json2csharp.com/ to generate a csharp class that represents my JSON, which worked wonderfully. However, after working with the third party api for a while, I realized that their responses aren't always sending the same type of data. I'm querying their API for Ticket data, and each ticket has a SellPrice. Sometimes they return that SellPrice as a Money object that has a currency (string) and amount (double), and sometimes they send it back as just a double. So I'm trying to find a way to handle this gracefully, so that I always set the Amount of my SellPrice object. Here's a short version of the code that works when they send a Money object as SellPrice

public class Ticket
{
    public string Ticket_Id {get; set;}
    //... other fields
    public SellPrice SellPrice {get; set;}
}
public class SellPrice
{
    public string Currency {get; set;}
    public double Amount {get; set;}
}

然后当我得到Json时,我会像这样反序列化,效果很好...

And then when I get the Json, I deserialize like so, which works great...

for (int i = 0; i < jItems.Count; i++)
{
    TUTicket2 item = jItems[i].ToObject<TUTicket2>();
}

...直到我遇到一个API调用,该调用返回一个double而不是一个对象.

...until I run into an API call that returns a double instead of an object.

因此,仅查看Money对象的情况,我认为我会尝试创建一个构造函数,以便可以根据对象类型设置值,如下所示:

So just looking at the case where it's Money object, I thought I'd try to create a constructor so that I can set the values depending on the object type, like so:

public class SellPrice
{
    public SellPrice(object sellPrice)
    {
        if (sellPrice.GetType() == typeof(Dictionary<string, object>))
        {
            Currency = (string)((Dictionary<string, object>)sellPrice)["Currency"];
            Amount = (double)((Dictionary<string, object>)sellPrice)["Currency"];
        }
    }
    public string Currency { get; set; }
    public double Amount { get; set; }
}

但是这不起作用,因为sellPrice对象始终为null,所以我认为我在那儿树错了树.有没有一种方法可以轻松做到这一点?我认为我的问题是将其自动反序列化为对象类型的方式,但是我一直在查看代码/文档,但没有弄清楚我所缺少的内容.

But that doesn't work because the sellPrice object is always null, so I think I'm barking up the wrong tree there. Is there a way to do this easily? I think my problem is the way it auto-deserializes to an object type, but I've been looking through the code/documentation and haven't figured out what I'm missing.

我在这里有两个目标:我们正在做足够的API工作,我希望能够利用json2csharp之类的工具来生成类.我还想避免对每个类/对象进行手动反序列化,尽管如果这是我唯一的选择,我可以朝这个方向发展,当99%的值正常工作时,感觉就像是过分杀伤力.我也不希望根据我进行的API调用而最终得到每个类的X个不同版本.我正在尝试找到一些解决方案,让我只覆盖一小部分而不是所有内容.任何反馈将不胜感激.

I have a couple goals here: We are doing enough API work that I'd like to be able to utilize a tool like json2csharp to generate the classes. I'd also like to avoid doing manual deserialization for every class/object, although if that's my only option, I can go in that direction, it just feels like overkill when 99% of the values behave normally. I also don't want to end up with X different versions of each of my classes depending on which API call I make. I'm trying to find some solution that just lets me override a small portion instead of everything. Any feedback would be appreciated.

推荐答案

您可以通过创建自定义 JsonConverter 用于您的SellPrice类,如下所示:

You can handle this by making a custom JsonConverter for your SellPrice class like this:

public class SellPriceConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(SellPrice);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        SellPrice sellPrice = new SellPrice();
        if (token.Type == JTokenType.Object)
        {
            serializer.Populate(token.CreateReader(), sellPrice);
        }
        else if (token.Type == JTokenType.Float)
        {
            sellPrice.Amount = (double)token;
            // if there is a default currency, set it here, e.g.:
            // sellPrice.Currency = "USD";
        }
        else
        {
            throw new JsonException("Unexpected token type for SellPrice: " + token.Type.ToString());
        }
        return sellPrice;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

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

要使用它,只需要做的就是向SellPrice类中添加一个[JsonConverter]属性,如下所示:

To use it, all you would need to do is add a [JsonConverter] attribute to the SellPrice class like this:

[JsonConverter(typeof(SellPriceConverter))]
public class SellPrice 
{
    ...
}

然后,您可以像平常一样反序列化,它应该可以处理两种情况.

Then you can just deserialize as normal and it should handle both situations.

在此处工作的演示: https://dotnetfiddle.net/qZekyp

这篇关于如何使用json.net自定义反序列化为对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆