Десериализовать JSON-файл с несколькими типами данных для ключа - PullRequest
1 голос
/ 19 мая 2019

Я хотел проанализировать Telegram-Chats, поэтому я экспортировал чат в формате JSON и хотел десериализовать его в свое аналитическое программное обеспечение.

    {
      "id": 397910,
      "type": "message",
      "date": "2018-02-21T10:27:59",
      "edited": "1970-01-01T01:00:00",
      "from": "Username",
      "from_id": 39033284,
      "text": "Some Text"
    }

Поэтому я использовал этот простой код для чтения JSON

    List<JSONObject> jsonObjects = JsonConvert.DeserializeObject<List<JSONObject>>(File.ReadAllText(openFileDialog.FileName));

    public class JSONObject
    {
       public int ID;
       public string type;
       public string date;
       public string edited;
       public string from;
       public int fromID;
       public string photo;
       public int width;
       public int height;
       public string text;
    }

Это было очень хорошо для первых 525 наборов данных, но после этого у меня возникли проблемы с десериализацией данных из-за "проблем согласованности".Тип данных текста иногда меняется на массив.

    {
       "id": 397911,
       "type": "message",
       "date": "2018-02-21T10:31:47",
       "edited": "1970-01-01T01:00:00",
       "from": "Username",
       "from_id": 272964614,
       "text": [
          "Some Text ",
          {
             "type": "mention",
             "text": "@school"
          },
          " Some Text"
       ]
    }

Кроме того, я нашел этот набор данных

    {
       "id": 397904,
       "type": "message",
       "date": "2018-02-21T10:18:12",
       "edited": "1970-01-01T01:00:00",
       "from": "Username",
       "from_id": 39033284,
       "text": [
          {
             "type": "link",
             "text": "google.com"
          },
          "\n\nSome Text"
        ]
    }

Я не знаю, как десериализовать данные, когда он показывает этот виднесоответствия.

1 Ответ

6 голосов
/ 19 мая 2019

поскольку ваше свойство сложное, вам нужно написать собственную логику десериализации.

Вот мой, но это всего лишь пример:

  • Прежде всего, ваше текстовое свойство, кажется,
    • Одно значение
    • Или массив значений

В этом случае я буду использовать результат «всегда список», случай с одним значением будет просто списком с одной записью.

public List<TextProperty> text;
  • Значение также может быть
    • Значение одной строки
    • Объект со строковым значением и метаданным (тип текста)

Опять же, я пойду на "всегда объект" без типа, если это только строка

public class TextProperty
{
    public string text { get; set; }
    public string type { get; set; }
}

Затем вам нужно создать собственный конвертер, чтобы справиться с этим, вам просто нужно наследовать от JsonConverter и реализовать логику

public class TextPropertyConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException(); // not covered here
    }

    // A value can be either single string or object
    // Return a TextProperty in both cases
    private TextProperty ParseValue(JToken value) 
    {
        switch(value.Type)
        {
            case JTokenType.String:
                return new TextProperty { text = value.ToObject<string>() };

            case JTokenType.Object:
                return value.ToObject<TextProperty>();

            default:
                return null;
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // You'll start either with a single value (we'll convert to list of one value) or an array (list of several values then)
        switch(reader.TokenType)
        {
            case JsonToken.String:
            case JsonToken.StartObject:
                return new List<TextProperty> { ParseValue(JToken.Load(reader)) };

            case JsonToken.StartArray:
                var a = JArray.Load(reader);
                var l = new List<TextProperty>();
                foreach(var v in a)
                    l.Add(ParseValue(v));
                return l;

            default:
                return null;
        }
    }

    public override bool CanConvert(Type objectType) => false;
}

Я думаю, что все случаи должны быть покрыты

Чтобы использовать его, просто добавьте атрибут JsonConverter к целевому свойству

public class JSONObject
{
    public int id;
    public string type;
    public string date;
    public string edited;
    public string from;
    public int from_id;
    public string photo;
    public int width;
    public int height;

    [JsonConverter(typeof(TextPropertyConverter))]
    public List<TextProperty> text;
}

А затем проверьте это:

static void Main(string[] args)
    {
        string json = @"
        [
            {
              ""id"": 397910,
              ""type"": ""message"",
              ""date"": ""2018-02-21T10:27:59"",
              ""edited"": ""1970-01-01T01:00:00"",
              ""from"": ""Username"",
              ""from_id"": 39033284,
              ""text"": ""Some Text""
            },

            {
               ""id"": 397911,
               ""type"": ""message"",
               ""date"": ""2018-02-21T10:31:47"",
               ""edited"": ""1970-01-01T01:00:00"",
               ""from"": ""Username"",
               ""from_id"": 272964614,
               ""text"": [
                  ""Some Text "",
                  {
                     ""type"": ""mention"",
                     ""text"": ""@school""
                  },
                  "" Some Text""
               ]
            }
        ]";

        List<JSONObject> jsonObjects = JsonConvert.DeserializeObject<List<JSONObject>>(json);

        Console.Read();
    }

Вот результаты:

Tada

...