C#: конструктор копирования или конструктор-десериализатор из JSON

По условию задачи мне надо десериализовать объект из JSON:

  1. Есть класс, в нём много свойств. Можно на первом этапе считать, что все свойства - «невложенные», то есть что-то вроде такого:
public class ContragentPayment 
{
    public int Id { get; set; }
    public int Status { get; set; }
    public int ArticleId { get; set; }
    public DateTime PaymentDate { get; set; }
    public int? InvoiceId { get; set; }
    public decimal Price { get; set; }
    public int CurrencyId { get; set; }
    public decimal CurrencyRate { get; set; }
    public int? OrderId { get; set; }
    public string PaySubject { get; set; }
}
  1. С сервера приходит сериализованный в JSON объект идентичной структуры (то есть, с теми же свойствами). При этом на клиенте я могу переписать класс как угодно, главное, чтобы тот же набор свойств можно было отобразить на клиенте.

  2. Мне было бы очень удобно этот JSON передавать в конструктор, чтобы писать что-то вроде ContragentPayment cp = new ContragentPayment(cp_as_json_string);

Подключил пакет System.Text.Json, десериализую JSON так:

var tmp = JsonSerializer.Deserialize<ContragentPayment>(cp_as_json_string);

Но я не могу понять, как поместить эту строку кода в конструктор, ведь thisдоступен только для чтения, то есть я не могу написать:

var tmp = new ContragentPayment(cp_as_json_string);
this = tmp;`

Вот если бы у меня в классе ContragentPayment был конструктор копирования, чтобы я мог инициализировать экземпляр объекта другим экземпляром объекта.

Я смотрю пример, и вижу, что в примере конструктора копирования используется перебор всех свойств вручную, мне же не хотелось бы этого делать, так как их достаточно много. То есть мне нужно либо что-то вроде AutoMapper, либо придётся «наступить на свою гордость» и написать блок кода, который сделает это через Reflection: переберёт свойства, но сделает это автоматически.

Однако, AutoMapper -- это лишняя внешняя зависимость, да и вообще -- стрельба из пушки по воробьям, а перебор через Reflection мне кажется вычурным и не соответствующим простоте задачи.

Возможно, я упускаю какую то очень простую вещь, на уровне базового синтаксиса языка, которая позволяет выкрутиться? Не посоветуете ли, в какую сторону копать?

Спасибо большое!


Ответы (1 шт):

Автор решения: VladD

Для того, чтобы заполнить объект данными из JSON, можно использовать JsonConvert.PopulateObject, если вы перейдёте на более старый Newtonsoft JSON.NET:

public ContragentPayment(string jsonSource)
{
    JsonConvert.PopulateObject(jsonSource, this);
}

Подобной функциональности пока нету в System.Text.Json, хотя существуют различные обходные пути.

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

Я бы посоветовал просто использовать JsonSerializer.Deserialize<ContragentPayment>(json) вместо конструктора.


Для реализации копирования проще всего использовать record. В record и так объявлен клонирующий конструктор, но он объявлен как protected, и используется в конструкции with. Можно выставить это наружу:

public ContragentPayment CreateCopy() => this with { };

Внимание: при этом вы получите shallow copy, то есть, другой объект, у которого будут скопированы (но не склонированы!) все свойства. Впрочем, если ваши DTO-классы будут неизменяемыми (immutable), то разницы особой и нет.

→ Ссылка