2017-04-04 33 views
1

我遇到了一個不尋常的問題。這可能不是一個非常現實的場景,但是這是我已經接觸到的,所以請耐心等待。將任意json響應轉換爲「東西」列表

我有一個返回Json的API,我使用Json.NET來處理Json響應。問題是,API可以返回一些東西,我必須能夠通過以下方式反序列化響應:

  • API可以返回單個Json對象。在這種情況下,我必須將其反序列化爲ExpandoObject並將其放入List<dynamic>
  • API可以返回null和undefined等,在這種情況下,我必須返回一個空列表。
  • API可以返回單個原始值,如Json字符串或Json浮點型。在這種情況下,我必須將其反序列化爲適當的.NET類型,並將其放入List<dynamic>並返回該類型。
  • 該API可以返回一個Json數組。在這種情況下,我要反序列化陣列爲List<dynamic>
    • 數組中的元素可以是JSON對象,在這種情況下,我必須再次向他們反序列化爲ExpandoObject,並把它們在列表中。
    • 元素也可以是原始值。在這種情況下,我必須將它們反序列化爲適當的.NET類型並將它們放入列表中。

根據我的研究,這裏就是我想出迄今:

protected IQueryable<dynamic> TestMethod(string r) 
{ 
    using (StringReader sr = new StringReader(r)) 
    using (JsonTextReader reader = new JsonTextReader(sr)) 
    { 
    if (!reader.Read()) 
    { 
     return new List<ExpandoObject>().AsQueryable(); 
    } 

    switch (reader.TokenType) 
    { 
     case JsonToken.None: 
     case JsonToken.Null: 
     case JsonToken.Undefined: 
     return new List<ExpandoObject>().AsQueryable(); 
     case JsonToken.StartArray: 
     return JsonConvert.DeserializeObject<List<ExpandoObject>>(r).AsQueryable(); 
     case JsonToken.StartObject: 
     return DeserializeAs<ExpandoObject>(r); 
     case JsonToken.Integer: 
     return DeserializeAs<long>(r); 
     case JsonToken.Float: 
     return DeserializeAs<double>(r); 
     // other Json primitives deserialized here 
     case JsonToken.StartConstructor: 
     // listing other not processed tokens 
     default: 
     throw new InvalidOperationException($"Token {reader.TokenType} cannot be the first token in the result"); 
    } 
    } 
} 

private IQueryable<dynamic> DeserializeAs<T>(string r) 
{ 
    T instance = JsonConvert.DeserializeObject<T>(r); 
    return new List<dynamic>() { instance }.AsQueryable(); 
} 

問題是與最後一顆子彈點。在開關情況下,當解串器遇到StartArray標記時,它會嘗試將json反序列化爲List<ExpandoObject>,但如果數組包含整數,則不能將其反序列化爲ExpandoObject

任何人都可以給我一個簡單的解決方案來支持兩種情況:Json對象數組爲List<ExpandoObject>和Json基元數組到他們各自的列表?

+0

這些來自同一端點嗎? – Dispersia

+0

是的,相同的端點。我知道這不是很現實,但我們假設這是一個給定的。 –

+0

簡單的解決方案是重構您的端點。 –

回答

1

由於Json.NET是下MIT license許可,您能適應的ExpandoObjectConverter邏輯來滿足您的需求,並創建以下方法:

public static class JsonExtensions 
{ 
    public static IQueryable<object> ReadJsonAsDynamicQueryable(string json, JsonSerializerSettings settings = null) 
    { 
     var serializer = JsonSerializer.CreateDefault(settings); 
     using (StringReader sr = new StringReader(json)) 
     using (JsonTextReader reader = new JsonTextReader(sr)) 
     { 
      var root = JsonExtensions.ReadJsonAsDynamicQueryable(reader, serializer); 
      return root; 
     } 
    } 

    public static IQueryable<dynamic> ReadJsonAsDynamicQueryable(JsonReader reader, JsonSerializer serializer) 
    { 
     dynamic obj; 

     if (!TryReadJsonAsDynamic(reader, serializer, out obj) || obj == null) 
      return Enumerable.Empty<dynamic>().AsQueryable(); 

     var list = obj as IList<dynamic> ?? new [] { obj }; 

     return list.AsQueryable(); 
    } 

    public static bool TryReadJsonAsDynamic(JsonReader reader, JsonSerializer serializer, out dynamic obj) 
    { 
     // Adapted from: 
     // https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs 
     // License: 
     // https://github.com/JamesNK/Newtonsoft.Json/blob/master/LICENSE.md 

     if (reader.TokenType == JsonToken.None) 
      if (!reader.Read()) 
      { 
       obj = null; 
       return false; 
      } 

     switch (reader.TokenType) 
     { 
      case JsonToken.StartArray: 
       var list = new List<dynamic>(); 
       ReadList(reader, 
        (r) => 
        { 
         dynamic item; 
         if (TryReadJsonAsDynamic(reader, serializer, out item)) 
          list.Add(item); 
        }); 
       obj = list; 
       return true; 

      case JsonToken.StartObject: 
       obj = serializer.Deserialize<ExpandoObject>(reader); 
       return true; 

      default: 
       if (reader.TokenType.IsPrimitiveToken()) 
       { 
        obj = reader.Value; 
        return true; 
       } 
       else 
       { 
        throw new JsonSerializationException("Unknown token: " + reader.TokenType); 
       } 
     } 
    } 

    static void ReadList(this JsonReader reader, Action<JsonReader> readValue) 
    { 
     while (reader.Read()) 
     { 
      switch (reader.TokenType) 
      { 
       case JsonToken.Comment: 
        break; 
       default: 
        readValue(reader); 
        break; 
       case JsonToken.EndArray: 
        return; 
      } 
     } 
     throw new JsonSerializationException("Unexpected end when reading List."); 
    } 

    public static bool IsPrimitiveToken(this JsonToken token) 
    { 
     switch (token) 
     { 
      case JsonToken.Integer: 
      case JsonToken.Float: 
      case JsonToken.String: 
      case JsonToken.Boolean: 
      case JsonToken.Undefined: 
      case JsonToken.Null: 
      case JsonToken.Date: 
      case JsonToken.Bytes: 
       return true; 
      default: 
       return false; 
     } 
    } 
} 

然後使用它像:

protected IQueryable<dynamic> TestMethod(string r) 
{ 
    return JsonExtensions.ReadJsonAsDynamicQueryable(r); 
} 

或者,您可以從您創建的custom JsonConverterReadJson()方法中調用ReadJsonAsDynamicQueryable

樣本fiddle

+0

檢出Json.NET:好主意。也感謝你的實施。 –