通过 JsonProperty 属性值将对象列表投影到另一种类型

Projecting list of objects into another type by JsonProperty attribute value

我有以下型号:

class CheckResponse
{
    public ICollection<CheckModel> Checks { get; set; }
}

public class CheckModel
{
    [JsonConverter(typeof(StringEnumConverter))]
    public CheckCodes CheckCode { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    public ResultCode ResultCode { get; set; }
}

public enum CheckCodes
{
    FirstCheck,
    SecondCheck,
    ThirdCheck,
}

public enum ResultCode
{
    Failure,
    Success,
    Warning
}

我需要将 Checks 转换成 CheckList:

class CheckList
{
    [JsonProperty(nameof(CheckCodes.FirstCheck))]
    public bool FirstCheckPassed { get; set; }

    [JsonProperty(nameof(CheckCodes.SecondCheck))]
    public bool SecondCheckPassed { get; set; }

    [JsonProperty(nameof(CheckCodes.ThirdCheck))]
    public bool ThirdCheckPassed { get; set; }
}

示例:

class Program
{
    static void Main(string[] args)
    {
        var response = GetResponse();

        var checkResponse = JsonConvert.DeserializeObject<CheckResponse>(response);

        // var checkList = ?
    }

    static string GetResponse() => @"{
'checks': [
    {
        'checkCode': 0,
        'resultCode': 1
    },
    {
        'checkCode': 1,
        'resultCode': 2
    },
    {
        'checkCode': 2,
        'resultCode': 0
    }
]}";
}

如果 resultCode 等于 2 (ResultCode.Warning) 则应该通过检查。

因此,checkList 应具有以下属性值:

FirstCheckPassed = true

SecondCheckPassed = true

ThirdCheckPassed = false

更新:

我的解决方案如下:

static void Main(string[] args)
{
    Test();
}

static IEnumerable<string> Yield(ICollection<CheckModel> checks)
{
    foreach (var check in checks)
    {
        var success = check.ResultCode == ResultCode.Success || check.ResultCode == ResultCode.Warning;
        yield return "'" + check.CheckCode.ToString() + "': '" + success.ToString() + "'";
    }
}

static void Test()
{
    var response = GetResponse();

    var checkResponse = JsonConvert.DeserializeObject<CheckResponse>(response);

    var o = "{" + string.Join(",", Yield(checkResponse.Checks)) + "}";
    var checkList = JsonConvert.DeserializeObject<CheckList>(o);
}

但我不确定这是最好的(实际上,我认为它有点丑)。有没有更好的方法?

您可以像这样向您的 CheckResponse class 添加一个方法(请注意,这也可以是一个扩展方法,或者通常移动到其他地方以将代码排除在您的模型之外)

public bool CheckPassed(CheckCodes check)
{
  //default to failure if there isn't a matching check
  var result = Checks.FirstOrDefault(x => x.CheckCode == check)?.ResultCode ?? ResultCode.Failure;
  return result != ResultCode.Failure;
}

然后您可以在创建 CheckList

时使用该方法
static void Main()
{
  var response = GetResponse();
  var checkResponse = JsonConvert.DeserializeObject<CheckResponse>(response);

  var checkList = new CheckList
  {
    FirstCheckPassed = checkResponse.CheckPassed(CheckCodes.FirstCheck),
    SecondCheckPassed = checkResponse.CheckPassed(CheckCodes.SecondCheck),
    ThirdCheckPassed = checkResponse.CheckPassed(CheckCodes.ThirdCheck)
  };
}

您可以创建自己的 JsonConverter,通过对 POCO 进行额外更改,您可以反序列化为您想要的确切对象。

小改成CheckResponse

class CheckResponse
{
    [JsonConverter(typeof(CheckModelConverter))]
    public CheckList Checks { get; set; }
}

添加自定义转换器

public class CheckModelConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = serializer.Deserialize<CheckModel[]>(reader);
        var result = new CheckList();
        foreach (var item in obj)
        {
            bool resultValue = item.ResultCode != ResultCode.Failure;
            switch (item.CheckCode)
            {
                case CheckCodes.FirstCheck:
                    result.FirstCheckPassed = resultValue;
                    break;
                case CheckCodes.SecondCheck:
                    result.SecondCheckPassed = resultValue;
                    break;
                case CheckCodes.ThirdCheck:
                    result.ThirdCheckPassed = resultValue;
                    break;
                default:
                    throw new Exception("No checkcode of " + item.CheckCode);
            }
        }
        return result;  
    }

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

披露:我更喜欢将其实际反序列化为确切的模型,然后才将其(或其中的一部分)映射到我想要的类型,这为我提供了更多的未来灵活性并保留了实际的模型结构。

编辑: 修复了 Disclosure 中的拼写问题。