request.Data
{
"jurisdictionCode": "California",
"claimId": 123654,
"claimGroupID": 12,
"claimXref": "32145",
"serviceXref": "Test",
"claimStart": "2021-07-30T13:20:15.338Z",
"claimEnd": "2021-07-30T13:20:15.338Z",
"status": 5,
"creationTimestamp": "2021-07-30T13:20:15.338Z",
"touchTimestamp": "2021-07-30T13:20:15.338Z",
"filingSource": 7,
"userName": "test",
"exportTs": "2021-07-30T13:20:15.338Z",
"payerXref": "test",
"dtbatchExportTs": "2021-07-30T13:20:15.338Z"
}
public class scaffolded_model
{
[Key]
[StringLength(10)]
public string JurisdictionCode { get; set; }
[Key]
public long ClaimID { get; set; }
public long ClaimGroupID { get; set; }
[Required]
[StringLength(64)]
public string ClaimXRef { get; set; }
[Required]
[StringLength(64)]
public string ServiceXRef { get; set; }
[Column(TypeName = "datetime")]
public DateTime ClaimStart { get; set; }
[Column(TypeName = "datetime")]
public DateTime ClaimEnd { get; set; }
public int Status { get; set; }
[Column(TypeName = "datetime")]
public DateTime CreationTimestamp { get; set; }
[Column(TypeName = "datetime")]
public DateTime TouchTimestamp { get; set; }
public int FilingSource { get; set; }
[Required]
[StringLength(256)]
public string UserName { get; set; }
[Key]
[Column(TypeName = "datetime")]
public DateTime ExportTS { get; set; }
[Required]
[StringLength(64)]
public string PayerXRef { get; set; }
[Column(TypeName = "datetime")]
public DateTime DTBatchExportTS { get; set; }
}
Code:
var data = JsonSerializer.Serialize(request.Data);
Dictionary<string, JsonElement> result = (Dictionary<string, JsonElement>)JsonSerializer.Deserialize(data, typeof(Dictionary<string, JsonElement>));
foreach (var item in result)
{
PropertyInfo pi = scaffolded_model
.GetType()
.GetProperty(item.Key, BindingFlags.Instance | BindingFlags.Public);
if (pi == null)
{
_logger.LogInformation("Bad Field");
continue;
}
pi.SetValue(scaffolded_model, item.Value);
}
I have been having issues with using the GetProperty() method to match and populate values from a json request coming in as request.Data and an Empty model called scaffolded_model. As far as I can tell, both sets of data are setup correctly. The code should run through each value in the request, match it by item.key in the empty model and populate the matching key with the value. item.key comes up empty everytime. I have tried different bindings, etc. If I hard code the first item.key coming through as JurisdictionCode, it grabs the value and populates it correctly. So everything is working, if the item.key would populate.
Thanks for looking and for all of your help.
[ApiVersion("1.0")]
[HttpPost("v{version:apiVersion}/Submitclaim")]
[Produces("application/json")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public Task<IActionResult> Submitclaim(ClaimModel request)
{
var source = MethodBase.GetCurrentMethod().Name;
IActionResult actionResult = null;
using (LogContext.PushProperty("jx", request.JurisdictionCode))
{
try
{
//var claim_data = JsonSerializer.Serialize(request);
//Dictionary<string, JsonElement> result = (Dictionary<string, JsonElement>)JsonSerializer.Deserialize(claim_data, typeof(Dictionary<string, JsonElement>));
API.CRUD.Claims.Model.Claim scaffolded_model = new API.CRUD.Claims.Model.Claim();
JsonSerializer.Deserialize<scaffolded_model>(request);
//foreach (var item in result)
//{
// PropertyInfo pi = scaffolded_model
// .GetType()
// .GetProperty(
// item.Key,
// BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
// if (pi == null)
// {
// _logger.LogInformation("Bad Field");
// continue;
// }
// pi.SetValue(
// scaffolded_model,
// Convert.ChangeType(item.Value.ToString(), pi.PropertyType));
//}
}
catch (Exception ex)
{
_logger.LogError($"Exception failed: {ex.Message}");
actionResult = Problem("Exception failed");
}
}
return Task.FromResult(actionResult);
}
If you have a good reason to implement custom JSON parsing via Reflection instead of using standard System.Text.Json or Newtonsoft.Json libraries, then there are couple issues that need to be resolved:
jurisdictionCode
property name to JurisdictionCode
property. The BindingFlags.IgnoreCase
flag should solve this issue.PropertyInfo pi = scaffolded_model
.GetType()
.GetProperty(
item.Key,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
PropertyInfo.SetValue
does not perform any type conversions. The value that need to be set must match property type, otherwise TargetException
will be thrown. At this time the code from the question always sets string value returned by item.Value.ToString()
method to all properties. It will not work for long
or DateTime
properties. To resolve this issue, the Convert.ChangeType
method can be used as the simplest option that will handle property types defined in the scaffolded_model
class.pi.SetValue(
scaffolded_model,
Convert.ChangeType(item.Value.ToString(), pi.PropertyType));
With these two changes it will be possible to parse Json form example.
But current code has some limitations:
It does not handle nulls. In case of string property, the empty string value will be assigned to the property instead of original null value. The nullable value types (e.g. long?) are not supported at all. To resolve this issue, current logic can be adjusted to check the JsonElement.ValueKind
property for JsonValueKind.Null
value.
Another issue is a DateTime
type. With the current implementation all DateTime
values will be adjusted to the local timezone, and Convert.ChangeType
method does not provide any ability to control it. The replacement of DateTime
time to DateTimeOffset
will not work as Convert.ChangeType
method does not support it. There is only option to check property type and perform manual conversion using, for example, DateTime.Parse
method instead of Convert.ChangeType
.
This list can be continued. So in general case it is better to use standard libraries for parsing Json.