JSON, çoğu C# programının web API'leriyle konuşma, ayarları saklama ve veri alışverişi yapma yoludur. Modern .NET onu .NET Core 3.0'dan itibaren çalışma zamanının bir parçası olan System.Text.Json ile okur ve yazar: NuGet paketi gerekmez. Ana giriş noktası, nesneleri JSON string'lerine ve geri çeviren statik JsonSerializer sınıfıdır.
System.Text.Json, .NET Core 3.0'a ve sonraki tüm sürümlere (.NET 5'ten .NET 10'a kadar) yerleşiktir. .NET Framework 4.6.2 ve sonrası ya da .NET Standard 2.0 üzerindeki projeler de System.Text.Json NuGet paketini kurarak onu kullanabilir. Bu sayfadaki örnekler, çıktısı yorumlarda olan düz kod olarak gösterilir. Onları bir .NET projesinde çalıştırmak için şu using yönergelerini ekleyin:
using System.Text.Json;
using System.Text.Json.Serialization;
Serialize: nesneden JSON'a
JsonSerializer.Serialize bir nesnenin her public property'sini property adlarını olduğu gibi kullanarak yazar:
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public List<string> Tags { get; set; } = new List<string>();
public bool InStock { get; set; }
}
var lamp = new Product { Name = "Desk lamp", Price = 34.90m, Tags = { "home", "light" }, InStock = true };
string json = JsonSerializer.Serialize(lamp);
Console.WriteLine(json);
// {"Name":"Desk lamp","Price":34.90,"Tags":["home","light"],"InStock":true}
Koleksiyonlar diziye, string anahtarlı dictionary'ler nesneye ({"apples":3,"pears":5}) dönüşür, null null kalır ve DateTime bir ISO 8601 string'i ("2026-03-01T14:30:00") olur. İki varsayılan insanları yakalar:
- Alanlar atlanır. Yalnızca property'ler serileştirilir.
public int X;içeren bir sınıf, seçeneklerdeIncludeFields = trueayarlamadıkça ya da alanı bir property'ye çevirmedikçe{}olarak serileştirilir. - Enum'lar sayıdır.
OrderStatus.Shipped1olarak yazılır (enum'un alttaki değeri)."Shipped"yazmak içinJsonStringEnumConverterekleyin (aşağıda).
Deserialize: JSON'dan nesneye
JsonSerializer.Deserialize<T> bir T oluşturur ve property'lerini JSON'dan ayarlar:
string json = "{\"Name\":\"Desk lamp\",\"Price\":34.90,\"Tags\":[\"home\",\"light\"]}";
Product p = JsonSerializer.Deserialize<Product>(json);
Console.WriteLine($"{p.Name} {p.Price} {p.Tags.Count}"); // Desk lamp 34.90 2
var many = JsonSerializer.Deserialize<List<Product>>("[{\"Name\":\"A\",\"Price\":1},{\"Name\":\"B\",\"Price\":2.5}]");
Console.WriteLine(many.Count); // 2
Eşleşen C# property'si olmayan JSON property'leri yok sayılır ve eşleşen JSON'u olmayan C# property'leri varsayılan değerlerini korur. Varsayılan olarak ikisi de hata değildir.
Neredeyse herkesi yanıltan kural: ad eşleştirme harf duyarlıdır. Çoğu web API'si camelCase gönderir ve camelCase PascalCase property'lerle eşleşmez:
string fromApi = "{\"name\":\"Mug\",\"price\":8.5}";
var a = JsonSerializer.Deserialize<Product>(fromApi);
Console.WriteLine($"[{a.Name}] {a.Price}"); // [] 0: nothing matched, no error
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var b = JsonSerializer.Deserialize<Product>(fromApi, options);
Console.WriteLine($"[{b.Name}] {b.Price}"); // [Mug] 8.5
new JsonSerializerOptions(JsonSerializerDefaults.Web) ASP.NET Core'un kullandığı ayarları verir: harf duyarsız okuma, camelCase yazma ve tırnaklı string olarak kabul edilen sayılar.
Deserialize işlemi her değeri ayarlamanın bir yolunu gerektirir: public bir setter, bir init erişimcisi ya da parametre adları property'lerle eşleşen bir constructor. Son kural, record'ların kutudan çıktığı gibi çalışmasının nedenidir:
public record Point(int X, int Y);
Point pt = JsonSerializer.Deserialize<Point>("{\"X\":1,\"Y\":2}");
Console.WriteLine(pt); // Point { X = 1, Y = 2 }
Seçenekler: camelCase ve girintili çıktı
JsonSerializerOptions adlandırmayı, biçimlendirmeyi ve daha fazlasını kontrol eder. Bir örnek oluşturun ve onu yeniden kullanın: serializer meta veriyi seçenek örneği başına önbelleğe alır, bu yüzden her çağrı için yeni seçenekler oluşturmak ölçülebilir biçimde daha yavaştır.
private static readonly JsonSerializerOptions Options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
};
Console.WriteLine(JsonSerializer.Serialize(lamp, Options));
// {
// "name": "Desk lamp",
// "price": 34.90,
// "tags": [
// "home",
// "light"
// ],
// "inStock": true
// }
Bilmeye değer diğer seçenekler: null property'leri dışarıda bırakmak için DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, IncludeFields = true, string olarak yazılmış sayıları kabul etmek için NumberHandling ve elle düzenlenen yapılandırma dosyaları için ReadCommentHandling = JsonCommentHandling.Skip artı AllowTrailingCommas = true. .NET 8, snake_case kullanan API'ler için JsonNamingPolicy.SnakeCaseLower'ı ekledi.
Öznitelikler: yeniden adlandırmak, yok saymak, string olarak enum'lar
Sınıftaki öznitelikler her seferinde bir property'yi kontrol eder ve seçeneklere üstün gelir:
public enum OrderStatus { Pending, Shipped }
public class Order
{
[JsonPropertyName("order_id")]
public int Id { get; set; }
public string Customer { get; set; }
[JsonIgnore]
public string InternalNote { get; set; } // never written or read
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Coupon { get; set; } // left out when null
[JsonConverter(typeof(JsonStringEnumConverter))]
public OrderStatus Status { get; set; }
public DateTime PlacedAt { get; set; }
}
var order = new Order
{
Id = 1042, Customer = "Ana", InternalNote = "vip",
Status = OrderStatus.Shipped, PlacedAt = new DateTime(2026, 3, 1, 14, 30, 0),
};
Console.WriteLine(JsonSerializer.Serialize(order));
// {"order_id":1042,"Customer":"Ana","Status":"Shipped","PlacedAt":"2026-03-01T14:30:00"}
Her property'yi işaretlemek yerine her enum'u string olarak yazmak için converter'ı seçeneklere ekleyin: options.Converters.Add(new JsonStringEnumConverter());.
Sınıf olmadan JSON okumak: JsonDocument ve JsonNode
Büyük bir yanıttan yalnızca birkaç değere ihtiyacınız olduğunda ya da şekli değiştiğinde sınıfı atlayın. JsonDocument, JsonElement değerlerinden oluşan salt okunur bir ağaca parse eder:
string weather = "{\"city\":\"Lisbon\",\"current\":{\"temp\":21.5,\"conditions\":[\"sunny\",\"windy\"]},\"alerts\":null}";
using (JsonDocument doc = JsonDocument.Parse(weather))
{
JsonElement root = doc.RootElement;
Console.WriteLine(root.GetProperty("city").GetString()); // Lisbon
Console.WriteLine(root.GetProperty("current").GetProperty("temp").GetDecimal()); // 21.5
foreach (JsonElement c in root.GetProperty("current").GetProperty("conditions").EnumerateArray())
Console.WriteLine(c.GetString()); // sunny, windy
Console.WriteLine(root.TryGetProperty("humidity", out _)); // False
Console.WriteLine(root.GetProperty("alerts").ValueKind); // Null
}
GetProperty olmayan bir ad için KeyNotFoundException fırlatır, bu yüzden isteğe bağlı alanlar için TryGetProperty kullanın. JsonDocument havuzdan bellek kiralar; using ile dispose edilmesinin nedeni budur.
JSON'u değiştirmek için indeksleyicileri olan değiştirilebilir bir ağaç veren System.Text.Json.Nodes içindeki JsonNode'u (.NET 6 ve sonrası) kullanın:
JsonNode node = JsonNode.Parse(weather);
Console.WriteLine((string)node["city"]); // Lisbon
node["current"]["temp"] = 23;
node["updated"] = true;
Console.WriteLine(node.ToJsonString());
// {"city":"Lisbon","current":{"temp":23,"conditions":["sunny","windy"]},"alerts":null,"updated":true}
Dosyalar ve stream'ler
Diskteki JSON bir dosyadaki string'tir, bu yüzden dosya metotları serializer ile doğrudan birleşir:
File.WriteAllText("settings.json", JsonSerializer.Serialize(settings, Options));
var loaded = JsonSerializer.Deserialize<Settings>(File.ReadAllText("settings.json"), Options);
// Same options both ways: camelCase names written with Options would not match on a default read.
Büyük dosyalar ve HTTP gövdeleri için async stream overload'ları tüm string'i bellekte oluşturmaktan kaçınır:
await using FileStream stream = File.OpenRead("orders.json");
List<Order> orders = await JsonSerializer.DeserializeAsync<List<Order>>(stream);
ASP.NET Core'da ve HttpClient ile serializer'ı nadiren kendiniz çağırırsınız: controller'lar JSON gövdelerini otomatik bağlar ve httpClient.GetFromJsonAsync<Order>(url) (System.Net.Http.Json içinde) isteği ve deserialize işlemini tek bir çağrıda yapar.
Hatalar
Geçersiz JSON ya da property'nin tipine dönüştürülemeyen bir değer JsonException fırlatır. Mesajı JSON yolunu ve konumu adlandırır; bu genellikle sorunu bulmak için yeterlidir:
try
{
JsonSerializer.Deserialize<Product>("{\"Name\": \"Lamp\", \"Price\": \"cheap\"}");
}
catch (JsonException e)
{
Console.WriteLine(e.Message);
// The JSON value could not be converted to System.Decimal. Path: $.Price | LineNumber: 0 | BytePositionInLine: 33.
}
JSON metni literal null olduğunda Deserialize (istisna değil) null döndürür, bu yüzden girdi dışarıdan geldiğinde sonucu kontrol edin.
Çıktıdaki kaçırılmış karakterler
Varsayılan olarak serializer ASCII olmayan karakterleri ve HTML içinde güvenli olmayan karakterleri kaçırır:
Console.WriteLine(JsonSerializer.Serialize(new { city = "São Paulo", note = "5 > 3" }));
// {"city":"S\u00E3o Paulo","note":"5 \u003E 3"}
Bu geçerli JSON'dur ve orijinal metin olarak geri okunur; çıktının bir HTML sayfasına güvenle bırakılabilmesi için kaçırılmıştır. İnsan tarafından okunabilir dosyalar için seçeneklerde Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping (System.Text.Encodings.Web'den) ayarlayın. "Unsafe" yalnızca sonucu HTML'e gömmeyi ifade eder.
System.Text.Json ile Newtonsoft.Json
Newtonsoft.Json (Json.NET, JsonConvert sınıfı) on yıl boyunca standarttı ve hâlâ her yerdedir. Başlıca farklar:
| System.Text.Json | Newtonsoft.Json | |
|---|---|---|
| Kullanılabilirlik | .NET Core 3.0+'a yerleşik; .NET Framework 4.6.2+ için NuGet paketi | .NET Framework ve .NET için NuGet paketi |
| Serialize / deserialize | JsonSerializer.Serialize(obj) / Deserialize<T>(json) | JsonConvert.SerializeObject(obj) / DeserializeObject<T>(json) |
| Ad eşleştirme | varsayılan olarak harf duyarlı | harf duyarsız |
| Tipsiz okuma | JsonDocument, JsonNode | JSONPath sorgularıyla JObject, JToken |
| Bir property'yi yeniden adlandırmak | [JsonPropertyName("x")] | [JsonProperty("x")] |
| Hoşgörü | katı: açılmadıkça yorum, sondaki virgül ya da tırnaklı sayı yok | varsayılan olarak hoşgörülü |
| Performans | daha hızlı, daha az ayırma, trimming ve AOT için source generation | daha yavaş, reflection tabanlı |
Özniteliklerin adları ve namespace'leri farklıdır, bu yüzden bir kod tabanını geçirmek bir bul-değiştir işi artı daha katı parse için testlerdir. Yeni .NET kodu için System.Text.Json ile başlayın.
Source generation (.NET 6+)
JsonSerializer normalde tiplerinizi çalışma zamanında reflection ile inceler. Trim edilmiş ya da önceden derlenmiş uygulamalar için (Native AOT, Blazor WebAssembly) bir source generator bu kodu bunun yerine derleme zamanında yazar:
[JsonSerializable(typeof(Product))]
internal partial class AppJsonContext : JsonSerializerContext { }
string json = JsonSerializer.Serialize(lamp, AppJsonContext.Default.Product);
Yaygın hatalar
- Varsayılan seçeneklerle camelCase JSON'u PascalCase property'lere okumak. Her property sessizce boş kalır.
PropertyNameCaseInsensitiveya daJsonSerializerDefaults.Webkullanın. - Property yerine public alanlar.
IncludeFieldsayarlanmadıkça serileştirilmezler. - Setter'ı olmayan property'ler. Eşleşen bir constructor parametresi olmayan yalnızca get property deserialize işleminde doldurulmaz.
- Çağrı başına yeni bir
JsonSerializerOptions. Tek bir statik örneği yeniden kullanın. - Kayan noktalı para.
0.1 + 0.2değerindeki birdouble0.30000000000000004olarak serileştirilir. Tutarlar içindecimalkullanın.
Sıkça Sorulan Sorular
C#'ta bir nesne JSON'a nasıl çevrilir?
System.Text.Json'dan JsonSerializer.Serialize(obj) çağırın; bu .NET Core 3.0 ve sonrasına yerleşiktir ve kurulacak bir paket yoktur. Her public property'yi yazar: {"Name":"Desk lamp","Price":34.90}. Okunabilir çıktı için new JsonSerializerOptions { WriteIndented = true }, camelCase adlar için PropertyNamingPolicy = JsonNamingPolicy.CamelCase verin.
C#'ta JSON bir nesneye nasıl çevrilir?
var product = JsonSerializer.Deserialize<Product>(json); bir Product oluşturur ve public, ayarlanabilir property'lerini eşleşen JSON adlarından doldurur. Eşleştirme varsayılan olarak harf duyarlıdır, bu yüzden PropertyNameCaseInsensitive = true ya da new JsonSerializerOptions(JsonSerializerDefaults.Web) vermedikçe camelCase JSON PascalCase property'leri boş bırakır. Hatalı biçimli JSON ya da yanlış bir değer tipi JsonException fırlatır.
C#'ta sınıf oluşturmadan JSON nasıl okunur?
JsonDocument.Parse(json) kullanın ve RootElement'i GetProperty("name"), GetString(), GetInt32() ve EnumerateArray() ile dolaşın; salt okunurdur, hızlıdır ve dispose edilmelidir. Değiştirmek istediğiniz JSON için JsonNode.Parse(json) (.NET 6+) değiştirilebilir bir ağaç verir: node["city"], atamalar ve ToJsonString().
System.Text.Json mı kullanmalıyım, Newtonsoft.Json mı?
.NET Core 3.0 ya da sonrasındaki yeni kod için System.Text.Json: yerleşiktir, daha hızlıdır, daha az bellek ayırır ve ASP.NET Core varsayılan olarak onu kullanır. Newtonsoft.Json (Json.NET) .NET Framework projelerinde (System.Text.Json orada yalnızca NuGet paketi olarak bulunur), ek özelliklerine (JSONPath sorguları, çok hoşgörülü parse, TypeNameHandling) bağlı kodda ve zaten onun üzerine kurulu büyük kod tabanlarında hâlâ yaygındır.
System.Text.Json neden é ve < gibi karakterleri kaçırıyor?
Varsayılan encoder, çıktının HTML'e gömülmesi güvenli olsun diye ASCII olmayan ve HTML'e duyarlı karakterleri (<, >, &, ') \uXXXX olarak kaçırır. Yine de geçerli JSON'dur ve aynı metne geri deserialize edilir. Okunabilir çıktı için seçeneklerde Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping ayarlayın, ama yalnızca JSON HTML'e yazılmıyorsa.