معروفترین کتابخانهی کار با JSON در دات نت، Json.NET است که این روزها، جزء جدایی ناپذیر حداقل، تمام برنامههای وب مبتنی بر دات نت میباشد. برای مثال ASP.NET Core 2x/1x و همچنین ASP.NET Web API پیش از NET Core.، به صورت پیشفرض از این کتابخانه برای کار با JSON استفاده میکنند. این کتابخانه 10 سال پیش ایجاد شد و در طول زمان، قابلیتهای زیادی به آن اضافه شدهاست. همین حجم بالای کار صورت گرفته سبب شدهاست که برای مثال شروع به استفادهی از <Span<T در آن برای بالابردن کارآیی، بسیار مشکل شده و نیاز به تغییرات اساسی در آن داشته باشد. به همین جهت خود تیم CoreFX دات نت Core گزینهی دیگری را برای کار با JSON در فضای نام جدید System.Text.Json ارائه دادهاست که برای کار با آن نیاز به نصب وابستگی ثالثی نیست و همچنین کارآیی آن به علت استفادهی از ویژگیهای جدید زبان، مانند ref struct و Span، به طور میانگین دو برابر کتابخانهی Json.NET است. برای مثال استفادهی از string (حالت پیشفرض کتابخانهی Json.NET) یعنی کار با رشتههایی از نوع UTF-16؛ اما کار با Span، امکان دسترسی مستقیم به رشتههایی از نوع UTF-8 را میسر میکند که نیازی به تبدیل به رشتههایی از نوع UTF-16 را ندارند.
ASP.NET Core 3x دیگر به صورت پیشفرض به همراه Json.NET ارائه نمیشود
در برنامههای ASP.NET Core 3x، وابستگی ثالث Json.NET حذف شدهاست و از این پس هر نوع خروجی JSON آن، مانند بازگشت مقادیر مختلف از اکشن متدهای کنترلرها، به صورت خودکار در پشت صحنه از امکانات ارائه شدهی در System.Text.Json استفاده میکند و دیگر Json.NET، کتابخانهی پیشفرض کار با JSON آن نیست. بنابراین برای کار با آن نیاز به تنظیم خاصی نیست. همینقدر که یک پروژهی جدید ASP.NET Core 3x را ایجاد کنید، یعنی در حال استفادهی از System.Text.Json هستید.
روش بازگشت به Json.NET در ASP.NET Core 3x
اگر به هر دلیلی هنوز نیاز به استفادهی از کتابخانهی Json.NET را دارید، آداپتور ویژهی آن نیز تدارک دیده شدهاست. برای اینکار:
الف) ابتدا باید بستهی نیوگت Microsoft.AspNetCore.Mvc.NewtonsoftJson را نصب کنید.
ب) سپس در کلاس Startup، باید این کتابخانه را به صورت یک سرویس جدید، با فراخوانی متد AddNewtonsoftJson، معرفی کرد:
یکی از دلایل بازگشت به Json.NET میتواند عدم پشتیبانی از OpenAPI / Swagger در حین کار با System.Text.Json باشد و این مورد قرار نیست در نگارش نهایی 3.0، حضور داشته باشد و انطباق با آن به نگارشهای بعدی موکول شدهاست.
روش کار مستقیم با System.Text.Json
اگر در قسمتی از برنامهی خود نیاز به کار مستقیم با اشیاء JSON را داشته باشید و یا حتی بخواهید از این قابلیت در برنامههای کنسول و یا کتابخانهها نیز استفاده کنید، روش انتقال کدهایی که از Json.NET استفاده میکنند به System.Text.Json، به صورت زیر است:
تبدیل رشتهی JSON حاوی اطلاعات شخص، به شیء متناظر با آن و یا حالت عکس آن:
در اینجا از کلاس System.Text.Json.Serialization.JsonSerializer، روش کار با دو متد Parse را برای Deserialization و ToString را برای Serialization مشاهده میکنید.
کلاس JsonSerializer دارای overloadهای زیر برای کار با متدهای Parse و ToString است:
یک نکته: کارآیی متد Parse با امضای ReadOnlySpan<byte> utf8Json، بیشتر است از نمونهای که string json را میپذیرد. از این جهت که چون با آرایهای از بایتهای رشتهای از نوع UTF-8 کار میکند، نیاز به تبدیل به UTF-16 را مانند متدی که string را میپذیرد، ندارد. برای تولید آرایهی بایتهای utf8Json از روی یک شیء، میتوانید از متد JsonSerializer.ToUtf8Bytes استفاده کنید و یا برای تولید آن از روی یک رشته، از متد Encoding.UTF8.GetBytes استفاده کنید.
سفارشی سازی JsonSerializer جدید
اگر به امضای متدهای Parse و ToString کلاس JsonSerializer دقت کنید، دارای یک پارامتر اختیاری از نوع JsonSerializerOptions نیز هستند که به صورت زیر تعریف شدهاست:
برای نمونه معادل تنظیم NullValueHandling در Json.NET:
اینبار توسط خاصیت IgnoreNullValues صورت میگیرد:
در برنامههای ASP.NET Core که این نوع متدها در پشت صحنه فراخوانی میشوند، روش تنظیم JsonSerializerOptions به صورت زیر است:
نگاشت نام ویژهی خواص در حین عملیات deserialization
در مثال فوق، فرض شدهاست که نام خاصیت BirthDay، دقیقا با اطلاعاتی که از رشتهی JSON دریافتی پردازش میشود، تطابق دارد. اگر این نام در اطلاعات دریافتی متفاوت است، میتوان از ویژگی JsonPropertyName برای تعریف این نگاشت استفاده کرد:
روش دیگر اینکار، برای نمونه تنظیم PropertyNamingPolicy به حالت CamelCase است:
و یا اگر میخواهید حساسیت به بزرگی و کوچکی حروف را ندید بگیرید (مانند حالت پیشفرض JSON.NET) از تنظیم JsonSerializerOptions.PropertyNameCaseInsensitive استفاده کنید.
در این بین اگر نمیخواهید خاصیتی در عملیات serialization و یا برعکس آن پردازش شود، میتوان از تعریف ویژگی [JsonIgnore] بر روی آن استفاده کرد.
ASP.NET Core 3x دیگر به صورت پیشفرض به همراه Json.NET ارائه نمیشود
در برنامههای ASP.NET Core 3x، وابستگی ثالث Json.NET حذف شدهاست و از این پس هر نوع خروجی JSON آن، مانند بازگشت مقادیر مختلف از اکشن متدهای کنترلرها، به صورت خودکار در پشت صحنه از امکانات ارائه شدهی در System.Text.Json استفاده میکند و دیگر Json.NET، کتابخانهی پیشفرض کار با JSON آن نیست. بنابراین برای کار با آن نیاز به تنظیم خاصی نیست. همینقدر که یک پروژهی جدید ASP.NET Core 3x را ایجاد کنید، یعنی در حال استفادهی از System.Text.Json هستید.
روش بازگشت به Json.NET در ASP.NET Core 3x
اگر به هر دلیلی هنوز نیاز به استفادهی از کتابخانهی Json.NET را دارید، آداپتور ویژهی آن نیز تدارک دیده شدهاست. برای اینکار:
الف) ابتدا باید بستهی نیوگت Microsoft.AspNetCore.Mvc.NewtonsoftJson را نصب کنید.
ب) سپس در کلاس Startup، باید این کتابخانه را به صورت یک سرویس جدید، با فراخوانی متد AddNewtonsoftJson، معرفی کرد:
public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddNewtonsoftJson() // ... }
روش کار مستقیم با System.Text.Json
اگر در قسمتی از برنامهی خود نیاز به کار مستقیم با اشیاء JSON را داشته باشید و یا حتی بخواهید از این قابلیت در برنامههای کنسول و یا کتابخانهها نیز استفاده کنید، روش انتقال کدهایی که از Json.NET استفاده میکنند به System.Text.Json، به صورت زیر است:
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public DateTime? BirthDay { get; set; } }
using System; using System.Text.Json.Serialization; namespace ConsoleApp { class Program { static void Main(string[] args) { Person person = JsonSerializer.Parse<Person>(...); string json = JsonSerializer.ToString(person); } } }
کلاس JsonSerializer دارای overloadهای زیر برای کار با متدهای Parse و ToString است:
namespace System.Text.Json.Serialization { public static class JsonSerializer { public static object Parse(ReadOnlySpan<byte> utf8Json, Type returnType, JsonSerializerOptions options = null); public static object Parse(string json, Type returnType, JsonSerializerOptions options = null); public static TValue Parse<TValue>(ReadOnlySpan<byte> utf8Json, JsonSerializerOptions options = null); public static TValue Parse<TValue>(string json, JsonSerializerOptions options = null); public static string ToString(object value, Type type, JsonSerializerOptions options = null); public static string ToString<TValue>(TValue value, JsonSerializerOptions options = null); } }
سفارشی سازی JsonSerializer جدید
اگر به امضای متدهای Parse و ToString کلاس JsonSerializer دقت کنید، دارای یک پارامتر اختیاری از نوع JsonSerializerOptions نیز هستند که به صورت زیر تعریف شدهاست:
public sealed class JsonSerializerOptions { public bool AllowTrailingCommas { get; set; } public int DefaultBufferSize { get; set; } public JsonNamingPolicy DictionaryKeyPolicy { get; set; } public bool IgnoreNullValues { get; set; } public bool IgnoreReadOnlyProperties { get; set; } public int MaxDepth { get; set; } public bool PropertyNameCaseInsensitive { get; set; } public JsonNamingPolicy PropertyNamingPolicy { get; set; } public JsonCommentHandling ReadCommentHandling { get; set; } public bool WriteIndented { get; set; } }
// Json.NET: var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; string json = JsonConvert.SerializeObject(person, settings);
// JsonSerializer: var options = new JsonSerializerOptions { IgnoreNullValues = true }; string json = JsonSerializer.ToString(person, options);
در برنامههای ASP.NET Core که این نوع متدها در پشت صحنه فراخوانی میشوند، روش تنظیم JsonSerializerOptions به صورت زیر است:
services.AddControllers() .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
نگاشت نام ویژهی خواص در حین عملیات deserialization
در مثال فوق، فرض شدهاست که نام خاصیت BirthDay، دقیقا با اطلاعاتی که از رشتهی JSON دریافتی پردازش میشود، تطابق دارد. اگر این نام در اطلاعات دریافتی متفاوت است، میتوان از ویژگی JsonPropertyName برای تعریف این نگاشت استفاده کرد:
[JsonPropertyName("birthdate")] public DateTime? BirthDay { get; set; }
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; string json = JsonSerializer.ToString(person, options);
در این بین اگر نمیخواهید خاصیتی در عملیات serialization و یا برعکس آن پردازش شود، میتوان از تعریف ویژگی [JsonIgnore] بر روی آن استفاده کرد.