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

总目录


一、JsonNamingPolicy 是什么?

1. 定义

JsonNamingPolicySystem.Text.Json 中用于 控制属性名称在序列化/反序列化时的命名格式的策略类。它允许开发者将 .NET 对象的属性名称转换为指定的格式(如 camelCasesnake_case 等),或自定义其他命名规则。

JsonNamingPolicy 是 System.Text.Json 命名空间中的一个类,用于定义如何在序列化和反序列化过程中处理属性名称与 JSON 字段名称之间的映射关系。

  • 通过使用 JsonNamingPolicy,开发者可以自定义 JSON 字段的命名约定,例如将 C# 中的驼峰命名约定转换为 JSON 中的蛇形命名约定,或者反之。
  • JsonNamingPolicy 通过重写 ConvertName 方法来实现名称转换逻辑。在序列化过程中,ConvertName 方法接收 C# 属性名称并返回 JSON 字段名称;在反序列化过程中,它接收 JSON 字段名称并返回 C# 属性名称。这种双向映射机制确保了在序列化和反序列化过程中名称转换的一致性。

2. 核心作用

  • 序列化时:将 C# 属性名转换为指定格式(如 PascalCasecamelCase)。
  • 反序列化时:将 JSON 中的键名转换为对象的属性名。
  • 命名优先级:显式标记 [JsonPropertyName] 的属性优先级高于命名策略。

3. 适用场景

JsonNamingPolicy 是 System.Text.Json 中 控制属性名称格式的核心工具,适用于以下场景:

  • 适配前后端命名规范(如 C# 的 PascalCase 转为前端的 camelCase)。
  • 统一 API 接口的键名格式(如 snake_case)。
  • 自定义特殊命名规则(如添加前缀/后缀)。

通过内置策略或自定义实现,开发者可以灵活控制 JSON 的属性名称,确保与外部系统的兼容性及代码的可维护性。

二、内置命名策略

System.Text.Json 提供了多种内置命名策略,以满足不同的命名约定需求:

1. 默认策略

默认情况下,无策略(null) ,即保留原始属性名(如 UserNameUserName)。

不过通常在 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 时,需要注意以下几点:

  1. 策略一致性:在序列化和反序列化过程中,应使用相同的命名策略,以确保名称映射的一致性。

    // 序列化时使用驼峰命名
    var serializedJson = JsonSerializer.Serialize(user, options); // 输出 {"userName":"Alice"}
    
    // 反序列化时需使用相同策略
    var deserializedUser = JsonSerializer.Deserialize<User>(serializedJson, options);
    // 成功匹配属性
    
  2. 性能考虑:使用命名策略可能会对性能产生轻微的影响,因为需要进行额外的字符串转换操作。对于性能敏感的应用

    • 可以考虑优化名称转换逻辑。
    • 避免频繁创建 JsonSerializerOptions:如果命名策略是固定的,可以创建一个静态的 JsonSerializerOptions 实例,并在应用程序中复用它。
  3. 忽略大小写:默认情况下,System.Text.Json 在反序列化过程中是大小写敏感的。如果需要进行大小写不敏感的匹配,可以设置 PropertyNameCaseInsensitive 选项为 true

    var options = new JsonSerializerOptions
    {
        PropertyNameCaseInsensitive = true
    };
    

结语

回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲤籽鲲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值