A
A
Anton Rodionov2018-08-08 17:04:50
JSON
Anton Rodionov, 2018-08-08 17:04:50

How to correctly describe the class for the Newtonsoft.Json C# library?

Good afternoon, dear connoisseurs. There was such a question. My program uses the Newtonsoft.Json.dll library to deserialize a JSON file with a complex structure. The file itself is huge, I will not give it. I will show only the tree, my question should be clear from it.
5b6af1d85db18972255046.png
And here is the class that describes it:

public class Row
    {
        public List<string> row { get; set; }
        public List<object> sub { get; set; }
    }
public class RootObject
    {
        public string _id { get; set; }
        public string _rev { get; set; }
        public string type { get; set; }
        public string hid { get; set; }
        public string title { get; set; }
        public string name { get; set; }
        public List<Row> rows { get; set; }
        public List<string> cols { get; set; }
    }

And the code that calls the conversion:
string json = File.ReadAllText(pathToFile);
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);

The deserialization is successful, but the problem is that, for example, rows[0]sub[9] has a built-in row : and sub : array, while rows[0]sub[10] already has the necessary data. In this scenario, rows[0]sub[9] remains under-deserialized (woo!) data, which will need to be deserialized again.
5b6af6e0affc5926166291.png
Is there any elegant solution to describe the class in such a way that it can accept data of different types and, as a result, after conversion, all data is stored in it? I understand that I can cycle through the current version and deserialize again, creating new instances of the Row class to store data, but I would really like to avoid this. Thanks ^_^

Answer the question

In order to leave comments, you need to log in

2 answer(s)
V
VoidVolker, 2018-08-08
@Viper029

Elementary: https://app.quicktype.io/#r=json2csharp - insert JSON on the left, get a ready class for parsing on the right. The most convenient service.
Example:

{
  "greeting": "Welcome to quicktype!",
  "instructions": [
    "Type or paste JSON here",
    "Or choose a sample above",
    "quicktype will generate code in your",
    "chosen language to parse the sample data"
  ]
}

namespace QuickType
{
    using System;
    using System.Collections.Generic;

    using System.Globalization;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;
    using J = Newtonsoft.Json.JsonPropertyAttribute;
    using R = Newtonsoft.Json.Required;
    using N = Newtonsoft.Json.NullValueHandling;

    public partial class Welcome
    {
        [J("greeting")]     public string Greeting { get; set; }      
        [J("instructions")] public string[] Instructions { get; set; }
    }

    public partial class Welcome
    {
        public static Welcome FromJson(string json) => JsonConvert.DeserializeObject<Welcome>(json, QuickType.Converter.Settings);
    }

    public static class Serialize
    {
        public static string ToJson(this Welcome self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
    }

    internal static class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters = {
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }
}

I take out the converter in a separate class and file:
using Newtonsoft.Json;

    /// <summary>
    /// Конвертер JSON 
    /// </summary>
    public static class Converter
    {
        /// <summary>
        /// Настройки конвертации JSON
        /// </summary>
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,     // Пропускаем аттрибуты
            DateParseHandling = DateParseHandling.None,                     // Выключаем парсинг дат
            NullValueHandling = NullValueHandling.Ignore                    // Пропускаем пустые значения 
                                                                            // (API-вызов при создании запроса 
                                                                            // сам заполняет нужные поля)
        };
    }

And I put JSON parsing/converting into a separate class, from which I already inherit classes for data parsing:
/// <summary>
    /// Абстрактный класс - сетевое сообщение: парсинг и 
    /// генерация JSON из экземпляров наследуемого класса
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class APIMessage<T>
    {
        /// <summary>
        /// Конвертация в JSON
        /// </summary>
        /// <returns></returns>
        public string ToJson()
        {
            return JsonConvert.SerializeObject(this, Converter.Settings);
        }

        /// <summary>
        /// Парсинг JSON в указанный тип
        /// </summary>
        /// <param name="json"></param>
        /// <returns></returns>
        public static T FromJson(string json)
        {
            try
            {
                return JsonConvert.DeserializeObject<T>(json, Converter.Settings);
            }
            catch (Exception e)
            {                
                throw new ServerConnectionException(
                    "Server response parse error!\nResponse type: <" + typeof(T).FullName
                    + ">\nError: " + e.Message
                );
            }
        }
    }

Somehow it goes like this:
public class Request : APIMessage<Request>
{
    <описание JSON свойств>
}

Z
Zelimkhan Beltoev, 2018-08-08
@Beltoev

With such a heterogeneous file structure, you need to write a custom converter:
Inherit from JsonConverterand override the method ReadJsonin which you check the type of the next deserialized object and, depending on it, create an instance of Rowor a typed class for data. Thus, any nesting rowscan be correctly processed.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question