Как я могу использовать имя объекта в массиве JSON в качестве значения свойства, используя JSON. NET? - PullRequest
2 голосов
/ 29 апреля 2020

У меня есть следующий JSON блок (извлеченный из полного JSON для упрощения вопроса):

 "statistics": [
  {
    "Strength": {
      "min": 16,
      "max": 20
    }
  },
  {
    "Dexterity": {
      "min": 16,
      "max": 20
    }
  }]

Я бы хотел десериализовать массив «statistics» в C# массив объектов "Statisti c", но я не могу найти, как это сделать ... Ключ каждого объекта statisti c может быть любым: "Сила", "Удача", "Ловкость" и и так далее, и каждый из этих ключей уникален.

Объект данных C# будет выглядеть так:

public class Container
{
    public Statistic[] Statistics { get; set; }
}

public class Statistic
{
    //Contains the name of the statistic
    public string Name { get; set; }
    public int Min { get; set; }
    public int Max { get; set; }
}

Может быть, я могу использовать какой-то полиморфизм, удалив Name свойство и создание всевозможных классов статистики, но оно отключает адаптивность моего кода.

Большое спасибо.

Ответы [ 2 ]

2 голосов
/ 29 апреля 2020

Вы можете справиться с этим хитрым JSON, используя простой JsonConverter для вашего Statistic класса, например:

class StatisticConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Statistic);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        JProperty prop = jo.Properties().First();
        Statistic stat = new Statistic
        {
            Name = prop.Name,
            Min = (int)prop.Value["min"],
            Max = (int)prop.Value["max"]
        };
        return stat;
    }

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

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

Чтобы использовать его, добавьте атрибут [JsonConverter] к Statistic класс, и он должен работать как положено:

[JsonConverter(typeof(StatisticConverter))]
public class Statistic
{
    public string Name { get; set; }
    public int Min { get; set; }
    public int Max { get; set; }
}

Вот рабочая демонстрация: https://dotnetfiddle.net/H7wR0g

Если вы не можете (или не хотите) добавить атрибут класса Statistic, вы также можете использовать конвертер, передав его методу DeserializeObject следующим образом:

var container = JsonConvert.DeserializeObject<Container>(json, new StatisticConverter());

Fiddle: https://dotnetfiddle.net/UTLzBk

1 голос
/ 29 апреля 2020

Учитывая предоставленную JSON, мы не можем напрямую go для структуры Dictionary, это потому, что у нас есть массив объектов, где у каждого объекта есть имя Statistic в качестве имени свойства (в то время как это должно быть значением свойства с именем Name)

Чтобы избежать работы с пользовательскими конвертерами, вот быстрое (не готовое к производству!) решение, которое может дать вам хорошую отправную точку. Идея состоит в том, чтобы десериализовать его как dynamic, затем извлечь объект statistics как JArray и построить оттуда структуру результатов.

Обратите внимание, что я ничего не проверяю на ноль, так что это работает, пока вход правильно сформирован (вам нужно Newtonsoft.Json от NuGet).

using System;
using System.Linq;
using Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace StackOverflow
{
    internal class Program
    {
        private static readonly string _inputJson =
            @"
{
    ""statistics"": [
        {
            ""Strength"": {
                ""min"": 16,
                ""max"": 20
            }
        },
        {
            ""Dexterity"": {
                ""min"": 16,
                ""max"": 20
            }
        }]
}
";

        private static void Main(string[] args)
        {
            var tempObject = JsonConvert.DeserializeObject<dynamic>(_inputJson).statistics as JArray;

            var result = new Container
            {
                Statistics = tempObject.Select(obj =>
                {
                    var token = obj.First as JProperty;
                    var stats = token.Value;
                    return new Statistic
                    {
                        Name = token.Name,
                        Min = Convert.ToInt32((stats["min"] as JValue).Value),
                        Max = Convert.ToInt32((stats["max"] as JValue).Value)
                    };
                }).ToArray()
            };

            foreach (var stat in result.Statistics) Console.WriteLine($"{stat.Name} = ({stat.Min}, {stat.Max})");
        }
    }
}

namespace Models
{
    public class Container
    {
        public Statistic[] Statistics { get; set; }
    }

    public class Statistic
    {
        //Contains the name of the statistic
        public string Name { get; set; }
        public int Min { get; set; }
        public int Max { get; set; }
    }
}

Это вывод, который я получаю:

Сила = (16, 20)

Ловкость = (16, 20)

...