Проецирование списка объектов в другой тип по значению атрибута JsonProperty - PullRequest
0 голосов
/ 06 марта 2020

У меня есть следующие модели:

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

Но я не уверен, что это лучший (на самом деле, я считаю это немного уродливым). Есть ли лучшие подходы?

Ответы [ 2 ]

1 голос
/ 06 марта 2020

Вы можете создать свой собственный 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();
    }
}

Раскрытие информации: я буду больше любить фактически десериализовать его в точную модель, и только тогда отобразить его (или часть) в нужный тип, это даст мне больше гибкости в будущем и сохранение фактической структуры модели.

Редактировать: Исправлено написание в раскрытии.

1 голос
/ 06 марта 2020

Вы можете добавить метод к вашему классу CheckResponse, как этот (обратите внимание, что это также может быть метод расширения или вообще перенесен в другое место, чтобы исключить код из вашей модели)

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