如何处理从JsonConvert.DeserializeObject返回的Json错误代码<t>()

时间:2016-12-07 00:28:06

标签: c# json json.net

我正在使用继承的代码,与第三方设备进行通信。我的开发套件是VS 2015,使用C#和NewtonSoft.Json库。

我的“顶级”例程看起来有点像这样(为清楚起见省略了一些细节):

public JsonResponse<T> SendTypedApiRequest<T>(JsonCommand command, 
    string apiPath, string description, int expectedResponseCode = 0)
{
    try
    {
        var r = SendPost(apiPath, command);
        if (r.Code == OK && r.Response != null) {
            var jsonResponse = JsonResponse<T>.Deserialize(r.Response);

            if (jsonResponse.RetCode != expectedResponseCode) {
                // handle errors
             }
         }
    }
    catch(){};
}

在另一个文件中,我有以下内容:

    public static JsonResponse<T> Deserialize(string json, 
        JsonSerializerSettings settings = null)
    {
        return JsonConvert.DeserializeObject<JsonResponse<T>>(json, settings);
    }

我希望从这个帖子的设备返回的是一个数值,所以在这种情况下T是'int'(或者更确切地说是int的JsonResponse):

{"retCode":0,"retMsg":"OK","data":41272}

然而,当不符合正确的前提条件时,我得到了这个:

{"retCode":1000,"retMsg":"No admin","data":{}}

看起来够公平吧?但是,由于这是一个类型化的响应反序列化器,它会在{}上发出阻塞,期望一个整数而不是开始的大括号。我得到的是一个JsonReaderException“解析值时遇到的意外字符:{。”我想得到的是来自Json响应的retCode和retMsg,所以我可以将实际的错误条件传递给用户。 JsonReaderException根本没用。

我尝试将JsonSerializerSettings用于Null和Empty值,但在这种情况下它们不起作用。我似乎找不到处理这种情况的标准方法。我可以在最后调用JsonConvert.DeserializeObject()时调用try / catch来从响应中提取实际错误并将其追回堆栈,但这看起来非常粗糙和笨拙 - 没有逻辑可以处理直到我在代码中达到3或4级(一切都期待一个类型化的响应)。

有一种优雅的方式可以解决这个问题吗?有没有规范的方法来解决这个问题?似乎我提出的每一种方法都是笨拙和丑陋,或根本不起作用。

TIA,戴夫

2 个答案:

答案 0 :(得分:2)

一种可能的方法是为JsonConverter创建自定义JsonResponse<T>来处理类型差异。我们的想法是将JSON数据加载到中间JObject,然后在单独的步骤中处理“行为良好”的属性(例如RetCode, RetMsg)和可能不正确的Data属性。这样,如果Data属性的转换失败,您仍然可以恢复其余的响应。

以下是转换器的外观:

public class JsonResponseConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType &&
               objectType.GetGenericTypeDefinition() == typeof(JsonResponse<>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load JSON data into a JObject
        JObject obj = JObject.Load(reader);

        // Remove the problematic data property from the JObject
        JProperty dataProp = obj.Property("data");
        if (dataProp != null) dataProp.Remove();

        // Create a JsonResponse object and populate it with the 
        // well-behaved properties in the JObject (e.g. RetCode, RetMsg)
        object jsonResponse = Activator.CreateInstance(objectType);
        serializer.Populate(obj.CreateReader(), jsonResponse);

        try
        {
            // Now try to add the data property into the same response
            if (dataProp != null)
            {
                JObject data = new JObject(dataProp);
                serializer.Populate(data.CreateReader(), jsonResponse);
            }
        }
        catch (JsonException) { }  // if it doesn't work, eat the exception

        return jsonResponse;
    }

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

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

要使用转换器,您可以将其添加到JsonSerializerSettings方法中的SendTypedApiRequest<T>,并将设置传递给JsonResponse<T>.Deserialize方法。

    var r = SendPost(apiPath, command);
    if (r.Code == OK && r.Response != null)
    {
        var settings = new JsonSerializerSettings();
        settings.Converters.Add(new JsonResponseConverter());

        var jsonResponse = JsonResponse<T>.Deserialize(r.Response, settings);
        ...
    }

以下是演示:https://dotnetfiddle.net/oWYkoU

答案 1 :(得分:0)

您可以作弊并将数据int类型更改为dynamic。有点黑客但它可能会解决你的问题。