Почему JsonSerializerContext генерирует пустой JSON для модели, содержащей свойства [ObservableProperty]?

У меня есть модели, которые включают наблюдаемые свойства , генерируемые автоматически из полей [ObservableProperty] с помощью набора инструментов CommunityToolkit.Mvvm , и я хотел бы сериализовать их с помощью System.Text.Json JsonSerializerContext. Но когда я это делаю, все мои свойства отсутствуют. Как я могу это исправить?

Вот мой код:

using CommunityToolkit.Mvvm.ComponentModel;
using System.IO.Ports;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;

namespace WinFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var _result = JsonSerializer.Serialize<EmptyConnection>(new EthernetConnection() {  IP = "192.168.125.201",Port=9100}, new JsonSerializerOptions()
            {
                Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All),
                TypeInfoResolver = EmptyConnectionGenerationContext.Default,
                IgnoreReadOnlyProperties = true,
            });
        }

        [JsonDerivedType(typeof(EmptyConnection), typeDiscriminator: "EmptyConnection")]
        [JsonDerivedType(typeof(SerialPortConnection), typeDiscriminator: "SerialPortConnection")]
        [JsonDerivedType(typeof(EthernetConnection), typeDiscriminator: "EthernetConnection")]
        public partial class EmptyConnection : ObservableObject
        {

        }
        public partial class SerialPortConnection : EmptyConnection
        {
            [ObservableProperty]
            string _PortName = "";

            [ObservableProperty]
            int? _BaudRate = null;

            [ObservableProperty]
            Parity? _Parity = null;

            [ObservableProperty]
            int? _DataBits = null;
            [ObservableProperty]
            StopBits? _StopBits = null;
            [ObservableProperty]
            string _PortFeature = "";
        }
        public partial class EthernetConnection : EmptyConnection
        {
            [ObservableProperty]
            string _IP = "";

            [ObservableProperty]
            int _Port = 0;

        }
        [JsonSerializable(typeof(EmptyConnection))]
        [JsonSerializable(typeof(SerialPortConnection))]
        [JsonSerializable(typeof(EthernetConnection))]
        [JsonSerializable(typeof(string))]
        [JsonSerializable(typeof(int))]
        [JsonSerializable(typeof(int?))]
        [JsonSerializable(typeof(Parity?))]
        [JsonSerializable(typeof(StopBits?))]
        public partial class EmptyConnectionGenerationContext : JsonSerializerContext
        {

        }
    }
}

После запуска программа генерирует пустой Json: {"$type":"EthernetConnection"}

Если я не использую TypeInfoResolver в JsonSerializerOptions, он успешно сгенерирует Json следующим образом:

{"$type":"EthernetConnection","IP":"192.168.125.201","Port":9100}

Что не так с моим кодом?

Я не знаю, что здесь происходит, и у меня нет инструментов для немедленного воспроизведения вашей проблемы, но у меня есть предположение: оба JsonSerializerContext и [ObservableProperty] работают через генераторы исходного кода . ...

dbc 13.04.2024 17:19

... Но я не уверен, можно ли объединить генераторы исходного кода в цепочку, чтобы код, сгенерированный одним, был виден другому. Если нет, то System.Text.Json не сможет сериализовать свойства, сгенерированные CommunityToolkit.Mvvm. . (И даже если бы их можно было объединить в цепочку, вам нужно было бы убедиться, что System.Text.Json запускается последним.)

dbc 13.04.2024 17:25

В настоящее время открыта проблема Разрешить способ гарантировать, что некоторые генераторы исходного кода запускаются до/после других #57239 предполагает, что не существует встроенного стандартного способа гарантировать, что генератор исходного кода System.Text.Json запускается последним.

dbc 13.04.2024 17:31

@pfx — свойства автоматически генерируются генераторами исходного кода CommunityToolkit.Mvvm , когда [ObservableProperty] применяется к полям. Таким образом, этот вопрос в основном касается управления взаимодействием между несколькими генераторами исходного кода из нескольких разных пакетов. (На самом деле это своего рода передовой край.)

dbc 13.04.2024 18:26

На самом деле я хочу преобразовать некоторые системные настройки в json, а затем сохранить их локально. Изначально это был проект WPF, и я воссоздал новый проект, чтобы свести к минимуму проблему восстановления (но я допустил ошибку, не используя также проект WPF). Я удалил тег ObservableProperty, как вы сказали, и заменил его на обычный get;set, после чего код работал нормально. Кажется, проблема кроется здесь. @dbc

Melon NG 13.04.2024 21:03
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
129
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

  • JsonSerializerContext System.Text.Json использует генераторы исходного кода для создания сериализаторов для указанных типов во время компиляции.
  • При применении к полю [ObservableProperty]CommunityToolkit.Mvvm использует генераторы источников для генерации наблюдаемого свойства для указанного поля.

Вы надеетесь, что эти два исходных генератора будут работать вместе, так что свойства, сгенерированные CommunityToolkit.Mvvm, будут подхвачены JsonSerializerContext и сериализованы, но, к сожалению, это, похоже, не реализовано. Код, сгенерированный одним генератором исходного кода, не виден другому генератору исходного кода при создании одного проекта. Для подтверждения см.:

Итак, какие у вас варианты?

Во-первых, вы можете вручную реализовать наблюдаемые свойства, следуя шаблону, показанному в документации, удалив [ObservableProperty] и добавив необходимые свойства для ваших полей. Например.

[ObservableProperty]
string _IP = "";

Станет

string _IP = "";
public string IP
{
    get => _IP;
    set => SetProperty(ref _IP, value);
}

Во-вторых, как вы уже заметили, вы можете использовать сериализацию на основе отражения, а не сериализацию на основе генерации источника:

var options = new JsonSerializerOptions()
{
    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All),
    //TypeInfoResolver = EmptyConnectionGenerationContext.Default, // REMOVE THIS
    IgnoreReadOnlyProperties = true,
};
var _result = JsonSerializer.Serialize<EmptyConnection>(new EthernetConnection() {  IP = "192.168.125.201", Port=9100 }, options);
Console.WriteLine(_result); // {"$type":"EthernetConnection","IP":"192.168.125.201","Port":9100}

Наконец, как предложил этот ответ Стивена Блома, вы можете извлечь свою модель данных и JsonSerializerContext в отдельные проекты, сделав проект контекста сериализации зависимым от проекта модели данных. Если вы это сделаете, вы гарантируете, что генераторы исходного кода для System.Text.Json увидят наблюдаемые свойства вашей модели данных, поскольку проект, содержащий вашу модель данных, уже будет создан. Тогда ваш проект WinFormsApp1 должен будет ссылаться на оба проекта, чтобы сериализовать вашу модель данных с помощью JsonSerializerContext.

Другие вопросы по теме