簡體   English   中英

使用嵌套JSON對象進行模型綁定

[英]Model Binding with Nested JSON Objects

我正在編寫一個端點來接受來自第三方的webhook上的POST請求,並且他們發送的數據是JSON編碼的主體。 所以,我無法控制發送給我的數據,我需要處理它。 我的問題是他們在他們的JSON中做了很多嵌套,因為我只使用他們發送給我的一些鍵,我不想創建一堆不必要的嵌套模型來獲取我想要的數據。 這是一個示例有效負載:

{
    id: "123456",
    user: {
        "name": {
            "first": "John",
            "Last": "Doe"
        }
    },
    "payment": {
        "type": "cash"
    }
}

我想把它放在一個看起來像這樣的模型中:

public class SalesRecord
{
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public string PaymentType {get; set;}
}

端點示例(目前還不多):

[HttpPost("create", Name = "CreateSalesRecord")]
public ActionResult Create([FromBody] SalesRecord record)
{
    return Ok(record);
}

我過去的工作一直在Phalcon PHP框架中,我通常只是直接訪問POST Body並自己設置模型中的值。 我當然看到模型綁定的優點,但我不明白如何正確解決這種情況。

對於這樣的場景,需要一個自定義模型綁定器。 該框架允許這種靈活性。

使用此處提供的演練

定制模型粘合劑樣品

並使其適應這個問題。

下面的示例使用ModelBinder的屬性SalesRecord模型:

[ModelBinder(BinderType = typeof(SalesRecordBinder))]
[JsonConverter(typeof(JsonPathConverter))]
public class SalesRecord {
    [JsonProperty("user.name.first")]
    public string FirstName {get; set;}
    [JsonProperty("user.name.last")]
    public string LastName {get; set;}
    [JsonProperty("payment.type")]
    public string PaymentType {get; set;}
}

在上面的代碼中, ModelBinder屬性指定了應該用於綁定SalesRecord操作參數的IModelBinder的類型。

SalesRecordBinder用於通過嘗試使用自定義JSON轉換器解析發布的內容來綁定SalesRecord參數,以簡化去酶化。

class JsonPathConverter : JsonConverter {
    public override object ReadJson(JsonReader reader, Type objectType, 
                                    object existingValue, JsonSerializer serializer) {
        JObject jo = JObject.Load(reader);
        object targetObj = Activator.CreateInstance(objectType);

        foreach (PropertyInfo prop in objectType.GetProperties()
                                                .Where(p => p.CanRead && p.CanWrite)) {
            JsonPropertyAttribute att = prop.GetCustomAttributes(true)
                                            .OfType<JsonPropertyAttribute>()
                                            .FirstOrDefault();

            string jsonPath = (att != null ? att.PropertyName : prop.Name);
            JToken token = jo.SelectToken(jsonPath);

            if (token != null && token.Type != JTokenType.Null) {
                object value = token.ToObject(prop.PropertyType, serializer);
                prop.SetValue(targetObj, value, null);
            }
        }
        return targetObj;
    }

    public override bool CanConvert(Type objectType) {
        // CanConvert is not called when [JsonConverter] attribute is used
        return false;
    }

    public override bool CanWrite {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, 
                                    JsonSerializer serializer) {
        throw new NotImplementedException();
    }
}

來源:我可以在屬性中指定一個路徑,將我的類中的屬性映射到我的JSON中的子屬性嗎?

public class SalesRecordBinder : IModelBinder {

    public Task BindModelAsync(ModelBindingContext bindingContext) {
        if (bindingContext == null){
            throw new ArgumentNullException(nameof(bindingContext));
        }

        // Try to fetch the value of the argument by name
        var valueProviderResult = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName);

        if (valueProviderResult == ValueProviderResult.None){
            return Task.CompletedTask;
        }

        var json = valueProviderResult.FirstValue;

        // Check if the argument value is null or empty
        if (string.IsNullOrEmpty(json)) {
            return Task.CompletedTask;
        }

        //Try to parse the provided value into the desired model
        var model = JsonConvert.DeserializeObject<SalesRecord>(json);

        //Model will be null if unable to desrialize.
        if (model == null) {
            bindingContext.ModelState
                .TryAddModelError(
                    bindingContext.ModelName,
                    "Invalid data"
                );
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, model);

        //could consider checking model state if so desired.

        //set result state of binding the model
        bindingContext.Result = ModelBindingResult.Success(model);
        return Task.CompletedTask;
    }
}

從那里開始,現在應該是在動作中使用模型的簡單問題

[HttpPost("create", Name = "CreateSalesRecord")]
public IActionResult Create([FromBody] SalesRecord record) {
    if(ModelState.IsValid) {
        //...
        return Ok();
    }

    return BadRequest(ModelState);
}

免責聲明:尚未經過測試。 可能還有一些問題需要解決,因為它基於上面提供的鏈接源。

注意 :這假定JSON輸入始終有效。 如果不是這樣,你將不得不添加一些檢查。

如果您不想使其過於復雜,可以使用DLR的幫助。 NewtonSoft.Json序列化程序允許您反序列化為dynamic對象:

[HttpPost]
public IActionResult CreateSalesRecord([FromBody]dynamic salesRecord)
{
    return Ok(new SalesRecord
    {
        FirstName = salesRecord.user.name.first,
        LastName = salesRecord.user.name.Last,
        PaymentType = salesRecord.payment.type
    });
}
[HttpPost]
    public IActionResult Json(string json)
    {
        JObject j = JObject.Parse(json);
        MyModel m = j.ToObject<MyModel>();
        return View();
    }

如果你的Json是字符串格式,你可以嘗試這個。 我相信它的工作你的數據模型必須是Json的精確表示。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM