来自云龙湖轮廓分明的月亮 发表于 2025-4-4 14:06:55

C# System.Text.Json 中 JsonNamingPolicy 利用详解

总目录

一、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 中的键名转换为对象的属性名。
[*]定名优先级:显式标记 的属性优先级高于定名计谋。
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)
    {
      returnchar.ToLower(name) + name.Substring(1);
    }
}
五、与 JsonPropertyName 特性联合

JsonPropertyNameAttribute 的优先级高于 JsonNamingPolicy,可覆盖定名计谋。
public class Product
{
    // 显式指定名称
    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 知识汇总
盼望以上内容可以帮助到各人,如文中有不对之处,还请品评指正。
参考资料:


[*].NET 官方文档:JsonNamingPolicy
[*]System.Text.Json 完全指南

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: C# System.Text.Json 中 JsonNamingPolicy 利用详解