PropertyInfo getProperty() 方法的问题

Issue with PropertyInfo getProperty() Method

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; }
}

代码:

    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);
}

我在使用 GetProperty() 方法匹配和填充来自 json 请求的值时遇到了问题 request.Data 和一个名为 scaffolded_model 的空模型。据我所知,两组数据都设置正确。代码应该 运行 通过请求中的每个值,在空模型中通过 item.key 匹配它,并用该值填充匹配的键。 item.key 每次都是空的。我尝试了不同的绑定等。如果我将第一个 item.key 硬编码为 JurisdictionCode,它会获取该值并正确填充它。所以一切正常,如果 item.key 会填充。

感谢您的关注和帮助。

    [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);

    }

如果您有充分的理由通过反射而不是使用标准 System.Text.Json 或 Newtonsoft.Json 库来实现自定义 JSON 解析,那么有几个问题需要解决:

  • Type.GetProperty 区分大小写。默认情况下,它不会将 jurisdictionCode 属性 名称匹配到 JurisdictionCode 属性。 BindingFlags.IgnoreCase 标志应该可以解决这个问题。
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 方法可以用作最简单的选项,它将处理 scaffolded_model class.
  • 中定义的 属性 类型
pi.SetValue(
  scaffolded_model, 
  Convert.ChangeType(item.Value.ToString(), pi.PropertyType));

通过这两项更改,可以解析 Json 表单示例。


但当前代码有一些限制:

  • 它不处理空值。在字符串 属性 的情况下,空字符串值将分配给 属性 而不是原始空值。完全不支持可空值类型(例如 long?)。要解决此问题,可以调整当前逻辑以检查 JsonElement.ValueKind 属性 的 JsonValueKind.Null 值。

  • 另一个问题是 DateTime 类型。在当前的实现中,所有 DateTime 值都将调整为本地时区,并且 Convert.ChangeType 方法不提供任何控制它的能力。将 DateTime 时间替换为 DateTimeOffset 将不起作用,因为 Convert.ChangeType 方法不支持它。只有选项可以检查 属性 类型并使用例如 DateTime.Parse 方法而不是 Convert.ChangeType 执行手动转换。

这个列表可以继续。所以在一般情况下,最好使用标准库来解析 Json.