Пользовательский Json Сериализация класса

У меня есть код, структурированный, как показано ниже.

public class Stats
{
        public string URL { get; set; }
        public string Status { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public int Length { get; set; }
}

и

 public class UrlStats
 {
        public string URL { get; set; }
        public int TotalPagesFound { get; set; }
        public List<Stats> TotalPages { get; set; }
        public int TotalTitleTags { get; set; }
        public List<Stats> TotalTitles { get; set; }
        public int NoDuplicateTitleTags { get; set; }
        public List<Stats> DuplicateTitles { get; set; }
        public int NoOverlengthTitleTags { get; set; }
        public List<Stats> OverlengthTitles { get; set; }
 }

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

Я использую JQuery и делаю вызовы AJAX в webservice и получая статистику URL-адресов, пока процесс запущен, чтобы показать статистику URL-адреса пользователей, собранную далеко, поскольку для сканирования большого веб-сайта требуется достаточно времени. Поэтому через каждые 5 секунд я получаю статистику с сервера. Теперь проблема - это все данные переменных List, которые мне нужно отправить в конце, когда обработка сканирования завершена, а не во время обновлений. Что происходит прямо сейчас, переменные данных List<Stats> также отправляются во время обновлений, которые представляют собой большой кусок данных, и я хочу отправлять только переменные типа int, которые необходимы для отображения обновлений процесса.

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

В основном я ищу сериализацию свойств в зависимости от их типа данных во время выполнения, если это возможно.

Ответы

Ответ 1

Существует два разных подхода к вашей проблеме.

Вы должны выбрать первый вариант, если вы собираетесь чаще менять свои классы, потому что первый подход позволяет забыть сериализовать недавно добавленное свойство. Кроме того, он гораздо более многоразовый, если вы хотите добавить еще один класс, который вы хотите сериализовать таким же образом.

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

1. Используйте настраиваемый конвертер, чтобы выбрать все свойства int

Первый подход заключается в использовании пользовательского JsonConverter, который сериализует класс или структуру, включая свойства, имеющие тип int. Код может выглядеть так:

class IntPropertyConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        // this converter can be applied to any type
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // we currently support only writing of JSON
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            serializer.Serialize(writer, null);
            return;
        }

        // find all properties with type 'int'
        var properties = value.GetType().GetProperties().Where(p => p.PropertyType == typeof(int));

        writer.WriteStartObject();

        foreach (var property in properties)
        {
            // write property name
            writer.WritePropertyName(property.Name);
            // let the serializer serialize the value itself
            // (so this converter will work with any other type, not just int)
            serializer.Serialize(writer, property.GetValue(value, null));
        }

        writer.WriteEndObject();
    }
}

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

[JsonConverter(typeof(IntPropertyConverter))]
public class UrlStats
{
    // ...
}

Отказ от ответственности: этот код был протестирован только очень грубо.


2. Выберите свойства индивидуально

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

Черный список: (я изменил свойства для лучшего обзора)

[JsonObject(MemberSerialization.OptOut)] // this is default and can be omitted
public class UrlStats
{
    [JsonIgnore] public string URL { get; set; }
    [JsonIgnore] public List<Stats> TotalPages { get; set; }
    [JsonIgnore] public List<Stats> TotalTitles { get; set; }
    [JsonIgnore] public List<Stats> DuplicateTitles { get; set; }
    [JsonIgnore] public List<Stats> OverlengthTitles { get; set; }

    public int TotalPagesFound { get; set; }
    public int TotalTitleTags { get; set; }
    public int NoDuplicateTitleTags { get; set; }
    public int NoOverlengthTitleTags { get; set; }
}

Белый список: (также переупорядочено)

[JsonObject(MemberSerialization.OptIn)] // this is important!
public class UrlStats
{
    public string URL { get; set; }
    public List<Stats> TotalPages { get; set; }
    public List<Stats> TotalTitles { get; set; }
    public List<Stats> DuplicateTitles { get; set; }
    public List<Stats> OverlengthTitles { get; set; }

    [JsonProperty] public int TotalPagesFound { get; set; }
    [JsonProperty] public int TotalTitleTags { get; set; }
    [JsonProperty] public int NoDuplicateTitleTags { get; set; }
    [JsonProperty] public int NoOverlengthTitleTags { get; set; }
}

Ответ 2

Ой, повторив свой вопрос, я думаю, вы можете сериализовать проекцию своих данных.

Вы можете попробовать следующее:

var json = JsonConvert.SerializeObject(new { u.TotalPagesFound, u.TotalTitleTags, u.NoDuplicateTitleTags, u.NoOverlengthTitleTags } );

Это преобразует в JSON только свойства int вашего класса. Это самый простой способ, и он привязан к структуре вашего класса. Если вам нужно что-то более общее, вам нужно будет реализовать собственный конвертер.

Оригинальный ответ:

Я не вижу проблем с вашими классами, у вас нет ничего странного, как ссылки на цикл, поэтому у Json.NET не должно быть проблем с сериализацией вашего класса. Итак, перейдите в Json.NET, а затем вы можете попробовать следующее

// create new instance of your url stat class
var u = new UrlStats() { URL = "a.com", TotalPages = new List<Stats>() { new Stats() { URL = "b.com", Status = "xxxx" } } };
// seralize!
var json = JsonConvert.SerializeObject(u);
Console.Write(json);

То, что я получаю с этим методом, выглядит примерно так:

{"URL":"a.com","TotalPagesFound":0,"TotalPages":[{"URL":"b.com","Status":"xxxx","Title":null,"Description":null,"Length":0}],"TotalTitleTags":0,"TotalTitles":null,"NoDuplicateTitleTags":0,"DuplicateTitles":null,"NoOverlengthTitleTags":0,"OverlengthTitles":null}

И это выглядит как хороший json для меня.

Надеюсь, что это поможет