我遇到了一个不寻常的问题。这可能不是一个非常现实的场景,但这就是我自己陷入的情况,所以请耐心等待。
我有一个返回 Json 的 API,并且我正在使用 Json.NET 来处理 Json 响应。问题是 API 可以返回很多内容,我必须能够通过以下方式反序列化响应:
- API 可以返回单个 Json 对象。在这种情况下,我必须将其反序列化为
ExpandoObject
并将其放入List<dynamic>
. - API 可以返回 null 和 undefined 等,在这种情况下我必须返回一个空列表。
- API 可以返回单个原始值,例如 Json 字符串或 Json float 。在这种情况下,我必须将其反序列化为适当的 .NET 类型,将其放入
List<dynamic>
中。并返回。 - API 可以返回 Json 数组。在这种情况下,我必须将数组反序列化为
List<dynamic>
:- 数组中的元素可以是 Json 对象,在这种情况下我必须将它们反序列化为
ExpandoObject
再次,并将它们放入列表中。 - 元素也可以是原始值。在这种情况下,我必须将它们反序列化为正确的 .NET 类型并将它们放入列表中。
- 数组中的元素可以是 Json 对象,在这种情况下我必须将它们反序列化为
根据我的研究,到目前为止我得出的结论如下:
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();
}
问题出在最后一个要点上。在 switch-case 中,当解串器遇到 StartArray
时 token ,它尝试将 json 反序列化为 List<ExpandoObject>
,但如果数组包含整数,则无法将它们反序列化为 ExpandoObject
.
任何人都可以给我一个简单的解决方案来支持这两种情况:Json 对象数组到 List<ExpandoObject>
和 Json 原语数组到各自的列表?
最佳答案
由于 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);
}
或者,您可以从 ReadJson()
中调用 ReadJsonAsDynamicQueryable
方法custom JsonConverter
您创建的。
样本fiddle .
关于c# - 将任意 json 响应转换为 "things"列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43214424/