Newtonsoft.Json使用

序列化与反序列化

1、反序列化是不区分大小写的

以下代码可以正常运行

public static void Test()
        {
            //反序列化时 不区分大小写的
            string jsonString = "{\"name\":\"张三\",\"age\":18,\"sex\":\"男\"}";
            var model = JsonConvert.DeserializeObject<PersonJson>(jsonString);
            if (model != null)
            {
                Console.WriteLine(model.Name);
            }
        }
public class PersonJson
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public string Sex { get; set; }

    }

2、序列化

若想不区分大小写,参考:使用Newtonsoft.Json进行Json序列化忽略首字母大小写,Json字符串反序列化到子集对象(C#)

JObject 用法 、JProperty 用法、JArray 用法

使用LINQ to JSON前,需要引用Newtonsoft.Json的dll和using Newtonsoft.Json.Linq的命名空间。

LINQ to JSON主要使用到JObject, JArray, JProperty和JValue这四个对象,

  • JObject用来生成一个JSON对象,简单来说就是生成”{}”,
  • JArray用来生成一个JSON数组,也就是”[]”,
  • JProperty用来生成一个JSON数据,格式为key/value的值,
  • 而JValue则直接生成一个JSON值

简单示例:

JArray jo1 = (JArray)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(list));
 JObject a1 = (JObject)resultJson["group"][times];
 a1.Add(new JProperty("list", jo1));

参考:

C# JArray与JObject 的使用

C# 关于JArray和JObject封装JSON对象

问题

1、平方² 保存到文件变成了2

键盘输入平方(m²)或立方(m³)等特殊字符

var test =new {name ="速度(m/²)"};

注意: File.WriteAllText()是,编码要设置为Encoding.UTF8,而不能是Default。

 

2、json字符串和 对象(复杂对象)  相互解析时,字段一一匹配的问题

2.1 MissingMemberHandling可以解决  对象中缺少 Json字符串中定义的字段

  并通过使用`JsonExtensionData`特性和`Dictionary<string, JToken>`类来捕获未解析的JSON属性

JsonSerializerSettings jss = new JsonSerializerSettings();
            jss. MissingMemberHandling = MissingMemberHandling.Error;
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj, Formatting.Indented, jss);

使用`Newtonsoft.Json`将字符串转换为对象时,确保对象中的所有字段在字符串中都能找到的写法是在`DeserializeObject`方法的第二个参数中添加一个`JsonSerializerSettings`对象,并将其`MissingMemberHandling`属性设置为`MissingMemberHandling.Error`。这样,当字符串中的字段没有对应到对象的属性时,将会抛出异常。

以下是示例代码:

```csharp
using Newtonsoft.Json;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

string json = @"{""Name"":""Tom"",""Age"":30,""Gender"":""male""}"; // 假设字符串中有一个未定义的 Gender 字段
JsonSerializerSettings settings = new JsonSerializerSettings
{
    MissingMemberHandling = MissingMemberHandling.Error  // 指定 MissingMemberHandling 属性为 Error
};
Person person = JsonConvert.DeserializeObject<Person>(json, settings); // 进行字符串转换为对象
```

在此示例中,我们定义了一个`Person`类,并初始化了一个包含未定义的`Gender`字段的JSON字符串。在调用`DeserializeObject`方法时,我们将第二个参数`settings`设置为一个包含`MissingMemberHandling`属性的`JsonSerializerSettings`对象,并将其值设置为`MissingMemberHandling.Error`,这将会在字符串中有未定义字段的情况下引发异常。最后,我们调用`DeserializeObject`方法将JSON字符串转换为`Person`对象,如果字符串中包含未定义字段,则会引发异常。

 

可以捕获哪些没有 解析的字段:

如果对象中的字段比字符串中的字段多,可以创建`JsonSerializerSettings`对象,并将其`MissingMemberHandling`属性设置为`MissingMemberHandling.Error`。这将会在反序列化过程中引发异常,以指示缺少的成员或JSON属性。

另一种方法是使用`JsonExtensionData`特性和`Dictionary<string, JToken>`类来捕获未解析的JSON属性。使用`JsonExtensionData`特性和`Dictionary<string, JToken>`类,可以在定义时不需要知道有哪些未解析的JSON属性(因为它们存储在字典中)。

【这种方式 只能检测:字符串比对象多的情况,字符串中多了的会放在 ,字典中】

以下是示例代码:

```csharp
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    [JsonExtensionData]
    private IDictionary<string, JToken> additionalProperties;

    public void Validate()
    {
        if (additionalProperties != null && additionalProperties.Count > 0)
        {
            throw new JsonSerializationException(string.Format("未解析的 JSON 属性: {0}", string.Join(",", additionalProperties.Keys)));
        }
    }
}

string json = @"{""Name"":""Tom"",""Age"":30,""Gender"":""male""}"; // 字段 "Gender" 对象中未定义
JsonSerializerSettings settings = new JsonSerializerSettings
{
    MissingMemberHandling = MissingMemberHandling.Error // 设置 MissingMemberHandling 属性
};
Person person = JsonConvert.DeserializeObject<Person>(json, settings); // 进行反序列化
person.Validate(); // 检查格式是否正确
```

在此示例中,我们定义了一个`Person`类,用于存储JSON对象的属性。在这个类中,我们将`additionalProperties`字段使用`JsonExtensionData`特性标记为扩展数据。当使用`DeserializeObject`方法将JSON字符串转换为`Person`对象时,所有未在类定义中列出的属性都将存储在`additionalProperties`字典中。我们定义了一个`Validate`方法来检查`additionalProperties`字段,并在其中引发异常,如果检查失败。

 参考:https://learn.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json/handle-overflow?pivots=dotnet-6-0

2.2 结合JsonProperty特性和Required属性,解决 Json中缺少 对象中定义的字段

需要结合JsonProperty特性和Required属性来解决。使用JsonProperty特性可以映射Json字典中的属性名,同时,可以使用Required属性,它会强制要求在 JSON 字典中具有指定的属性。只要Required属性将其设置为true,则在反序列化过程中如果缺少其对应的属性,则会抛出异常。

using Newtonsoft.Json;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    [JsonProperty(Required = Required.Always)]
    public Student Std { get; set; }
}

public class Student
{
    public int Id { get; set; }
    [JsonProperty(Required = Required.Always)]
    public int Parent { get; set; }
}

string json = @"{""Name"":""Tom"",""Age"":30,""Std"":{""Id"":12}}"; // 缺少 "Parent" 属性
Person person = JsonConvert.DeserializeObject<Person>(json); // 进行反序列化

在此示例中,我们在Person类和Student类的属性上分别使用了JsonPropertyRequired属性。Required属性的值设置为Required.Always,表示在JSON字符串中必须包含此属性。由于JSON字符串缺少Parent属性,当我们使用JsonConvert.DeserializeObject方法将JSON字符串转换为Person对象时,将抛出异常。

总之,使用JsonProperty特性和Required属性可以解决JSON字符串中少字段的问题,确保在反序列化时字段一一匹配,缺失的部分会报错。

2.3 对所有 属性打特性

假如一个复杂对象有上百个字段,所有字段都需要注明[JsonProperty(Required = Required.Always)],该如何写?

如果一个复杂对象有很多字段,每个字段都需要使用`[JsonProperty(Required = Required.Always)]`特性来强制要求在 JSON 字典中有对应的属性,那么我们可以使用属性标记器来对所有的属性进行统一标记。具体步骤如下:

1.定义一个自定义特性,用于在所有属性上注明`[JsonProperty(Required = Required.Always)]`特性:

```csharp
[AttributeUsage(AttributeTargets.Property)]
public class RequiredJsonPropertyAttribute : Attribute { }
```

2.在所有要求强制要求在 JSON 字典中有对应的属性的属性上,标记自定义特性,如下所示:

```csharp
public class Person
{
    [RequiredJsonProperty]
    public string Name { get; set; }

    [RequiredJsonProperty]
    public int Age { get; set; }

    [RequiredJsonProperty]
    public Student Std { get; set; }

    // ...
}
```

3.在反序列化对象之前,使用反射来为所有打了标记的属性添加`[JsonProperty(Required = Required.Always)]`特性,具体实现如下:

```csharp
using Newtonsoft.Json;
using System.Linq;
using System.Reflection;

public class MyJsonConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object instance = Activator.CreateInstance(objectType);
        PropertyInfo[] properties = objectType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (PropertyInfo property in properties)
        {
            RequiredJsonPropertyAttribute attribute = property.GetCustomAttribute<RequiredJsonPropertyAttribute>();
            if (attribute != null)
            {
                JsonPropertyAttribute jsonAttribute = property.GetCustomAttributes(true).OfType<JsonPropertyAttribute>().FirstOrDefault();
                if (jsonAttribute == null)
                {
                    jsonAttribute = new JsonPropertyAttribute();
                }
                jsonAttribute.Required = Required.Always;
                property.SetCustomAttribute(jsonAttribute);
            }
        }
        serializer.Populate(reader, instance);
        return instance;
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override bool CanWrite
    {
        get { return false; }
    }
}
```

以下是使用示例:

```csharp
string json = @"{""Name"":""Tom"",""Age"":30,""Std"":{""Id"":12}}"; // 缺少 "Parent" 属性

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new MyJsonConverter());

Person person = JsonConvert.DeserializeObject<Person>(json, settings); // 进行反序列化

```

在此示例中,我们首先定义了一个名为`RequiredJsonPropertyAttribute`的自定义特性,并将其用于在所有应强制要求在JSON字典中具有相应属性的属性上。然后,我们创建了一个派生自`JsonConverter`的自定义JSON转换器,名为`MyJsonConverter`。在`ReadJson`方法中,我们通过反射遍历所有打了特性标记`[RequiredJsonProperty]`的属性,并添加到`[JsonProperty(Required = Required.Always)]`特性中来强制要求在JSON字典中存在相应的属性。最后,使用`settings.Converters.Add`方法将`MyJsonConverter`对象添加到`JsonConvert.DeserializeObject`方法的设置参数中。

总之,使用自定义特性与反射技术,可以用这种方式轻松地为所有复杂对象的属性添加`[JsonProperty(Required = Required.Always)]`特性,确保在反序列化时字段一一匹配,缺失的部分会报错。

 

posted @ 2022-07-04 20:09  peterYong  阅读(1687)  评论(0编辑  收藏  举报