C# json使用之Json.NET(2)

以下为json.net的一系列参数设置以及属性设置说明,翻译自官方文档。有错误的地方请指正。

Serialization Attributes 序列化属性

序列化属性

属性可用于控制Json.NET如何序列化和反序列化.NET对象。

  • JsonObjectAttribute - 放在类上以控制它们应该如何序列化为JSON对象。

  • JsonArrayAttribute - 放置在集合上以控制它们应如何序列化为JSON数组。

  • JsonDictionaryAttribute - 放在字典上以控制它们应该如何序列化为JSON对象。

  • JsonPropertyAttribute - 放置在字段和属性上以控制它们应如何序列化为JSON对象中的属性。

  • JsonConverterAttribute - 放置在类或字段和属性上,以指定在序列化期间应使用哪个JsonConverter。

  • JsonExtensionDataAttribute - 放置在集合字段或属性上,以将没有匹配的类成员的属性反序列化到指定的集合中,并在序列化期间写入值。

  • JsonConstructorAttribute - 放置在构造函数上以指定在反序列化期间应该使用它来创建类。

标准.NET序列化属性

除了使用内置的Json.NET属性之外,Json.NET还在确定如何序列化和反序列化JSON时,查找SerializableAttribute(如果DefaultCutractResolver上的IgnoreSerializableAttribute设置为false)DataContractAttribute,DataMemberAttribute和NonSerializedAttribute以及属性。

Json.NET属性优先于标准.NET序列化属性(例如,如果属性上存在JsonPropertyAttribute和DataMemberAttribute,并且都自定义名称,则将使用JsonPropertyAttribute中的名称)。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[JsonObject(MemberSerialization.OptIn)]
public class Person
{
    // "John Smith"
    [JsonProperty]
    public string Name { get; set; }
 
    // "2000-12-15T22:11:03"
    [JsonProperty]
    public DateTime BirthDate { get; set; }
 
    // new Date(976918263055)
    [JsonProperty]
    public DateTime LastModified { get; set; }
 
    // not serialized because mode is opt-in
    public string Department { get; set; }
}

Json.NET序列化属性

本节包含以下小节:

  • JsonObjectAttribute

  • JsonArrayAttribute/JsonDictionaryAttribute

  • JsonPropertyAttribute

  • JsonIgnoreAttribute

  • JsonConverterAttribute

  • JsonExtensionDataAttribute

  • JsonConstructorAttribute

JsonObjectAttribute

此属性上的MemberSerialization标志指定成员序列化是否为opt-in(成员必须具有要序列化的JsonProperty或DataMember属性),opt-out(默认情况下所有内容都是序列化的,但可以使用JsonIgnoreAttribute忽略,Json.NET的默认值 行为)或字段(所有公共和私有字段都被序列化,属性被忽略)。

可以将此属性上的NamingStrategy设置设置为NamingStrategy类型,该类型指定如何序列化属性名称。

Json.NET将实现IEnumerable的.NET类序列化为填充了IEnumerable值的JSON数组。 放置JsonObjectAttribute会覆盖此行为并强制序列化程序序列化类的字段和属性。

DataContractAttribute可以用作JsonObjectAttribute的替代。 DataContractAttribute将默认成员序列化为opt-in。

JsonArrayAttribute/JsonDictionaryAttribute

JsonArrayAttribute和JsonDictionaryAttribute用于指定是否将类序列化为该集合类型。

集合属性具有自定义JsonConverter,类型名称处理和应用于集合项的引用处理的选项。

JsonPropertyAttribute

JsonPropertyAttribute有许多用途:

  • 默认情况下,JSON属性将与.NET属性具有相同的名称。 此属性允许自定义名称。

  • JsonPropertyAttribute指示当成员序列化设置为opt-in时应该序列化属性。

  • 它包括序列化和反序列化中的非公共属性。

  • 它可用于自定义属性值的类型名称,引用,null和默认值处理。

  • 它可用于自定义序列化属性名称的NamingStrategy。

  • 它可用于自定义属性的集合项JsonConverter,类型名称处理和引用处理。

DataMemberAttribute可以用作JsonPropertyAttribute的替代。

JsonIgnoreAttribute

从序列化中排除字段或属性。

NonSerializedAttribute可以用作JsonIgnoreAttribute的替代。

JsonConverterAttribute

JsonConverterAttribute指定用于转换对象的JsonConverter。

该属性可以放在类或成员上。 放在类上时,属性指定的JsonConverter将是序列化该类的默认方式。 当属性位于字段或属性上时,指定的JsonConverter将始终用于序列化该值。

使用JsonConverter的优先级是成员属性,然后是类属性,最后是传递给JsonSerializer的任何转换器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public enum UserStatus
{
    NotConfirmed,
    Active,
    Deleted
}
 
public class User
{
    public string UserName { get; set; }
 
    [JsonConverter(typeof(StringEnumConverter))]
    public UserStatus Status { get; set; }
}

此示例显示应用于属性的JsonConverterAttribute。

要将JsonConverter应用于集合中的项,请使用JsonArrayAttribute,JsonDictionaryAttribute或JsonPropertyAttribute,并将ItemConverterType属性设置为要使用的转换器类型。

JsonExtensionDataAttribute

JsonExtensionDataAttribute指示JsonSerializer反序列化属性,并且类型上没有匹配的字段或属性到指定的集合中。 在序列化期间,此集合中的值将写回实例的JSON对象。

即使已经写入了同名的属性,也会在序列化期间写入所有扩展数据值。

此示例显示应用于字段的JsonExtensionDataAttribute,在反序列化期间将不匹配的JSON属性添加到字段的集合中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class DirectoryAccount
{
    // normal deserialization
    public string DisplayName { get; set; }
 
    // these properties are set in OnDeserialized
    public string UserName { get; set; }
    public string Domain { get; set; }
 
    [JsonExtensionData]
    private IDictionary _additionalData;
 
    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
        // SAMAccountName is not deserialized to any property
        // and so it is added to the extension data dictionary
        string samAccountName = (string)_additionalData["SAMAccountName"];
 
        Domain = samAccountName.Split('\\')[0];
        UserName = samAccountName.Split('\\')[1];
    }
 
    public DirectoryAccount()
    {
        _additionalData = new Dictionary();
    }
}
 
 
string json = @"{
  'DisplayName': 'John Smith',
  'SAMAccountName': 'contoso\\johns'
}";
 
DirectoryAccount account = JsonConvert.DeserializeObject(json);
 
Console.WriteLine(account.DisplayName);
// John Smith
 
Console.WriteLine(account.Domain);
// contoso
 
Console.WriteLine(account.UserName);
// johns

JsonConstructorAttribute

JsonConstructorAttribute指示JsonSerializer在反序列化类时使用特定的构造函数。 它可以用于使用参数化构造函数而不是默认构造函数创建类,或者选择在有多个时使用哪个特定参数化构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class User
{
    public string UserName { get; private set; }
    public bool Enabled { get; private set; }
 
    public User()
    {
    }
 
    [JsonConstructor]
    public User(string userName, bool enabled)
    {
        UserName = userName;
        Enabled = enabled;
    }
}
 
string json = @"{
  ""UserName"": ""domain\\username"",
  ""Enabled"": true
}";
 
User user = JsonConvert.DeserializeObject(json);
 
Console.WriteLine(user.UserName);
// domain\username

Serialization Callbacks 序列化回调

序列化回调

Json.NET支持序列化回调方法。 回调可用于在JsonSerializer进行序列化和反序列化之前和之后操作对象。

  • OnSerializing

  • OnSerialized

  • OnDeserializing

  • OnDeserialized

要告诉序列化程序在对象的序列化生命周期中应该调用哪些方法,请使用适当的属性(OnSerializingAttribute,OnSerializedAttribute,OnDeserializingAttribute,OnDeserializedAttribute)修饰方法。

具有序列化回调方法的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class SerializationEventTestObject
{
    // 2222
    // This member is serialized and deserialized with no change.
    public int Member1 { get; set; }
 
    // The value of this field is set and reset during and 
    // after serialization.
    public string Member2 { get; set; }
 
    // This field is not serialized. The OnDeserializedAttribute 
    // is used to set the member value after serialization.
    [JsonIgnore]
    public string Member3 { get; set; }
 
    // This field is set to null, but populated after deserialization.
    public string Member4 { get; set; }
 
    public SerializationEventTestObject()
    {
        Member1 = 11;
        Member2 = "Hello World!";
        Member3 = "This is a nonserialized value";
        Member4 = null;
    }
 
    [OnSerializing]
    internal void OnSerializingMethod(StreamingContext context)
    {
        Member2 = "This value went into the data file during serialization.";
    }
 
    [OnSerialized]
    internal void OnSerializedMethod(StreamingContext context)
    {
        Member2 = "This value was reset after serialization.";
    }
 
    [OnDeserializing]
    internal void OnDeserializingMethod(StreamingContext context)
    {
        Member3 = "This value was set during deserialization";
    }
 
    [OnDeserialized]
    internal void OnDeserializedMethod(StreamingContext context)
    {
        Member4 = "This value was set after deserialization.";
    }
}

由Json.NET序列化和反序列化的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
SerializationEventTestObject obj = new SerializationEventTestObject();
 
Console.WriteLine(obj.Member1);
// 11
Console.WriteLine(obj.Member2);
// Hello World!
Console.WriteLine(obj.Member3);
// This is a nonserialized value
Console.WriteLine(obj.Member4);
// null
 
string json = JsonConvert.SerializeObject(obj, Formatting.Indented);
// {
//   "Member1": 11,
//   "Member2": "This value went into the data file during serialization.",
//   "Member4": null
// }
 
Console.WriteLine(obj.Member1);
// 11
Console.WriteLine(obj.Member2);
// This value was reset after serialization.
Console.WriteLine(obj.Member3);
// This is a nonserialized value
Console.WriteLine(obj.Member4);
// null
 
obj = JsonConvert.DeserializeObject(json);
 
Console.WriteLine(obj.Member1);
// 11
Console.WriteLine(obj.Member2);
// This value went into the data file during serialization.
Console.WriteLine(obj.Member3);
// This value was set during deserialization
Console.WriteLine(obj.Member4);
// This value was set after deserialization.

Serialization Error Handling 序列化错误处理

序列化错误处理

Json.NET支持序列化和反序列化期间的错误处理。 错误处理允许您捕获错误并选择是否处理它并继续序列化或让错误冒出来并在应用程序中抛出。

错误处理是通过两种方法定义的:JsonSerializer上的Error事件和OnErrorAttribute。

  • Error Event

  • OnErrorAttribute

Error Event

Error事件是在JsonSerializer上找到的事件处理程序。 只要在序列化或反序列化JSON时抛出异常,就会引发错误事件。 与JsonSerializer上的所有设置一样,它也可以在JsonSerializerSettings上设置并传递给JsonConvert上的序列化方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
List errors = new List();
 
List c = JsonConvert.DeserializeObject>(@"[
      '2009-09-09T00:00:00Z',
      'I am not a date and will error!',
      [
        1
      ],
      '1977-02-20T00:00:00Z',
      null,
      '2000-12-01T00:00:00Z'
    ]",
    new JsonSerializerSettings
    {
        Error = delegate(object sender, ErrorEventArgs args)
        {
            errors.Add(args.ErrorContext.Error.Message);
            args.ErrorContext.Handled = true;
        },
        Converters = { new IsoDateTimeConverter() }
    });
 
// 2009-09-09T00:00:00Z
// 1977-02-20T00:00:00Z
// 2000-12-01T00:00:00Z
 
// The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.
// Unexpected token parsing date. Expected String, got StartArray.
// Cannot convert null value to System.DateTime.

在此示例中,我们将JSON数组反序列化为DateTimes的集合。在JsonSerializerSettings上,已将一个处理程序分配给Error事件,该事件将记录错误消息并将错误标记为已处理。

反序列化JSON的结果是三个成功反序列化的日期和三个错误消息:一个用于格式错误的字符串(“我不是日期并且会出错!”),一个用于嵌套的JSON数组,另一个用于null值,因为该列表不允许可以为空的DateTimes。事件处理程序已记录这些消息,并且Json.NET继续反序列化JSON,因为错误被标记为已处理。

在Json.NET中,错误处理需要注意的一件事是,未处理的错误会冒出来并在其每个父项上引发事件。例如,序列化对象集合时的未处理错误将引发两次,一次针对该对象,然后再次针对该集合。这将允许您处理错误发生的位置或其父节点之一。

1
2
3
4
5
6
7
8
9
10
11
List errors = new List();
 
JsonSerializer serializer = new JsonSerializer();
serializer.Error += delegate(object sender, ErrorEventArgs args)
{
    // only log an error once
    if (args.CurrentObject == args.ErrorContext.OriginalObject)
    {
        errors.Add(args.ErrorContext.Error.Message);
    }
};

如果您没有立即处理错误并且只想对其执行一次操作,那么您可以检查ErrorEventArgs的CurrentObject是否等于OriginalObject。 OriginalObject是抛出错误的对象,而CurrentObject是引发事件的对象。 它们仅在第一次针对OriginalObject引发事件时相等。

OnErrorAttribute

OnErrorAttribute的工作方式与Json.NET支持的其他.NET序列化属性非常相似。 要使用它,只需将属性放在采用正确参数的方法上:StreamingContext和ErrorContext。 方法的名称无关紧要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class PersonError
{
    private List _roles;
 
    public string Name { get; set; }
    public int Age { get; set; }
 
    public List Roles
    {
        get
        {
            if (_roles == null)
            {
                throw new Exception("Roles not loaded!");
            }
 
            return _roles;
        }
        set { _roles = value; }
    }
 
    public string Title { get; set; }
 
    [OnError]
    internal void OnError(StreamingContext context, ErrorContext errorContext)
    {
        errorContext.Handled = true;
    }
}

在此示例中,访问Roles属性将在未设置角色时引发异常。 HandleError方法将在将Roles序列化为处理时设置错误,并允许Json.NET继续序列化该类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PersonError person = new PersonError
{
    Name = "George Michael Bluth",
    Age = 16,
    Roles = null,
    Title = "Mister Manager"
};
 
string json = JsonConvert.SerializeObject(person, Formatting.Indented);
 
Console.WriteLine(json);
//{
//  "Name": "George Michael Bluth",
//  "Age": 16,
//  "Title": "Mister Manager"
//}

Preserving Object References 保留对象引用

保留对象引用

默认情况下,Json.NET将按值对它遇到的所有对象进行序列化。 如果列表包含两个Person引用,并且两个引用都指向同一个对象,则JsonSerializer将写出每个引用的所有名称和值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Person p = new Person
{
    BirthDate = new DateTime(1980, 12, 23, 0, 0, 0, DateTimeKind.Utc),
    LastModified = new DateTime(2009, 2, 20, 12, 59, 21, DateTimeKind.Utc),
    Name = "James"
};
 
List people = new List();
people.Add(p);
people.Add(p);
 
string json = JsonConvert.SerializeObject(people, Formatting.Indented);
//[
//  {
//    "Name": "James",
//    "BirthDate": "1980-12-23T00:00:00Z",
//    "LastModified": "2009-02-20T12:59:21Z"
//  },
//  {
//    "Name": "James",
//    "BirthDate": "1980-12-23T00:00:00Z",
//    "LastModified": "2009-02-20T12:59:21Z"
//  }
//]

在大多数情况下,这是期望的结果,但在某些情况下,将列表中的第二项作为对第一项的引用是更好的解决方案。 如果现在反序列化上面的JSON,那么返回的列表将包含两个具有相同值的完全独立的Person对象。 按值写入引用也会导致出现循环引用的对象出现问题。

  • PreserveReferencesHandling

  • IsReference

  • IReferenceResolver

PreserveReferencesHandling

设置PreserveReferencesHandling将在序列化和反序列化JSON时跟踪对象引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
string json = JsonConvert.SerializeObject(people, Formatting.Indented,
    new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
 
//[
//  {
//    "$id": "1",
//    "Name": "James",
//    "BirthDate": "1983-03-08T00:00Z",
//    "LastModified": "2012-03-21T05:40Z"
//  },
//  {
//    "$ref": "1"
//  }
//]
 
List deserializedPeople = JsonConvert.DeserializeObject>(json,
    new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
 
Console.WriteLine(deserializedPeople.Count);
// 2
 
Person p1 = deserializedPeople[0];
Person p2 = deserializedPeople[1];
 
Console.WriteLine(p1.Name);
// James
Console.WriteLine(p2.Name);
// James
 
bool equal = Object.ReferenceEquals(p1, p2);
// true

列表中的第一个人是通过添加对象ID序列化的。 JSON中的第二个人现在只是对第一个人的引用。

使用PreserveReferencesHandling,现在在反序列化时只创建一个Person对象,该列表包含两个对它的引用,反映了我们开始使用的内容。

像$ id这样的元数据属性必须位于JSON对象的开头,以便在反序列化期间成功检测到。 如果无法控制JSON对象中的属性顺序,则可以使用MetadataPropertyHandling来删除此限制。

通过非默认构造函数设置值时,无法保留引用。 使用非默认构造函数时,必须在父值之前创建子值,以便将它们传递给构造函数,从而无法进行跟踪引用。 ISerializable类型是一个类的示例,其值使用非默认构造函数填充,并且不能与PreserveReferencesHandling一起使用。

IsReference

JsonSerializer上的PreserveReferencesHandling设置将更改所有对象的序列化和反序列化方式。 对于应该将哪些对象和成员序列化为引用的细粒度控制,JsonObjectAttribute,JsonArrayAttribute和JsonPropertyAttribute上有IsReference属性。

将JsonObjectAttribute或JsonArrayAttribute上的IsReference设置为true将意味着JsonSerializer将始终序列化该属性所针对的类型作为引用。 将JsonPropertyAttribute上的IsReference设置为true将仅将该属性序列化为引用。

1
2
3
4
5
6
[JsonObject(IsReference = true)]
public class EmployeeReference
{
    public string Name { get; set; }
    public EmployeeReference Manager { get; set; }
}

IReferenceResolver

要自定义生成和解析引用的方式,可以使用IReferenceResolver接口继承并使用JsonSerializer。

CustomCreationConverter 

CustomCreationConverter

CustomCreationConverter 是一个JsonConverter,它提供了一种在JSON反序列化期间自定义对象创建方式的方法。 一旦创建了对象,它就会被序列化程序填充到它上面。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface IPerson
{
    string FirstName { get; set; }
    string LastName { get; set; }
    DateTime BirthDate { get; set; }
}
 
public class Employee : IPerson
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
 
    public string Department { get; set; }
    public string JobTitle { get; set; }
}
 
public class PersonConverter : CustomCreationConverter
{
    public override IPerson Create(Type objectType)
    {
        return new Employee();
    }
}

这是一个非常简单的例子。 更复杂的场景可能涉及在运行时解析对象的对象工厂或服务定位器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//[
//  {
//    "FirstName": "Maurice",
//    "LastName": "Moss",
//    "BirthDate": "1981-03-08T00:00Z",
//    "Department": "IT",
//    "JobTitle": "Support"
//  },
//  {
//    "FirstName": "Jen",
//    "LastName": "Barber",
//    "BirthDate": "1985-12-10T00:00Z",
//    "Department": "IT",
//    "JobTitle": "Manager"
//  }
//]
 
List people = JsonConvert.DeserializeObject>(json, new PersonConverter());
 
IPerson person = people[0];
 
Console.WriteLine(person.GetType());
// Newtonsoft.Json.Tests.Employee
 
Console.WriteLine(person.FirstName);
// Maurice
 
Employee employee = (Employee)person;
 
Console.WriteLine(employee.JobTitle);
// Support

Serializing Collections 序列化集合

序列化集合

Json.NET非常支持序列化和反序列化对象集合。

  • Serializing Collections

  • Deserializing Collections

  • Deserializing Dictionaries

Serializing Collections

要序列化集合 - 通用列表,数组,字典或您自己的自定义集合 - 只需使用要获取JSON的对象调用序列化程序。 Json.NET将序列化集合及其包含的所有值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Product p1 = new Product
{
    Name = "Product 1",
    Price = 99.95m,
    ExpiryDate = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc),
};
Product p2 = new Product
{
    Name = "Product 2",
    Price = 12.50m,
    ExpiryDate = new DateTime(2009, 7, 31, 0, 0, 0, DateTimeKind.Utc),
};
 
List products = new List();
products.Add(p1);
products.Add(p2);
 
string json = JsonConvert.SerializeObject(products, Formatting.Indented);
//[
//  {
//    "Name": "Product 1",
//    "ExpiryDate": "2000-12-29T00:00:00Z",
//    "Price": 99.95,
//    "Sizes": null
//  },
//  {
//    "Name": "Product 2",
//    "ExpiryDate": "2009-07-31T00:00:00Z",
//    "Price": 12.50,
//    "Sizes": null
//  }
//]

Deserializing Collections

要将JSON反序列化为.NET集合,只需指定要反序列化的集合类型即可。 Json.NET支持多种集合类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
string json = @"[
  {
    'Name': 'Product 1',
    'ExpiryDate': '2000-12-29T00:00Z',
    'Price': 99.95,
    'Sizes': null
  },
  {
    'Name': 'Product 2',
    'ExpiryDate': '2009-07-31T00:00Z',
    'Price': 12.50,
    'Sizes': null
  }
]";
 
List products = JsonConvert.DeserializeObject>(json);
 
Console.WriteLine(products.Count);
// 2
 
Product p1 = products[0];
 
Console.WriteLine(p1.Name);
// Product 1

Deserializing Dictionaries

使用Json.NET,您还可以将JSON对象反序列化为.NET泛型字典。 JSON对象的属性名称和值将添加到字典中。

1
2
3
4
5
6
7
8
9
string json = @"{""key1"":""value1"",""key2"":""value2""}";
 
Dictionary values = JsonConvert.DeserializeObject>(json);
 
Console.WriteLine(values.Count);
// 2
 
Console.WriteLine(values["key1"]);
// valu

你可能感兴趣的:(C#)