PropertyInfo getProperty() 方法的问题

Issue with PropertyInfo getProperty() Method

提问人:user1615351 提问时间:8/3/2021 最后编辑:user1615351 更新时间:8/4/2021 访问量:570

问:

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 请求中的值时遇到了问题。数据和一个名为 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);

    }
C# API 属性 映射 PropertyInfo

评论

0赞 Charlieface 8/3/2021
我不明白:你为什么不直接反序列化到你的类型中,你不应该自己编写反序列化:.我注意到序列化然后立即反序列化 JSON 显然是没有意义的,也许您只是在测试。如果是这样,您在顶部提供的 JSON 显然不匹配,因为它也具有外部根对象。scaffolded_modelJsonSerializer.Deserialize<scaffolded_model>(data)
0赞 Charlieface 8/3/2021
您是否正在接收不同的 JSON,或者顶部的 JSON 是否真的是您想要反序列化的 JSON?此外,你的代码无论如何都是坏的:应该只是item.Value.ToString()item.Value
0赞 user1615351 8/4/2021
嗨,CharlieFace,我像你指出的那样清理了请求。此外,如果可能的话,我很想直接反序列化到我的模态中。不过,我不太确定该怎么做。使用 Botan 的以下建议,我现在可以传递字符串,并且需要处理 Datetime 和 null 值,我不知道该怎么做。
0赞 Charlieface 8/4/2021
对不起,仍然没有得到它:为什么你不能像我展示的那样反序列化:JsonSerializer.Deserialize<scaffolded_model>(data)
0赞 user1615351 8/4/2021
嗨,CharlieFace,很抱歉造成混淆,基本上,这是一个 API 调用,我将为您提供所有代码。当我尝试接受进来的请求时,就是请求。数据,我尝试直接反序列化到新创建的模型,我得到了一些错误。不知道出了什么问题:

答:

1赞 Botan 8/3/2021 #1

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

  • Type.GetProperty 区分大小写。默认情况下,它不会将属性名称与属性匹配。BindingFlags.IgnoreCase 标志应该可以解决此问题。jurisdictionCodeJurisdictionCode
PropertyInfo pi = scaffolded_model
  .GetType()
  .GetProperty(
     item.Key, 
     BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
  • 第二个问题与类型转换有关。PropertyInfo.SetValue 不执行任何类型转换。需要设置的值必须与属性类型匹配,否则将被抛出。此时,问题中的代码始终将方法返回的字符串值设置为所有属性。它不适用于 或 属性。若要解决此问题,可以将 Convert.ChangeType 方法用作处理类中定义的属性类型的最简单选项。TargetExceptionitem.Value.ToString()longDateTimescaffolded_model
pi.SetValue(
  scaffolded_model, 
  Convert.ChangeType(item.Value.ToString(), pi.PropertyType));

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


但当前代码有一些局限性:

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

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

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

评论

0赞 user1615351 8/3/2021
嗨,博坦,感谢您的帮助。由于存在冲突的问题,我们不得不从项目中删除 NewtonSoft,并且我们在另一个项目中使用脚手架模型库,这使得这变得更加困难。至于 Nulls 和 DateTime 的问题,我理解你在说什么,你能详细说明我如何修改我的代码来处理这两个问题吗?此外,该项目。我的代码中的 Value.ToString() 添加只是我测试的东西,除非必须,否则不需要在那里。再次感谢您的所有帮助。
0赞 Botan 8/4/2021
嗨,@user1615351,我看到您已经更新了问题并包含了控制器操作的实际代码。现在,您将要实现的目标变得更加清晰。我强烈建议在这种情况下避免反思,它会产生更多的问题而不是解决任何问题。最简单、最可靠的解决方案是根据原始请求手动填充scaffolded_model属性。仅当 scaffolded_model 的名称和类型属性按名称和类型完全匹配原始请求的属性时,反序列化的技巧才有效。
0赞 Botan 8/4/2021
嗨,@user1615351,您能否尝试使用以下语句反序列化scaffolded_model: 其中 jsonString 是需要反序列化为 scaffolded_model 的实际 JSON。根据您的示例,模型和示例 JSON 中的属性名称的大小写不同。设置为 true 的选项将允许不区分大小写的属性匹配。API.CRUD.Claims.Model.Claim scaffolded_model = JsonSerializer.Deserialize<API.CRUD.Claims.Model.Claim>(jsonString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });PropertyNameCaseInsensitive
0赞 user1615351 8/7/2021
感谢您提供信息Botan。我的代码现在可以工作了。