反序列化有时会产生对象列表或空对象的JSON字符串

时间:2017-02-22 07:27:56

标签: c# json serialization json.net

我有一个从API返回的JSON字符串,有时候这样,"result"键只有一个空对象:

{
  "_meta": {
    "status": "SUCCESS",
    "count": 0
  },
  "result": {}
}

或者这个,其中"result"键有一个对象数组:

{
  "_meta": {
    "status": "SUCCESS",
    "count": 0
  },
  "result": [
    { ... },
    { ... }
  ]
}

我已尝试从此question实施自定义JsonConverter,但我想我没有运气。这是我的实施:

class EmptyObjectJsonConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object retObj = new Object();

        if (reader.TokenType == JsonToken.StartArray)
        {
            retObj = serializer.Deserialize<List<T>>(reader);
        }
        else if (reader.TokenType == JsonToken.StartObject)
        {
            retObj = new List<T>();
        }

        return retObj;
    }

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

我用这种方式:

public class ApiReturn
{
    public ApiMeta _Meta { get; set; }

    [JsonConverter(typeof(EmptyObjectJsonConverter<ApiResult>))]
    public List<ApiResult> Result { get; set; }
}

我想从上面的实现中实现的是:如果令牌是start数组,那么以正常方式反序列化它,但如果令牌是起始对象,那么只返回一个空的对象列表。

但是,当我尝试使用具有空对象的"result"键运行程序时,它会引发异常:Additional text found in JSON string after finishing deserializing object.,结果ApiReturn.Result为空。

我是以错误的方式实施的吗?任何帮助将不胜感激。

基于@A的

更新。基耶萨回答:请查看我对GetApiResult

的实施情况
[JsonProperty("result")]
public JToken Result { get; set; }

public T GetResultAs<T>()
{
    var objectReturned = default(T);

    try
    {
        if (Result.Type == JTokenType.Array)
        {
            objectReturned = Result.ToObject<T>();
        }
        else if (Result.Type == JTokenType.Object)
        {
            // Should it return an empty List<ApiResult> if I use type List<ApiResult> ?
            objectReturned = default(T);
        }
    }
    catch
    {
        objectReturned = default(T);
    }

    return objectReturned;
}

为什么当JSON节点令牌是对象时我得到null?当我使用List<ApiResult>时,它应该返回空GetResultAs<List<ApiResult>>吗?

3 个答案:

答案 0 :(得分:1)

我喜欢使用由反序列化类抽象的JToken属性。

一个非常天真的实现是:

public enum MetaStatus
{
    SUCCESS
    // FAILURE,
    // ... etc
}

public class Meta
{
    public MetaStatus Status { get; set; }
    public long Count { get; set; }
}

public class ApiResponse
{
    [JsonProperty("_meta")]
    public Meta Meta { get; set; }

    [JsonProperty("result")]
    public JToken JsonResult { get; set; }

    public T GetResultAs<T>()
    {
        // put in place some error handling logic, depending upon what's really an error
        // in this method.
        try
        {
            return JsonResult.ToObject<T>();
        }
        catch
        {
            return default(T);
        }
    }
}

简而言之:您要求Json.Net将结果保存为Json对象,然后,在执行反序列化后,您可以尝试按照您想要的方式反序列化JToken,只需调用一个类型{{1和} GetResultAs一样。

代码不是按原样使用(可能你想要处理一些检查),但基本的反序列化机制就在那里。

答案 1 :(得分:0)

您可以尝试立即将JSON反序列化为对象。我使用json2sharp从您的JSON代码生成类。

public class Meta
{
    public string status { get; set; }
    public int count { get; set; }
}

public class Result
{
    <Add whatever field you are expecting>
}

public class RootObject
{
    public Meta _meta { get; set; }
    public Result result { get; set; }
}

然后,您可以使用下面的行立即将JSON反序列化为对象。

RootObject root = JsonConvert.DeserializeObject<RootObject>("YOUR JSON");

如果结果数组为空,则RootObject类中的列表将为空。

答案 2 :(得分:0)

这是我对@A的全面实施。如果你想知道的话,基耶萨的答案符合我的需要。

起初我认为default(T)与创建新实例相同

所以在这里我使用泛型类型的实例化(对我来说是新的:C# Create New T()):

public class ApiReturn
{
    [JsonProperty("_meta")]
    public ApiMeta Meta { get; set; }

    [JsonProperty("result")]
    public JToken Result { get; set; }

    public T GetResultAs<T>() where T : new()
    {
        var objectReturned = default(T);

        try
        {
            if (Result.Type == JTokenType.Array)
            {
                objectReturned = Result.ToObject<T>();
            }
            else if (Result.Type == JTokenType.Object)
            {
                objectReturned = new T();
            }
        }
        catch
        {
            objectReturned = new T();
        }

        return objectReturned;
    }
}