【问题标题】:Deserialize JSON string that sometimes resulting a list of object or an empty object反序列化有时会生成对象列表或空对象的 JSON 字符串
【发布时间】:2017-02-22 07:27:55
【问题描述】:

我有一个从 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; }
}

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

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

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


更新基于@A。 Chiesa回答:请看我对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?当我使用GetResultAs&lt;List&lt;ApiResult&gt;&gt; 时,它是否应该返回一个空的List&lt;ApiResult&gt;

【问题讨论】:

    标签: c# json serialization json.net


    【解决方案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,只需调用类型化的 GetResultAs,例如GetResultAs&lt;List&lt;ApiLineObj&gt;&gt;().

    代码并不打算按原样使用(可能您想处理一些检查),但基本的反序列化机制已经存在。

    【讨论】:

    • 抱歉回复晚了。那么,如果我想根据收到的JsonResult 的令牌获取对象列表或空对象,我应该在GetResultAs 中输入什么检查语法?
    • 您可以检查 JToken Type 属性,如第一个示例所示。
    • 我刚试过,当JSON节点为对象(空对象)时,GetResultAs&lt;List&lt;ApiResult&gt;&gt;()的结果为null。
    • 是的,因为它进入了 catch 块。根据您的其他要求,您可以使用 null 或在调用 GetResultAs 之前检查 JsonResult.Type
    • 空列表不是默认值(T)。只需用你关心的类型反序列化,当 Type 不是 Array 时返回新的 List。
    【解决方案2】:

    您可以尝试立即将 JSON 反序列化为 Object。我使用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 反序列化为 Object。

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

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

    【讨论】:

    • 抱歉,我不是这个意思。不过,我一开始就使用它。
    【解决方案3】:

    这是我对@A 的完整工作实现。如果您想知道,Chiesa 的回答符合我的需要。

    一开始我以为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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-03-06
      • 2021-01-16
      • 1970-01-01
      • 2021-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多