【问题标题】:Using reflection on object created from json对从 json 创建的对象使用反射
【发布时间】:2022-11-01 22:55:37
【问题描述】:

我发现有人问过类似的问题,但无法从这些问题的答案中找到解决方案。

我有以下字符串:"{"message":"验证失败。发现 1 个错误。","errorCode":"E04300","developerHint":"检查验证错误并更正您的请求。","logId":"7612fd90f484abda-CPH","httpStatusCode":400,"errors":{ "customerNumber":{"errors":[{"propertyName":"customerNumber","errorMessage":"客户编号存在","errorCode":"E06010","inputValue":29926638,"developerHint":"客户编号29926638 已经存在"}]}},"logTime":"2022-10-28T12:29:27","errorCount":1}"

我将它反序列化为一个对象:

var obj = JsonConvert.DeserializeObject(json)

当我在 LinqPad 中调用 obj.Dump() 时,我得到了这个:

很明显,数据在我的对象中。只有我找不到它使用 obj.GetType().GetProperty("message").GetValue(obj, null)

任何想法如何获取此对象中的数据?

我努力了

obj.GetType().GetCustomAttributes();
obj.GetType().GetFields();
obj.GetType().GetMembers();
obj.GetType().GetProperties();

似乎找不到任何东西。

我知道显而易见的答案是只创建适合数据的类并反序列化到该类,但我特别想避免这种情况。

【问题讨论】:

  • 这是一个 JObject - 没有 message 属性等。您尝试的反射调用只有在您创建了一个适合数据的类。将JObject 视为更复杂的Dictionary<string, JValue>。阅读链接文档和LINQ to JSON 文档了解更多详细信息。
  • 谢谢。我还没有解决它,但是将它反序列化为 JObject 可以让我访问 Descendants() ,这看起来很有用。本周一将继续工作。

标签: c# json-deserialization system.reflection


【解决方案1】:

你需要得到JProperty or the KeyValuePair<string, JToken> from it

在任何一种情况下,您都可以通过其Value 属性从中获取数据。

使用.GetType() 是行不通的,因为这不会访问实际包含您想要的数据的内部集合。如前所述,JObject 是您可以想象的内部字典的利基包装器

话虽如此,如果您预先知道 JSON 文件的结构,请将其反序列化为类对象,因为您可以利用该库中的许多出色功能,包括用于精细控制反序列化的注释、充分利用这些对象的反射等.

当您的 JSON 结构可以是可变的时,反序列化为 JObject 很有用,缺点是需要知道要访问的确切字段“键”。如果您不这样做,并且必须始终退回到循环通过 JObject,只需知道您正在引入循环带来的时间和空间复杂性的额外牺牲。它不是世界末日,但在大多数情况下是不必要的,因为没有人喜欢不可预测的 JSON 结构,并且通常不会创建执行类似操作的 API

使用泛型更新、反序列化 它可能需要一些设置,但例如:

  1. 设置所有这些子类都将实现的接口,即使接口完全空白
  2. 为构成错误响应的每个错误对象定义类
  3. 创建响应将反序列化到的“根”类,并使其采用通用参数。将泛型约束到您创建的接口。现在在这个根类中,指定类型 T 的属性
    public interface IAllErrorObjs
    {
        //...
    }
    
    public class ErrorOne : IAllErrorObjs 
    { 
        //... 
    }
    
    public class ErrorTwo : IAllErrorObjs 
    { 
        //... 
    }
    
    public class ResponseRoot<T> where T : IAllErrorObjs
    {
        public T ErrorObj { get; set; } //T can be ErrorOne, ErrorTwo, etc
    }
    
    1. 创建您的任务以访问 API,并使用此设置进行反序列化,从中返回您想要的特定对象
    private static readonly HttpClient _client = new HttpClient();
    
    public async Task<T> MakeTheRequestAsync<T>() where T : IAllErrorObjs
    {
        string requestUrl = "url here";
    
        HttpRequestMessage request = new HttpRequestMessage
        {
            Method = HttpMethod.Get,
            RequestUri = new Uri(requestUrl)
        };
    
        using (HttpResponseMessage response = await _client.SendAsync(request).ConfigureAwait(false))
        {
            if (!response.IsSuccessStatusCode) throw new Exception($"Request failed: {response.StatusCode}");
    
            string body = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
            ResponseRoot<T> rsp = JsonConvert.DeserializeObject<ResponseRoot<T>>(body);
    
            return rsp.ErrorObj;
        }
    }
    
    

【讨论】:

  • 问题与描述的完全一样,我收到的json对象不一样。它是一个错误对象,错误的类型决定了存在哪些子类对象。如果我知道所有这些,那么我可以创建具有所有属性的 C# 类,然后只查看那些不为空的。似乎这些对象非常相似,并且对于错误消息都有相同的“键”。
  • 嗯......您有可能在这里使用一些泛型,但它会假定您知道您所说的每个“子类对象”的形式。我会编辑我的帖子,因为评论可能会很长,让我知道你的想法
猜你喜欢
  • 1970-01-01
  • 2016-11-14
  • 2012-05-15
  • 1970-01-01
  • 1970-01-01
  • 2021-09-13
  • 2023-02-14
  • 2023-03-19
  • 2015-07-30
相关资源
最近更新 更多