У меня есть модели, которые включают наблюдаемые свойства , генерируемые автоматически из полей [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}
Что не так с моим кодом?
... Но я не уверен, можно ли объединить генераторы исходного кода в цепочку, чтобы код, сгенерированный одним, был виден другому. Если нет, то System.Text.Json не сможет сериализовать свойства, сгенерированные CommunityToolkit.Mvvm. . (И даже если бы их можно было объединить в цепочку, вам нужно было бы убедиться, что System.Text.Json запускается последним.)
Я нашел такой вопрос: Можно ли объединить генераторы исходников в .net? на что ответ нет. Если это правильно, я не верю, что вы сможете объединить генерацию исходного кода System.Text.Json с генерацией исходного кода CommunityToolkit.Mvvm. В ответе Стивена Блома есть предложенный обходной путь, так что, возможно, вы могли бы попробовать.
В настоящее время открыта проблема Разрешить способ гарантировать, что некоторые генераторы исходного кода запускаются до/после других #57239 предполагает, что не существует встроенного стандартного способа гарантировать, что генератор исходного кода System.Text.Json запускается последним.
@pfx — свойства автоматически генерируются генераторами исходного кода CommunityToolkit.Mvvm , когда [ObservableProperty] применяется к полям. Таким образом, этот вопрос в основном касается управления взаимодействием между несколькими генераторами исходного кода из нескольких разных пакетов. (На самом деле это своего рода передовой край.)
На самом деле я хочу преобразовать некоторые системные настройки в json, а затем сохранить их локально. Изначально это был проект WPF, и я воссоздал новый проект, чтобы свести к минимуму проблему восстановления (но я допустил ошибку, не используя также проект WPF). Я удалил тег ObservableProperty, как вы сказали, и заменил его на обычный get;set, после чего код работал нормально. Кажется, проблема кроется здесь. @dbc
Вы столкнулись с ошибкой взаимодействия между двумя технологиями, использующими генераторы источников:
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
.
Я не знаю, что здесь происходит, и у меня нет инструментов для немедленного воспроизведения вашей проблемы, но у меня есть предположение: оба JsonSerializerContext и [ObservableProperty] работают через генераторы исходного кода . ...