总目录
一、JsonNamingPolicy 是什么?
1. 定义
JsonNamingPolicy
是 System.Text.Json 中用于 控制属性名称在序列化/反序列化时的命名格式的策略类。它允许开发者将 .NET 对象的属性名称转换为指定的格式(如 camelCase
、snake_case
等),或自定义其他命名规则。
JsonNamingPolicy
是 System.Text.Json 命名空间中的一个类,用于定义如何在序列化和反序列化过程中处理属性名称与 JSON 字段名称之间的映射关系。
- 通过使用
JsonNamingPolicy
,开发者可以自定义 JSON 字段的命名约定,例如将 C# 中的驼峰命名约定转换为 JSON 中的蛇形命名约定,或者反之。JsonNamingPolicy
通过重写ConvertName
方法来实现名称转换逻辑。在序列化过程中,ConvertName
方法接收 C# 属性名称并返回 JSON 字段名称;在反序列化过程中,它接收 JSON 字段名称并返回 C# 属性名称。这种双向映射机制确保了在序列化和反序列化过程中名称转换的一致性。
2. 核心作用
- 序列化时:将 C# 属性名转换为指定格式(如
PascalCase
→camelCase
)。 - 反序列化时:将 JSON 中的键名转换为对象的属性名。
- 命名优先级:显式标记
[JsonPropertyName]
的属性优先级高于命名策略。
3. 适用场景
JsonNamingPolicy
是 System.Text.Json 中 控制属性名称格式的核心工具,适用于以下场景:
- 适配前后端命名规范(如 C# 的
PascalCase
转为前端的camelCase
)。 - 统一 API 接口的键名格式(如
snake_case
)。 - 自定义特殊命名规则(如添加前缀/后缀)。
通过内置策略或自定义实现,开发者可以灵活控制 JSON 的属性名称,确保与外部系统的兼容性及代码的可维护性。
二、内置命名策略
System.Text.Json 提供了多种内置命名策略,以满足不同的命名约定需求:
1. 默认策略
默认情况下,无策略(null) ,即保留原始属性名(如 UserName
→ UserName
)。
不过通常在 C# 中 属性会遵守PascalCase 命名策略
public class User
{
public string UserName { get; set; }
public int UserAge { get; set; }
}
class Program
{
static void Main()
{
var user = new User { UserName = "John", UserAge = 15 };
// 序列化
string json = JsonSerializer.Serialize(user);
Console.WriteLine(json);
// 输出:{"UserName":"John","UserAge":15}
// 反序列化
string jsonString = """{"UserName":"John","UserAge":15}""";
var u= JsonSerializer.Deserialize<User>(jsonString);
Console.WriteLine($"UserName = {u.UserName} ,UserAge = {u.UserAge}");
// 输出:UserName = John ,UserAge = 15
}
}
2. CamelCase
JsonNamingPolicy.CamelCase
策略将 PascalCase 的 C# 属性名称转换为 camelCase 的 JSON 字段名称。例如,PublicProperty
将被转换为 “publicProperty”。
public class User
{
public string UserName { get; set; }
public int UserAge { get; set; }
}
class Program
{
static void Main()
{
var user = new User { UserName = "John", UserAge = 15 };
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
// 序列化
string json = JsonSerializer.Serialize(user,options);
Console.WriteLine(json);
// 输出:{"userName":"John","userAge":15}
// 反序列化
string jsonString = """{"userName":"John","userAge":15}""";
var u= JsonSerializer.Deserialize<User>(jsonString);
Console.WriteLine($"UserName = {u.UserName} ,UserAge = {u.UserAge}");
// 输出:UserName = ,UserAge = 0
var u2 = JsonSerializer.Deserialize<User>(jsonString,options);
Console.WriteLine($"UserName = {u2.UserName} ,UserAge = {u2.UserAge}");
// 输出:UserName = John ,UserAge = 15
}
}
注意:序列化和反序列化的时候需要保持使用一致的命名策略,否则就会出现如上案例所示的问题
3. SnakeCaseLower/SnakeCaseUpper
JsonNamingPolicy.SnakeCaseLower
策略将 PascalCase 的 C# 属性名称转换为 snake_case 的 JSON 字段名称,并且字段名称为小写。例如,PublicProperty
将被转换为 “public_property”。JsonNamingPolicy.SnakeCaseUpper
策略将 PascalCase 的 C# 属性名称转换为 SNAKE_CASE 的 JSON 字段名称,并且字段名称为大写。例如,PublicProperty
将被转换为 “PUBLIC_PROPERTY”。
public class User
{
public string UserName { get; set; }
public int UserAge { get; set; }
}
class Program
{
static void Main()
{
var user = new User { UserName = "John", UserAge = 15 };
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseUpper
};
var options2 = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
// 序列化
string json = JsonSerializer.Serialize(user,options);
Console.WriteLine(json);
// 输出:{"USER_NAME":"John","USER_AGE":15}
json = JsonSerializer.Serialize(user, options2);
Console.WriteLine(json);
// 输出:{"user_name":"John","user_age":15}
// 反序列化
string jsonString = """{"USER_NAME":"John","USER_AGE":15}""";
var u= JsonSerializer.Deserialize<User>(jsonString,options);
Console.WriteLine($"UserName = {u.UserName} ,UserAge = {u.UserAge}");
// 输出:UserName = John ,UserAge = 15
}
}
4. KebabCaseUpper/KebabCaseLower
JsonNamingPolicy.KebabCaseLower
策略将 PascalCase 的 C# 属性名称转换为 kebab-case 的 JSON 字段名称,并且字段名称为小写。例如,UserName
将被转换为 “user-name”。JsonNamingPolicy.KebabCaseUpper
策略将 PascalCase 的 C# 属性名称转换为 KEBAB_CASE 的 JSON 字段名称,并且字段名称为大写。例如,UserName
将被转换为 “USER-NAME”。
public class User
{
public string UserName { get; set; }
public int UserAge { get; set; }
}
class Program
{
static void Main()
{
var user = new User { UserName = "John", UserAge = 15 };
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.KebabCaseUpper
};
var options2 = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower
};
// 序列化
string json = JsonSerializer.Serialize(user,options);
Console.WriteLine(json);
// 输出:{"USER-NAME":"John","USER-AGE":15}
json = JsonSerializer.Serialize(user, options2);
Console.WriteLine(json);
// 输出:{"user-name":"John","user-age":15}
// 反序列化
string jsonString = """{"USER-NAME":"John","USER-AGE":15}""";
var u= JsonSerializer.Deserialize<User>(jsonString,options);
Console.WriteLine($"UserName = {u.UserName} ,UserAge = {u.UserAge}");
// 输出:UserName = John ,UserAge = 15
}
}
5. 示例:内置策略与 WriteIndented 配合使用
JsonNamingPolicy
不仅可以处理简单的对象,还可以处理包含嵌套对象和集合的复杂对象图。命名策略会递归地应用于对象图中的所有对象。
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public List<PhoneNumber> PhoneNumbers { get; set; }
}
public class PhoneNumber
{
public string Type { get; set; }
public string Number { get; set; }
}
var person = new Person
{
FirstName = "John",
LastName = "Doe",
Address = new Address
{
Street = "123 Main St",
City = "New York",
PhoneNumbers = new List<PhoneNumber>
{
new PhoneNumber { Type = "home", Number = "555-1234" },
new PhoneNumber { Type = "work", Number = "555-5678" }
}
}
};
var options = new JsonSerializerOptions
{
NamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
string json = JsonSerializer.Serialize(person, options);
// 输出:
//{
// "firstName": "John",
// "lastName": "Doe",
// "address": {
// "street": "123 Main St",
// "city": "New York",
// "phoneNumbers": [
// {
// "type": "home",
// "number": "555-1234"
// },
// {
// "type": "work",
// "number": "555-5678"
// }
// ]
// }
//}
三、自定义命名策略
除了内置的命名策略外,System.Text.Json 还允许开发者创建自定义的命名策略,以满足特定的命名转换需求。要创建自定义命名策略,需要创建一个继承自 JsonNamingPolicy
的类,并重写 ConvertName
方法。
1. 创建并使用自定义命名策略
1)创建自定义命名策略
通过继承 JsonNamingPolicy
并重写 ConvertName
方法,可以实现自定义的命名规则。
public class AllUppercaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.ToUpper();
}
}
在上述示例中,AllUppercaseNamingPolicy
类将所有属性名称转换为全大写。
2)使用自定义命名策略
要使用这个自定义策略,可以将其配置到 JsonSerializerOptions
中:
public class User
{
public string UserName { get; set; }
public int UserAge { get; set; }
}
class Program
{
static void Main()
{
var user = new User { UserName = "John", UserAge = 15 };
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = new AllUppercaseNamingPolicy()
};
// 序列化
string json = JsonSerializer.Serialize(user,options);
Console.WriteLine(json);
// 输出:{"USERNAME":"John","USERAGE":15}
// 反序列化
string jsonString = """{"USERNAME":"John","USERAGE":15}""";
var u= JsonSerializer.Deserialize<User>(jsonString,options);
Console.WriteLine($"UserName = {u.UserName} ,UserAge = {u.UserAge}");
// 输出:UserName = John ,UserAge = 15
}
}
2. 自定义命名策略处理命名冲突
在某些情况下,JSON 字段名称可能与 C# 属性名称不匹配,或者存在命名冲突。通过创建自定义的命名策略,可以解决这些问题。
public class CustomNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
// 处理特定的命名转换
if (name == "CreatedDate")
return "created_at";
// 对于其他属性,使用默认的 PascalCase 到 snake_case 转换
return name.Replace(" ", "_").ToLower();
}
}
四、字典键的命名策略(DictionaryKeyPolicy
)
通过 JsonSerializerOptions.DictionaryKeyPolicy
,可以控制 字典键名的格式。
示例:字典键转为驼峰命名
class Program
{
static void Main()
{
var dictionary = new Dictionary<string, string>
{
["UserName"] = "John",
["UserPwd"] = "123"
};
var options = new JsonSerializerOptions
{
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase // 字典键转为 camelCase
};
string json = JsonSerializer.Serialize(dictionary, options);
Console.WriteLine(json); //输出:{"userName":"John","userPwd":"123"}
var options2 = new JsonSerializerOptions
{
DictionaryKeyPolicy = new CustomCamelCasePolicy()
};
json = JsonSerializer.Serialize(dictionary, options2);
Console.WriteLine(json); //输出:{"userName":"John","userPwd":"123"}
}
}
public class CustomCamelCasePolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return char.ToLower(name[0]) + name.Substring(1);
}
}
五、与 JsonPropertyName
特性结合
JsonPropertyNameAttribute
的优先级高于 JsonNamingPolicy
,可覆盖命名策略。
public class Product
{
[JsonPropertyName("product_id")] // 显式指定名称
public int Id { get; set; }
public string Name { get; set; } // 使用命名策略
}
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var product = new Product { Id = 1, Name = "Laptop" };
string json = JsonSerializer.Serialize(product, options);
// 输出:{"product_id":1,"name":"Laptop"}(Name 转为 name)
六、命名策略的注意事项
在使用 JsonNamingPolicy
时,需要注意以下几点:
-
策略一致性:在序列化和反序列化过程中,应使用相同的命名策略,以确保名称映射的一致性。
// 序列化时使用驼峰命名 var serializedJson = JsonSerializer.Serialize(user, options); // 输出 {"userName":"Alice"} // 反序列化时需使用相同策略 var deserializedUser = JsonSerializer.Deserialize<User>(serializedJson, options); // 成功匹配属性
-
性能考虑:使用命名策略可能会对性能产生轻微的影响,因为需要进行额外的字符串转换操作。对于性能敏感的应用
- 可以考虑优化名称转换逻辑。
- 避免频繁创建 JsonSerializerOptions:如果命名策略是固定的,可以创建一个静态的
JsonSerializerOptions
实例,并在应用程序中复用它。
-
忽略大小写:默认情况下,System.Text.Json 在反序列化过程中是大小写敏感的。如果需要进行大小写不敏感的匹配,可以设置
PropertyNameCaseInsensitive
选项为true
。var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
结语
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料: