class 属性 在 c# 中解析 json 数据时的操作

class property manipulation when parsing json data in c#

我正在练习网络 api。我的目标是创建一个 Get 端点,它从外部 api 接收数据,然后 return 一个不同的结果。外部 api link: https://www.themealdb.com/api/json/v1/1/search.php?f=a,外部 api 数据如下所示:

{
  "meals": [
    {
      "idMeal": "52768",
      "strMeal": "Apple Frangipan Tart",
      "strDrinkAlternate": null,
      "strCategory": "Dessert",
      .....
    },
    {
      "idMeal": "52893",
      "strMeal": "Apple & Blackberry Crumble",
      ....
     }
   ]
}

我希望我的端点提供不同的结果,如下所示:

[
    {
      "idMeal": "52768",
      "strMeal": "Apple Frangipan Tart",
      "ingredients": ["Apple", "sugar"...]
    },
    {
      "idMeal": "52893",
      "strMeal": "Apple & Blackberry Crumble",
      "ingredients": ["Apple", "sugar"...]
     }
 ] 

下面的代码是我到目前为止尝试的代码,它正在工作,但是当我将 属性 成分 1 从 public 更改为私有时,列表中的成分将变为空,还有,配料那么多,有的默认是null,如果是null我就不想加了,请问这两个问题怎么解决?非常感谢

using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc;
using RestSharp;

namespace testAPI.Controllers;
public class Content
{
    [JsonPropertyName("meals")]
    public List<Meal> Meals { get; set; }
}

public class Meal
{
    [JsonPropertyName("idMeal")]
    public string MealId { get; set; }
    [JsonPropertyName("strMeal")]
    public string Name { get; set; }
    [JsonPropertyName("strIngredient1")]
    public string Ingredient1 { get; set; }
    [JsonPropertyName("strIngredient2")]
    public string Ingredient2 { get; set; }
    [JsonPropertyName("strIngredient20")]
    public string Ingredient20 { get; set; }

    public List<string> Ingredients
    {
        get { return new List<string>(){Ingredient1, Ingredient2, Ingredient20};} 
    }
}

[ApiController]
[Route("api/[controller]")]
public class DishesController : ControllerBase
{
    
    [HttpGet]
    public async Task<IActionResult> GetAllRecipes()
    {
        var client = new RestClient($"https://www.themealdb.com/api/json/v1/1/search.php?s=");
        var request = new RestRequest();
        var response = await client.ExecuteAsync(request);
        var mealList = JsonSerializer.Deserialize<Content>(response.Content);
        
        return Ok(mealList.Meals);
    }
    
}

一次解决一个问题...

the moment I changed property ingredient1 from public to private, that ingredient in list will become null

更改访问修饰符会同时影响反序列化和序列化,因此这不能仅用于阻止它序列化 属性。您应该将数据模型拆分为您想要接收的内容和您想要接收的内容 expose/return.

there are so many ingredients, some of them are null by default, I don't want to add them if they are null

除了拆分数据模型之外,您还可以在从一个模型映射到另一个模型时处理此问题。

以下代码应该可以解决这两个问题:

namespace TheMealDb.Models
{
    // These are the models you receive from TheMealDb
    // JSON converted to classes with https://json2csharp.com/
    public class Root
    {
        public List<Meal> meals { get; set; }
    }
    
    public class Meal
    {
        public string idMeal { get; set; }
        public string strMeal { get; set; }
        public string strIngredient1 { get; set; }
        public string strIngredient2 { get; set; }
        public string strIngredient3 { get; set; }
        // Other properties removed for brevity...
    }
}

namespace Internal.Models
{
    // This is the model you want to return from your controller action
    public class Meal
    {
        [JsonPropertyName("id")] // No need to use the same name as from themealdb
        public string Id { get; set; }
        [JsonPropertyName("name")]
        public string Name { get; set; }
        [JsonPropertyName("ingredients")]
        public List<string> Ingredients { get; set; }
    }
}

现在,要获取、映射和 return 控制器操作中的数据:

[HttpGet]
public async Task<IActionResult> GetAllRecipes()
{
    var client = new RestClient($"https://www.themealdb.com/api/json/v1/1/search.php?s=");
    var request = new RestRequest();
    var response = await client.ExecuteAsync(request);

    // Deserialize to the "TheMealDb" models
    var mealList = JsonSerializer.Deserialize<TheMealDb.Models.Root>(response.Content);
    // Map to your own models
    var myMealList = mealDbList.meals?.Select(MapToInternal);
        
    return Ok(myMealList);
}

// Map "TheMealDb" model to your own model
private Internal.Models.Meal MapToInternal(TheMealDb.Models.Meal externalMeal)
{
    return new Internal.Models.Meal
    {
        Id = externalMeal.idMeal,
        Name = externalMeal.strMeal,
        Ingredients = new []
            {
                externalMeal.strIngredient1,
                externalMeal.strIngredient2,
                externalMeal.strIngredient3,
                // ...
            }
            // Remove empty/null ingredients
            .Where(ingr => !string.IsNullOrEmpty(ingr))
            .ToList()
    };
}

参见 the code in action