JSON序列化-System.Text.Json

序列化

序列化为字符串

UserDTO user = new UserDTO
{
    UserName = "张三",
    Age = 18,
    Birthday = new DateTime(2000, 1, 1, 1, 1, 1),
    IsCheck = true
};
string json = JsonSerializer.Serialize(user);
Console.WriteLine(json);

// {"UserName":"\u5F20\u4E09","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true}

输出的中文,竟然被转码了,需要自定义序列化选项来控制输出

序列化为字节数组

UserDTO user = new UserDTO
{
    UserName = "张三",
    Age = 18,
    Birthday = new DateTime(2000, 1, 1, 1, 1, 1),
    IsCheck = true
};

byte[] data = JsonSerializer.SerializeToUtf8Bytes(user);
// 转为字符串
Console.WriteLine(System.Text.UTF8Encoding.UTF8.GetString(data));

// {"UserName":"\u5F20\u4E09","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true}

中文转码

设置一下JsonSerializerOptions中的Encoder字段

UserDTO user = new UserDTO
{
    UserName = "张三",
    Age = 18,
    Birthday = new DateTime(2000, 1, 1, 1, 1, 1),
    IsCheck = true
};

JsonSerializerOptions options = new JsonSerializerOptions
{
    Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);

// {"UserName":"张三","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true}

自定义属性名

1、JsonPropertyName实现

class UserDTO
{
    [JsonPropertyName("user_name")]
    public string UserName { get; set; }
    public int? Age { get; set; }
    public DateTime? Birthday { get; set; }
    public bool? IsCheck { get; set; }
}
//
UserDTO user = new UserDTO
 {
     UserName = "张三",
     Age = 18,
     Birthday = new DateTime(2000, 1, 1, 1, 1, 1),
     IsCheck = true
 };

 string json = JsonSerializer.Serialize(user);
 Console.WriteLine(json);
// {"user_name":"\u5F20\u4E09","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true}

2、JsonSerializerOptions.PropertyNamingPolicy参数

系统内置了几种名称转换方式(C#默认是大驼峰规则,所以内置策略里面没有这个选项)

微软PropertyNamingPolicy对比

命名策略说明原始属性名称经过转换的属性名称
CamelCase第一个单词以小写字符开头。连续单词以大写字符开头TempCelsiustempCelsius
KebabCaseLower单词由连字符分隔,所有字符均为小写TempCelsiustemp-celsius
KebabCaseUpper单词由连字符分隔,所有字符均为大写TempCelsiusTEMP-CELSIUS
SnakeCaseLower单词用下划线分隔,所有字符均为小写TempCelsiustemp_celsius
SnakeCaseUpper单词用下划线分隔,所有字符均为大写TempCelsiusTEMP_CELSIUS
UserDTO user = new UserDTO
{
    UserName = "张三",
    Age = 18,
    Birthday = new DateTime(2000, 1, 1, 1, 1, 1),
    IsCheck = true
};

JsonSerializerOptions options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};

string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);

// {"user_name":"\u5F20\u4E09","age":18,"birthday":"2000-01-01T01:01:01","is_check":true}

3、自定义命名策略

扩展抽象类JsonNamingPolicy

  • 自定义命名策略

统一在名称前面加前缀

  public class UserNamingPolicy : JsonNamingPolicy
  {
      public override string ConvertName(string name)
      {
          return "ext_" + name;
      }
  }
  • 使用自定义命名策略
UserDTO user = new UserDTO
{
    UserName = "张三",
    Age = 18,
    Birthday = new DateTime(2000, 1, 1, 1, 1, 1),
    IsCheck = true
};

JsonSerializerOptions options = new JsonSerializerOptions
{
    PropertyNamingPolicy = new UserNamingPolicy()
};

string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);

// {"ext_UserName":"\u5F20\u4E09","ext_Age":18,"ext_Birthday":"2000-01-01T01:01:01","ext_IsCheck":true}

4、字典key使用命名策略

字典对象的key是动态的,重命名需要单独设置一下,命名策略是公用的

 class UserDTO
 {
     [JsonPropertyName("user_name")]
     public string UserName { get; set; }
     public int? Age { get; set; }
     public DateTime? Birthday { get; set; }
     public bool? IsCheck { get; set; }
     public Dictionary<String, object> Ext { get; set; }
 }
 ///
UserDTO user = new UserDTO
  {
      UserName = "张三",
      Age = 18,
      Birthday = new DateTime(2000, 1, 1, 1, 1, 1),
      IsCheck = true,
      Ext = new Dictionary<string, object> {
          { "width", 10 },
          { "height", 200 },
          { "desc", "测试" }
      }
  };

  JsonSerializerOptions options = new JsonSerializerOptions
  {
      //DictionaryKeyPolicy= JsonNamingPolicy.CamelCase
      DictionaryKeyPolicy = new UserNamingPolicy()
  };

  string json = JsonSerializer.Serialize(user, options);
  Console.WriteLine(json);

  // {"user_name":"\u5F20\u4E09","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true,"Ext":{"ext_width":10,"ext_height":200,"ext_desc":"\u6D4B\u8BD5"}}

输出字段顺序控制

通过JsonPropertyOrder来控制,由小到大现实,不设置默认是0

 class UserDTO
 {
     [JsonPropertyOrder(100)]
     public string UserName { get; set; }
     public int? Age { get; set; }
     [JsonPropertyOrder(99)]
     public DateTime? Birthday { get; set; }
     [JsonPropertyOrder(-100)]
     public bool? IsCheck { get; set; }
     [JsonPropertyOrder(-99)]
     public Dictionary<String, object> Ext { get; set; }
 }
 //
UserDTO user = new UserDTO
{
   UserName = "张三",
   Age = 18,
   Birthday = new DateTime(2000, 1, 1, 1, 1, 1),
   IsCheck = true,
   Ext = new Dictionary<string, object> {
       { "width", 10 },
       { "height", 200 },
       { "desc", "测试" }
   }
};

JsonSerializerOptions options = new JsonSerializerOptions
{
   //DictionaryKeyPolicy= JsonNamingPolicy.CamelCase
   DictionaryKeyPolicy = new UserNamingPolicy()
};

string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);
 
// {"IsCheck":true,"Ext":{"ext_width":10,"ext_height":200,"ext_desc":"\u6D4B\u8BD5"},"Age":18,"Birthday":"2000-01-01T01:01:01","UserName":"\u5F20\u4E09"}

枚举展示

默认情况下,枚举会序列化为数字(默认从0开始,也可以自定义)。 若要将枚举名称序列化为字符串,需要使用JsonStringEnumConverter,设置JsonSerializerOptions类的Converters字段
JsonStringEnumConverter有两个关键参数
1、namingPolicy,输出的字符串可以配置命名策略,默认为null,原样输出
2、allowIntegerValues,默认为true,当为true时,如果枚举值未定义,它将以数字形式输出,而不是字符串形式
-定义Gender枚举

  public enum Gender
  {
      Male, Female
  }
  • 定义UserDTO
 class UserDTO
 {
     [JsonPropertyOrder(100)]
     public string UserName { get; set; }
     public int? Age { get; set; }
     [JsonPropertyOrder(99)]
     public DateTime? Birthday { get; set; }
     [JsonPropertyOrder(-100)]
     public bool? IsCheck { get; set; }
     [JsonPropertyOrder(-99)]
     public Dictionary<String, object> Ext { get; set; }
     public Gender? UserGender { get; set; }
 }
  • 使用枚举
UserDTO user = new UserDTO
{
    UserName = "张三",
    Age = 18,
    Birthday = new DateTime(2000, 1, 1, 1, 1, 1),
    IsCheck = true,
    Ext = new Dictionary<string, object> {
        { "width", 10 },
        { "height", 200 },
        { "desc", "测试" }
    },
    UserGender = Gender.Male
};

JsonSerializerOptions options = new JsonSerializerOptions
{
    Converters =    {
        new JsonStringEnumConverter()
    }
};

string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);

// {"IsCheck":true,"Ext":{"width":10,"height":200,"desc":"\u6D4B\u8BD5"},"Age":18,"UserGender":"Male","Birthday":"2000-01-01T01:01:01","UserName":"\u5F20\u4E09"}

属性忽略

默认情况下,所有公共属性都会序列化。 如果不想让某些属性出,可以使用如下方法控制输出

1、使用JsonIgnore

主要针对单个属性,个性化配置

  • Never,序列化、反序列化
  • Always,永远会被忽略
  • WhenWritingDefault,默认值场景,会被忽略(例如引用类型时的null)
  • WhenWritingNull,适用引用类型,为null时被忽略
  • 定义UserDTO
 class UserDTO
 {
     [JsonIgnore(Condition = JsonIgnoreCondition.Always)]
     public string UserName { get; set; }
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
     public int Age { get; set; }
     [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
     public DateTime? Birthday { get; set; }
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     public bool? IsCheck { get; set; }
     public Dictionary<String, object> Ext { get; set; }
     public Gender? UserGender { get; set; }
 }
  • 具体使用
 UserDTO user = new UserDTO
 {
     UserName = "张三",
     Age = 0,
     UserGender = Gender.Male
 };

 JsonSerializerOptions options = new JsonSerializerOptions
 {
     Converters =    {
         new JsonStringEnumConverter()
     }
 };

 string json = JsonSerializer.Serialize(user, options);
 Console.WriteLine(json);
 
// {"Birthday":null,"Ext":null,"UserGender":"Male"}

UserName : 设置为Always,永不显示,无论是否有值
Birthday:设置为Never,永远显示,为null也会显示
Age:设置为WhenWritingDefault,默认值不显示,值类型,默认为0,所以不显示
IsCheck:设置为WhenWritingNull,null不显示,可空类型,默认为null

2、使用JsonSerializerOptions

  • IgnoreReadOnlyProperties忽略所有只读属性
JsonSerializerOptionsoptions = new JsonSerializerOptions
{
    IgnoreReadOnlyProperties = true
};
  • DefaultIgnoreCondition,与JsonIgnore的条件通用
JsonSerializerOptions options = new JsonSerializerOptions
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
  • 定义UserDTO
 class UserDTO
 {
     public string UserName { get; set; }
     public int Age { get; set; }
     public DateTime? Birthday { get; private set; } = new DateTime(2000, 12, 20, 1, 1, 1);
     public bool? IsCheck { get; set; }
     public Dictionary<String, object> Ext { get; set; }
     public Gender? UserGender { get; set; }
 }
  • 使用
 UserDTO user = new UserDTO
 {
     UserName = "张三",
 };

 JsonSerializerOptions options = new JsonSerializerOptions
 {
     IgnoreReadOnlyProperties = true,
     DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
 };

 string json = JsonSerializer.Serialize(user, options);
 Console.WriteLine(json);
// {"UserName":"\u5F20\u4E09","Age":0}

Birthday:set方法是私有的,属于只读属性,虽然有值,但是也会被忽略
Ext :引用类型,null,被忽略

包含字段(field)

一般情况,公共属性字段默认会被序列化,但是字段(field)默认不会被序列化,如果需要把指定的field序列化,需要特殊设置

JsonInclude

针对单个field进行设置

 [JsonInclude]
 public String Hello;

JsonSerializerOptionsIncludeFields字段

全局field设置

JsonSerializerOptions options = new JsonSerializerOptions
{
    IncludeFields = true
};
  • 定义UserDTO
   class UserDTO
   {
       public string UserName { get; set; }
       [JsonInclude]
       public String Hello;
       public String World;
   }
  • 使用
UserDTO user = new UserDTO
{
    UserName = "张三",
    Hello = "hello",
    World = "world"
};

JsonSerializerOptions options = new JsonSerializerOptions
{
    IncludeFields = true
};

string json = JsonSerializer.Serialize(user, options );
Console.WriteLine(json);

格式化输出

JsonSerializerOptions类的WriteIndented字段

   UserDTO user = new UserDTO
   {
       UserName = "张三",
       Birthday = new DateTime(2000, 12, 20, 1, 1, 1)
   };

   JsonSerializerOptions options = new JsonSerializerOptions
   {
       WriteIndented = true,
       DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
   };

   string json = JsonSerializer.Serialize(user, options);
   Console.WriteLine(json);
{
	"UserName": "\u5F20\u4E09",
	"Age": 0,
	"Birthday": "2000-12-20T01:01:01"
}

日期格式化

需要扩展JsonConverter抽象类,实现自定义类型转换输出

  • 定义日期转换器
  public class DateTimJsonConverter : JsonConverter<DateTime>
  {
      public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
      {
          return DateTime.Parse(reader.GetString());
      }

      public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
      {
          writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
      }
  }
  • 使用
UserDTO user = new UserDTO
{
    UserName = "张三",
    Birthday = new DateTime(2000, 12, 20, 1, 1, 1)
};

JsonSerializerOptions options = new JsonSerializerOptions
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    Converters = {
        new DateTimJsonConverter()
    }
};

string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);

// {"UserName":"\u5F20\u4E09","Age":0,"Birthday":"2000-12-20 01:01:01"}

反序列化

注意值类型触发异常

  • 对于值类型属性,例如int等,json文本中该属性最好有合法的值,否则整个key-value数据就不要提供,否则会抛异常
  • 将值类型定义为可空类型,例如int换成int?或则Nullable<int> (不确定上游系统返回的数据格式,尽量做好兼容)
 class UserDTO
 {
     public string UserName { get; set; }
     public int Age { get; set; }
     public DateTime? Birthday { get; set; }
     public bool? IsCheck { get; set; }
     public Dictionary<String, object> Ext { get; set; }
     public Gender? UserGender { get; set; }
 }

示例1

这里是一个正常返回的示例,Age虽然是值类型,但是提供的数据,有正常的值

string json = """
   {"UserName":"张三","Age":18,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}
   """;
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);

示例2

这里没有提供Age数据,反序列化不会解析这个字段,会给默认值0

string json = """
    {"UserName":"张三","IsCheck":true,"Birthday":"2000-12-20T01:01:01"}
    """;
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);

示例3

这里给值类型的Age提供了null,肯定会抛异常;类似场景在Java中有intInteger
如果Age属性定义为int?,就当成引用类型处理,最终解析为null

string json = """
    {"UserName":"张三","Age":null,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}
    """;
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);

必须属性

必须的要求哪怕提供null也是可以的,否则验证不通过

  • required关键字
  • JsonRequiredAttribute属性
  • JsonSerializerOptionsTypeInfoResolver字段
class UserDTO
{
    public required string UserName { get; set; }
    [JsonRequired]
    public Nullable<int> Age { get; set; }
    public DateTime? Birthday { get; set; }
    public bool? IsCheck { get; set; }
    public Dictionary<String, object> Ext { get; set; }
    public Gender? UserGender { get; set; }
}

示例1

Age字段标记必须,提供一个null也代表赋值了,可以通过验证

string json = """
   {"UserName":"张三","Age":null,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}
   """;
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);

示例2

UserName字段标记必须,输入数据没提供任何数据,验证不通过

string json = """
    {"Age":null,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}
    """;
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);

示例3

通过TypeInfoResolver设置某个属性是否必须

JsonSerializerOptions options = new JsonSerializerOptions
 {
     TypeInfoResolver = new DefaultJsonTypeInfoResolver
     {
         Modifiers =
         {
             static typeInfo =>
             {
                 if (typeInfo.Kind != JsonTypeInfoKind.Object)
                 {
                     return;
                 }

                 foreach (JsonPropertyInfo propertyInfo in typeInfo.Properties)
                 {
                     if("UserName".Equals(propertyInfo.Name))
                     {
                         propertyInfo.IsRequired = true;
                     }
                 }
             }
         }
     }
 };

 string json = """
     {"Age":null,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}
     """;
 UserDTO user = JsonSerializer.Deserialize<UserDTO>(json, options);
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值