当我的API发送回单个对象时,我们将其称为Item(OData术语中的GetEntity),它看起来像这样:
{
"d" : {
"Item" : "123456",
"OldItem" : "78921",
}
}
当我抓住一组相同的对象,即返回Item of Item时,我得到:
{
"d":{
"results":[
{
"Item":"343431",
"OldItem":"21314"
},
{
"Item":"341321",
"OldItem":"43563"
}
]
}
}
除了显而易见的" d"基本节点我需要摆脱,我在尝试使用C#中的同一个类时遇到了麻烦。我有一个Item类:
public class Material : IEntity
{
[JsonProperty("Item")]
public string material_number { get; set; }
[JsonProperty("OldItem")]
public string old_material_number { get; set; }
// Methods
public Material() {}
public bool Validate() { throw new NotImplementedException(); }
}
我希望能够调用自定义JsonConverter
来处理这个问题,但我还没有能够获得一些示例,可以让单个对象,数组转换器工作。理想情况下,我应该可以致电:
JsonConvert.DeserializeObject<T>
其中T
为Material
或List<Material>
。如何构建JsonConverter
来处理这两种情况?
我正在调用JsonConvert.DeserializeObject<T>
:
if (response.IsSuccessStatusCode)
{
return JsonConvert.DeserializeObject<T>(JObject.Parse(response.Content.ReadAsStringAsync().Result).SelectToken("d").ToString());
}
else
{
throw new Exception("Service Error");
}
答案 0 :(得分:1)
这是JsonConverter
,适用于您的情况。请注意,如果您可以收到任何其他未在您的问题中显示的JSON格式 - 例如,如果d
在没有结果时可以具有值null
- 您可能需要调整转换器。目前,如果遇到它不期望的内容,它会抛出异常,但如果您愿意,可以让它返回null或者返回一个空列表。
public class MaterialArrayConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Object)
{
JToken results = token["results"];
if (results != null && results.Type == JTokenType.Array)
{
// we've got multiple items; deserialize to a list
return results.ToObject<List<Material>>(serializer);
}
else if (results == null)
{
// "results" property not present; return a list of one item
return new List<Material> { token.ToObject<Material>(serializer) };
}
}
// some other format we're not expecting
throw new JsonSerializationException("Unexpected JSON format encountered in MaterialArrayConverter: " + token.ToString());
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
// CanConvert is not called when [JsonConverter] attribute is used
return false;
}
}
您可以通过如下所示注释RootObject
类,然后将JSON反序列化为:
public class RootObject
{
[JsonProperty("d")]
[JsonConverter(typeof(MaterialArrayConverter))]
public List<Material> Materials { get; set; }
}
然后:
var root = JsonConvert.DeserializeObject<RootObject>(json);
从那里,您可以检索材料列表并根据需要使用它。
答案 1 :(得分:0)
假设您要在集合中退回材料,可以使用以下通用JsonConverter
public class SingleOrResultListConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(ICollection<T>).IsAssignableFrom(objectType);
}
const string Results = "results";
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (objectType.IsArray)
{
var list = (List<T>)ReadJson(reader, typeof(List<T>), new List<T>(), serializer);
return list.ToArray();
}
else
{
var list = (ICollection<T>)(existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());
if (reader.TokenType == JsonToken.StartArray)
{
serializer.Populate(reader, list);
}
else if (reader.TokenType == JsonToken.StartObject)
{
JObject obj = null;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.PropertyName:
string propertyName = reader.Value.ToString();
if (!reader.Read())
{
throw new JsonSerializationException("Unexpected end while reading collection");
}
if (propertyName == Results)
{
serializer.Populate(reader, list);
}
else
{
obj = obj ?? new JObject();
obj[propertyName] = JToken.Load(reader);
}
break;
case JsonToken.Comment:
break;
case JsonToken.EndObject:
if (obj != null)
list.Add(obj.ToObject<T>(serializer));
return list;
}
}
throw new JsonSerializationException("Unexpected end while reading collection");
}
else
{
throw new JsonSerializationException("Unexpected start token: " + reader.TokenType);
}
return list;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var collection = (ICollection<T>)value;
if (collection.Count == 1)
{
serializer.Serialize(writer, collection.First());
}
else
{
writer.WriteStartObject();
writer.WritePropertyName(Results);
writer.WriteStartArray();
foreach (var item in collection)
{
serializer.Serialize(writer, item);
}
writer.WriteEndArray();
writer.WriteEndObject();
}
}
}
然后按如下方式使用:
var obj = JObject.Parse(json);
var subObj = obj["d"] ?? obj; // Strip the "d".
var settings = new JsonSerializerSettings { Converters = new[] { new SingleOrResultListConverter<Material>() } };
var list = subObj.ToObject<List<Material>>(JsonSerializer.CreateDefault(settings));
或者如果您更喜欢数组到List<T>
:
var array = subObj.ToObject<Material[]>(JsonSerializer.CreateDefault(settings));
原型fiddle。
<强>更新强>
您需要确保分配并将转换器传递给Json.NET。由于您的反序列化方法是通用的,因此您需要执行通用的操作,如下所示:
var json = response.Content.ReadAsStringAsync().Result;
var converters = typeof(T).GetCollectionItemTypes()
.Select(t => (JsonConverter)Activator.CreateInstance(typeof(SingleOrResultListConverter<>).MakeGenericType(new [] { t })))
.ToArray();
var settings = new JsonSerializerSettings { Converters = converters };
return JToken.Parse(json).SelectToken("d").ToObject<T>(JsonSerializer.CreateDefault(settings));
使用扩展方法:
public static class TypeExtensions
{
/// <summary>
/// Return all interfaces implemented by the incoming type as well as the type itself if it is an interface.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
{
if (type == null)
throw new ArgumentNullException();
if (type.IsInterface)
return new[] { type }.Concat(type.GetInterfaces());
else
return type.GetInterfaces();
}
public static IEnumerable<Type> GetCollectionItemTypes(this Type type)
{
foreach (Type intType in type.GetInterfacesAndSelf())
{
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
yield return intType.GetGenericArguments()[0];
}
}
}
}