我正在使用Newtonsoft.Json解析一些API响应,它们似乎有一个非常规则的结构,除了根据返回类型更改的数组属性名称。一些响应示例(包含假/空数据):
客户:
{
"success": true,
"message": "Records Retrieved Successfully",
"data": {
"total_count": "1",
"customer": [
{
"id": "1234",
"accountId": "220",
"email": "json.voorhees@lycos.com",
"name": "JSON Voorhees",
"company": "Test Company",
"customFieldsValues": [
{
"value": "Some Guy",
"field": {
"id": "69",
"name": "SalespersonID",
"label": "Account Manager"
}
}
]
}
]
}
}
发票:
{
"success": true,
"message": "Records Retrieved Successfully",
"data": {
"total_count": "0",
"invoice": []
}
}
您会在第一个中注意到,数组的属性名称是" customer",而在第二个中,它是"发票" (我们没有任何发票,因此我不确切知道该对象的结构是什么)。
我的最终目标是反序列化为类似这样的类结构:
public class Response {
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
public class Response<T> : Response {
public List<T> Data { get; set; }
}
由于这不能直接通过简单的DeserializeObject()
调用(因为业务对象数组包含在该中间数&#34;数据&#34;属性)中,这似乎是&#39 ;更接近于所需的内容,但问题是[JsonProperty()]
属性的移动目标:
public class Response {
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
public class Response<T> : Response {
public ResponseData Data { get; set; }
}
public class ResponseData<T> {
[JsonProperty("total_count")]
public int TotalCount { get; set; }
[JsonProperty("???")] //Moving target
public List<T> Data { get; set; }
}
将这种方法拉下来的最佳方法是什么?
答案 0 :(得分:3)
JavaScriptDeserializer
对象可以帮助您,dynamic
:
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
dynamic obj = serializer.Deserialize(json, typeof(object));
然后,获取所有属性以测试对象具有哪些属性:
var propertyInfo = obj.GetType().GetProperties();
然后,获取所需的属性名称并将其传递给:
var value = obj.data[0].GetType().GetProperty(propertyName).GetValue(obj, null);
作为示例循环:
foreach (var property in obj.GetType().GetProperties()) {
Console.WriteLine(String.Format("The value for property {0} is {1}.",
property.Name,
obj.data[0].GetType().GetProperty(propertyName).GetValue(obj, null));
}
请注意,这个答案使用System.Reflection
,这对于大型计算来说非常缓慢(即,除非你有一些空闲的时间来杀死,否则不要迭代这些方法数千次!)
答案 1 :(得分:1)
当你要求&#34; sanest&#34;时,我不完全确定你的意思。解决这个问题的方法。 Json.NET支持[JsonExtensionData]
将意外属性捕获到Dictionary<string, object>
或Dictionary<string, JToken>
。但是,您的List<T> Data
是类型化的,并且Json.NET没有任何内置功能可以将任意命名的属性反序列化为类型化对象。你还写了对转换为Dictionary 的中间步骤并不完全满意,所以听起来好像你想要一个避免反序列化为中间表示的解决方案。以下转换器可以实现此目的:
[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class JsonAnyPropertyNameAttribute : System.Attribute
{
}
class JsonAnyPropertyNameConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException("This converter is intended to be applied directly to a type or a property.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
try
{
int defaultCount = 0;
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
if (existingValue == null)
existingValue = contract.DefaultCreator();
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.Comment:
break;
case JsonToken.PropertyName:
{
var name = reader.Value.ToString();
var property = contract.Properties.GetClosestMatchProperty(name);
if (!reader.Read())
throw new JsonSerializationException(string.Format("Missing value at path: {0}", reader.Path));
if (property == null)
{
property = contract.Properties.Where(p => p.AttributeProvider.GetAttributes(true).OfType<JsonAnyPropertyNameAttribute>().Any()).Single();
defaultCount++;
if (defaultCount > 1)
{
throw new JsonSerializationException(string.Format("Too many properties with unknown names for type {0} at path {1}", objectType, reader.Path));
}
}
var value = serializer.Deserialize(reader, property.PropertyType);
property.ValueProvider.SetValue(existingValue, value);
}
break;
case JsonToken.EndObject:
return existingValue;
default:
throw new JsonSerializationException(string.Format("Unknown token {0} at path: {1} ", reader.TokenType, reader.Path));
}
}
throw new JsonSerializationException(string.Format("Unclosed object at path: {0}", reader.Path));
}
catch (Exception ex)
{
if (ex is JsonException)
throw;
// Wrap any exceptions encountered in a JsonSerializationException
throw new JsonSerializationException(string.Format("Error deserializing type {0} at path {1}", objectType, reader.Path), ex);
}
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
然后将其添加到您的类型中,如下所示。 [JsonAnyPropertyName]
属性指示应将未知属性反序列化的c#属性。
[JsonConverter(typeof(JsonAnyPropertyNameConverter))]
public class ResponseData<T>
{
[JsonProperty("total_count")]
public int TotalCount { get; set; }
[JsonAnyPropertyName]
public List<T> Data { get; set; }
}
public class Response
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
public class Response<T> : Response
{
[JsonProperty("data")]
public ResponseData<T> Data { get; set; }
}
如果存在多个未知属性,则转换器会抛出异常而不是覆盖先前反序列化的数据。
答案 2 :(得分:0)
好吧,这是有效的,但它肯定不是我见过的最漂亮的东西(对于转换为字典的中间步骤并不完全满意)。暂时搁置这个问题,以防有人知道更好的方法。
各种对象:
public class Response {
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
public class Response<T> : Response {
[JsonProperty("data")]
[JsonConverter(typeof(DataConverter))]
public List<T> Data { get; set; }
}
public class DataConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return typeof(List<object>).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
Dictionary<string, object> data = (Dictionary<string, object>)serializer.Deserialize(reader, typeof(Dictionary<string, object>));
foreach (KeyValuePair<string, object> kvp in data) {
if (kvp.Key != "total_count") {
return ((JToken)kvp.Value).ToObject(objectType);
}
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
throw new NotImplementedException();
}
}
然后获取回复:
public Response<Customer> GetCustomers() {
string response = SendRequest("/api/v1/customers");
Response<Customer> aresponse = JsonConvert.DeserializeObject<Response<Customer>>(response);
}