I have found people asking similar questions but couldn't quite find the solution from the answers to those.
I have the following string: "{"message":"Validation failed. 1 error found.","errorCode":"E04300","developerHint":"Inspect validation errors and correct your request.","logId":"7612fd90f484abda-CPH","httpStatusCode":400,"errors":{"customerNumber":{"errors":[{"propertyName":"customerNumber","errorMessage":"Customer number exists","errorCode":"E06010","inputValue":29926638,"developerHint":"Customer number 29926638 already exists"}]}},"logTime":"2022-10-28T12:29:27","errorCount":1}"
I deserialize it to an object:
var obj = JsonConvert.DeserializeObject(json)
When I call obj.Dump() in LinqPad I get this:
So obviously the data is there in my object. Only I can't find it using
obj.GetType().GetProperty("message").GetValue(obj, null)
Any ideas how to get at the data in this object?
I have tried
obj.GetType().GetCustomAttributes();
obj.GetType().GetFields();
obj.GetType().GetMembers();
obj.GetType().GetProperties();
can't seem to find anything.
I am aware that the obvious answer would be to just create the class that fits the data and deserialize to that class, but I am specifically trying to avoid that.
You need to be getting the JProperty or the KeyValuePair<string, JToken> from it
In either scenario, you get the data from it via its Value
property.
Use of .GetType()
is not going to work, as that won't be accessing the internal collection that actually houses the data you want. As mentioned previously a JObject is a niche wrapper around what you can imagine as an internal dictionary
This being said, if you know the structure of the JSON file upfront, deserialize it to a class object as you can leverage a lot of great features from the library, including annotations to finely control deserialization, full use of reflection on those objects, etc.
Deserializing to a JObject is useful when your JSON structure can be variable, with the drawback of needing to know the exact field "keys" to access. If you don't, and have to always fallback on looping through the JObject, just know you are introducing that extra sacrifice of time and space complexity that come with loops. Its not the end of the world, but unnecessary under most circumstances as no one likes unpredicable JSON structure and typically will not create an API that does something like that
Update, may deserialize with generics
It may take some setup but for example:
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
}
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;
}
}
Type constraints is one of the major powerhouses here, because it allows you to deserialize to a class that simply contains the specific generic class you want. Its also extensible as any new error subclasses will simply have to implement the constraining interface. Its "dynamic" enough without sacrificing type safety
You can think of it as a sort of wrapping and unwrapping a generic in order to allow for polymorphic deserialization