JsonConvert строка в коллекцию - PullRequest
0 голосов
/ 25 апреля 2018

Я получаю этот формат JSON от API:

"url_service": "",
"Description": null,
"Target": "5,6",
"Category ": "2"

Я пытаюсь десериализовать JSON в модель.Проблема с полем "Target", которое должно быть ICollection int.Вот моя модель:

public string Description{ get; set; }
public ICollection<int> Target{ get; set; }
public int Category { get; set; }

Есть ли способ обработать Json до его сериализации, чтобы сделать коллекцию из строки, разделенной запятыми?

Ответы [ 4 ]

0 голосов
/ 26 апреля 2018

Поле Target имеет тип строки в вашем json, поэтому сериализатор попытается прочитать его как строку.Вы можете использовать конвертер для отмены этого, например, используя Newtonsoft Json .

Давайте предположим, что ваша структура данных будет определена следующим образом:

public class Data {
    public string Description{ get; set; }
    public ICollection<int> Target{ get; set; }
    public int Category { get; set; }
}

Тогда вы можетесоздайте свой собственный JsonConverter следующим образом:

public class DataConverter : JsonConverter {

    public override bool CanWrite => false;

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {

        var jObject = JObject.Load(reader);

        var data = new Data();

        if (jObject.TryGetValue("Description", out JToken jDescription)) {
            data.Description = jDescription.Value<string>();
        }
        if (jObject.TryGetValue("Target", out JToken jTarget)) {
            data.Target = ToTarget(jTarget, serializer);
        }
        if (jObject.TryGetValue("Category", out JToken jCategory)) {
            data.Category = jCategory.Value<int>();
        }

        return req;

    }

    private static ICollection<int> ToTarget( JToken jTarget, JsonSerializer serializer ) {

        int defaultValue = -1;
        var target = new List<int>();
        var collection = jTarget.Value<string>().Split(',');
        foreach (string str in collection)
            if (int.TryParse(str, out int number))
                target.Add(number);
            else
                target.Add(defaultValue);

        return target;

    }

    public override bool CanConvert(Type objectType) => objectType == typeof(Data);

}

Затем вы можете десериализовать, используя следующий код:

var jsonSettings = new JsonSerializerSettings();
jsonSettings.Converters.Add(new DataConverter);
Data data = JsonConvert.DeserializeObject<Data>(yourJsonString, jsonSettings);

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

0 голосов
/ 25 апреля 2018

Вместо того, чтобы пытаться изменить логику десериализации, почему бы не упростить для себя и просто включить новое свойство в свою модель?

public string Description{ get; set; }
public int Category { get; set; }

//Change this back to string, since that is what your JSON is
public string Target{ get; set; }

//Add a "TargetList" property that just reads from "Target" and turns into a List
public List<int> TargetList => Target.Split(',').Select(x => int.Parse(x)).ToList();

Помните, что в моем коде нет обработки ошибок, поэтомувам придется изменить соответственно.

0 голосов
/ 25 апреля 2018

Ваш JSON-фрагмент описывает не коллекцию целых чисел, а строку.Правильным будет

"Target": [ 5, 6 ],

, что означает схему JSON

 "Target": {
     "type": ["array"],
     "items": { "type": "integer"}
    },

Если у вас нет контроля над результатом, тогдасоздайте другое свойство, которое будет результатом целых чисел, таких как

   private string _target;
   public string Target {  get { return _target; } 
                           set { 
                                 _target = value; 
                                 Targets = Regex.Matches(_target, @"(\d+)")
                                                .OfType<Match>()
                                                .Select(mt => int.Parse(mt.Value))
                                                .ToList();
   public List<int> Targets { get; set; }
0 голосов
/ 25 апреля 2018

Да, ваш класс C # реализует интерфейс ISerializable. Существуют функции OnDeserialized (), OnSerialized (), которые вы можете реализовать.

См. Десериализация .NET с OnDeserializing и OnDeserialized

...