Почему System.Text Json Serialiser не сериализует это обобщенное свойство c, а Json. NET делает? - PullRequest
4 голосов
/ 27 мая 2020

У меня следующая ситуация. Я упростил задачу в следующем примере, хотя моя реальная ситуация более сложна.

System.Text. Json не сериализует объект полностью, а Newtonsoft Json. NET делает.

Предположим, у меня есть следующая структура классов.

public class A
{
    public string AProperty { get; set; } = "A";
}

public class A<T> : A where T : class, new()
{
    public T TObject { get; set; } = new T();
}

public class B
{
    public string BProperty { get; set; } = "B";
}

public class B<T> : B where T : class, new()
{
    public T TObject { get; set; } = new T();
}

public class C
{
    public string CProperty { get; set; } = "C";
}

Вот простой. NET Основная программа:

public class Program
{
    private static void Main(string[] args)
    {
        var obj = new A<B> { TObject = new B<C>() };

        var systemTextSerialized = JsonSerializer.Serialize(obj);
        var newtonsoftSerialized = JsonConvert.SerializeObject(obj);
    }
}

Сериализованные результаты выглядят следующим образом:

System.Text. Json

{
  "TObject": {
    "BProperty": "B"
  },
  "AProperty": "A"
}

Newtonsoft

{
  "TObject": {
    "TObject": {
      "CProperty": "C"
    },
    "BProperty": "B"
  },
  "AProperty": "A"
}

Из-за структуры моего приложения, Я не знаю общий параметр c для B. Я знаю только, что это A<B>. Фактическое значение TObject из B неизвестно до времени выполнения.

Почему эти два метода сериализации различаются? Есть ли способ получить System.Text. Json для полной сериализации объекта, или мне нужно написать собственный конвертер?

1 Ответ

3 голосов
/ 27 мая 2020

Это задокументированное ограничение System.Text.Json. Из docs :

Сериализация свойств производных классов

Сериализация типа polymorphi c иерархия не поддерживается. Например, если свойство определено как интерфейс или абстрактный класс, сериализуются только свойства, определенные в интерфейсе или абстрактном классе, даже если тип среды выполнения имеет дополнительные свойства. В этом разделе объясняются исключения из этого поведения ....

Чтобы сериализовать свойства производного типа в предыдущем примере, используйте один из следующих подходов:

  1. Вызвать перегрузку Serialize, которая позволяет указать тип во время выполнения ...

  2. Объявить объект для сериализации как object.

В вашем случае A<B>.TObject объявлен как имеющий тип B, но на самом деле он имеет тип B<C> в создаваемом вами экземпляре, поэтому только свойства базового класса B сериализуются как согласно документации. Вот так. Для дальнейшего обсуждения см. Закрытую проблему System.Text. Json .JsonSerializer не сериализует свойства из производных классов # 31742 .

Доступно несколько обходных путей , тем не мение. Во-первых, вы можете построить obj как наиболее вероятный производный тип A<B<C>>:

var obj = new A<B<C>> { TObject = new B<C>() };

Теперь все свойства TObject сериализуются. Демо скрипка # 1 здесь . Но, к сожалению, вы не можете использовать этот обходной путь, поскольку Фактическое TObject из B неизвестно до времени выполнения.

В качестве альтернативы , если вам нужно только сериализовать obj, вы можете следовать предложению № 2 из документации и объявить суррогатное свойство object и сериализовать это:

public class A<T> : A where T : class, new()
{
    [System.Text.Json.Serialization.JsonPropertyName("TObject")]
    [Newtonsoft.Json.JsonIgnore]
    public object SerializedTObject => TObject;

    [System.Text.Json.Serialization.JsonIgnore]
    public T TObject { get; set; } = new T();
}

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

Демо-скрипт # 2 здесь .

Наконец, , если вам нужны полиморфные c сериализация и десериализация , вам нужно будет написать собственный JsonConverter. Для начала см.

...