1. Giới thiệu
Nếu bạn mới bắt đầu tìm hiểu về tuần tự hóa (serialization) trong .NET, thì hoàn toàn tự nhiên khi thắc mắc: cần thêm một thư viện nữa làm gì khi đã có System.Text.Json tích hợp? Câu trả lời đơn giản: Newtonsoft.Json ra đời sớm hơn và qua nhiều năm vẫn phát triển thành công cụ rất linh hoạt cho JSON trên .NET.
Nó đã trở thành de-facto standard vì hỗ trợ nhiều scenario phức tạp: custom contracts, powerful converters, serialize private fields, LINQ tới JSON, dynamic objects (JObject), xử lý tham chiếu vòng linh hoạt và nhiều định dạng ngày/giờ. Rất nhiều thư viện và API vẫn dùng Json.NET “bên dưới nắp”.
Lưu ý: trong một số trường hợp System.Text.Json tích hợp vẫn thua về khả năng so với Newtonsoft.Json, nên việc học Json.NET vẫn có ích.
Cài đặt Newtonsoft.Json (Json.NET)
Cài gói qua NuGet:
dotnet add package Newtonsoft.Json
Import namespace:
using Newtonsoft.Json;
2. Tuần tự hóa với Newtonsoft.Json
Lấy ví dụ class truyền thống Person:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Serialize đối tượng thành chuỗi JSON
Person person = new Person { Name = "Ivan", Age = 30 };
// Chuyển sang JSON
string json = JsonConvert.SerializeObject(person);
Console.WriteLine(json);
// Kết quả: {"Name":"Ivan","Age":30}
Deserialize JSON về lại object
string json = "{\"Name\":\"Ivan\",\"Age\":30}";
Person person = JsonConvert.DeserializeObject<Person>(json);
Console.WriteLine($"{person.Name}, {person.Age}");
// Kết quả: Ivan, 30
Chuyện gì xảy ra "bên trong"?
Newtonsoft.Json duyệt qua tất cả các property public, tuần tự hóa chúng ra JSON và ghi vào chuỗi. Khi deserialize nó sẽ map các khóa từ JSON với tên property và gán giá trị vào object.
3. Tuần tự hóa collection các object
Serialize collections và mảng
Collections hoạt động “mặc định” luôn.
List<Person> people = new List<Person>
{
new Person { Name = "Ivan", Age = 30 },
new Person { Name = "Maria", Age = 25 }
};
string json = JsonConvert.SerializeObject(people);
// Kết quả: [{"Name":"Ivan","Age":30},{"Name":"Maria","Age":25}]
List<Person> deserialized = JsonConvert.DeserializeObject<List<Person>>(json);
// Giờ bạn lại có danh sách Person!
Đặc điểm khi serialize/deserialize Dictionary
var dict = new Dictionary<string, int>
{
["apple"] = 2,
["banana"] = 5
};
string json = JsonConvert.SerializeObject(dict);
// Kết quả: {"apple":2,"banana":5}
var deserializedDict = JsonConvert.DeserializeObject<Dictionary<string,int>>(json);
// Mọi thứ ổn!
Nếu khóa không phải string (ví dụ Dictionary<int,string>), Json.NET sẽ convert các khóa thành chuỗi khi serialize, và khi deserialize sẽ cố convert ngược lại. Với key phức tạp (ví dụ Guid) thì an toàn hơn khi dùng Dictionary<string, TValue>.
4. Làm việc với object lồng nhau và cấu trúc phân cấp
public class Order
{
public int Id { get; set; }
public Person Customer { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public string Title { get; set; }
public double Price { get; set; }
}
Order order = new Order
{
Id = 123,
Customer = new Person { Name = "Ivan", Age = 30 },
Products = new List<Product>
{
new Product { Title = "Laptop", Price = 50000.0 },
new Product { Title = "Chuột", Price = 1500.0 }
}
};
string json = JsonConvert.SerializeObject(order);
Console.WriteLine(json);
Kết quả: các object lồng nhau sẽ được biểu diễn đúng trong cấu trúc JSON.
5. Cấu hình serialization bằng attribute
Bỏ qua thuộc tính
public class Person
{
public string Name { get; set; }
[JsonIgnore]
public int Age { get; set; }
}
Bây giờ Age sẽ không xuất hiện trong JSON.
Đổi tên thuộc tính
public class Person
{
[JsonProperty("full_name")]
public string Name { get; set; }
}
Trong JSON tên sẽ là "full_name".
6. Cấu hình linh hoạt: JsonSerializerSettings
Formatting (JSON đẹp nhiều dòng):
string json = JsonConvert.SerializeObject(
people,
Formatting.Indented
);
Kết quả:
[
{
"Name": "Ivan",
"Age": 30
},
{
"Name": "Maria",
"Age": 25
}
]
Các thiết lập hay dùng:
| Thuộc tính | Mô tả |
|---|---|
|
Cách xử lý các thuộc tính null (bỏ qua hay ghi rõ null) |
|
Có bỏ qua giá trị mặc định hay không |
|
Làm gì với circular references |
|
Chuỗi định dạng cho ngày/giờ |
Ví dụ bỏ qua null:
string json = JsonConvert.SerializeObject(
person,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }
);
7. Làm việc với JSON động: JObject, JArray và các loại khác
Khi cấu trúc JSON không biết trước, dùng kiểu từ Newtonsoft.Json.Linq:
using Newtonsoft.Json.Linq;
string json = @"{
'Name': 'Ivan',
'Age': 30,
'Skills': ['C#', 'SQL', 'JSON']
}";
JObject obj = JObject.Parse(json);
Console.WriteLine(obj["Name"]); // Ivan
Console.WriteLine(obj["Skills"][0]); // C#
Tạo JSON “ngay lúc chạy”:
var jObj = new JObject
{
["Status"] = "Success",
["Result"] = new JArray("item1", "item2", "item3")
};
Console.WriteLine(jObj.ToString(Formatting.Indented));
JObject và JArray là biểu diễn đối tượng JSON và mảng, về bản chất — là collection tiện dụng.
8. Những lưu ý hữu ích
Tham chiếu vòng
Newtonsoft.Json xử lý tham chiếu vòng khá linh hoạt:
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
string json = JsonConvert.SerializeObject(obj, settings);
Vậy các vòng sẽ bị bỏ qua. Nếu muốn giữ nguyên tham chiếu, có thể dùng ReferenceLoopHandling.Serialize kết hợp với [JsonObject(IsReference = true)].
Serialize anonymous và dynamic objects
var anon = new { Foo = 42, Bar = "Hello" };
string json = JsonConvert.SerializeObject(anon);
// {"Foo":42,"Bar":"Hello"}
Validate và xử lý lỗi
try
{
Person p = JsonConvert.DeserializeObject<Person>(brokenJson);
}
catch (JsonSerializationException ex)
{
Console.WriteLine("Lỗi khi deserialization: " + ex.Message);
}
So sánh Newtonsoft.Json vs System.Text.Json
| Newtonsoft.Json (Json.NET) | System.Text.Json (.NET) | |
|---|---|---|
| Hỗ trợ .NET | .NET Framework/Standard/6+ | .NET Core 3.0+ / .NET 5/6/9 |
| LINQ tới JSON (JObject/JArray) | Có | Không |
| Tùy chỉnh | Rất mạnh | Hạn chế |
| Attributes ([JsonProperty], ...) | Có | Có (một phần, ít tính năng hơn) |
| Hỗ trợ private properties | Có | Không |
| Tốc độ | Chậm hơn trong một số scenario | Nhanh hơn |
| Bộ converter phức tạp | Có | Có (ít linh hoạt hơn) |
| Hỗ trợ DataTable, DataSet | Có | Không |
| Tài liệu và ví dụ | Rất nhiều | Đang tăng |
9. Lỗi của người mới và bẫy thường gặp
Lỗi #1: thuộc tính trở thành null sau khi deserialize.
Thường là vì property không có setter hoặc không có constructor không tham số — serializer không có cách nào để tạo và fill object.
Lỗi #2: tên thuộc tính không khớp giữa JSON và class.
Nếu trong JSON trường là "fullName", còn trong class là FullName, thì dùng [JsonProperty] hoặc cấu hình ContractResolver để map tên.
Lỗi #3: serialize mặc định chỉ làm việc với public properties.
Private fields/properties không được serialize trừ khi cấu hình thêm. Cần custom converters hoặc resolver/contract đặc biệt.
Lỗi #4: tham chiếu vòng gây ra StackOverflowException.
Mutual references giữa các object có thể gây vòng vô hạn khi serialize nếu không cấu hình. Hãy cấu hình xử lý vòng (ví dụ ReferenceLoopHandling) hoặc thay đổi model dữ liệu.
GO TO FULL VERSION