Tạo thuộc tính deserialize nhưng không tuần tự hóa với json.net


124

Chúng tôi có một số tệp cấu hình được tạo bằng cách tuần tự hóa các đối tượng C # với Json.net.

Chúng tôi muốn di chuyển một thuộc tính của lớp được tuần tự hóa từ một thuộc tính enum đơn giản sang một thuộc tính của lớp.

Một cách dễ dàng để thực hiện điều này, là để thuộc tính enum cũ trên lớp và sắp xếp để Json.net đọc thuộc tính này khi chúng ta tải cấu hình, nhưng không lưu lại khi chúng ta tuần tự hóa đối tượng tiếp theo. Chúng tôi sẽ giải quyết việc tạo lớp mới từ enum cũ một cách riêng biệt.

Có cách nào đơn giản để đánh dấu (ví dụ: với các thuộc tính) thuộc tính của một đối tượng C #, để Json.net CHỈ bỏ qua nó khi tuần tự hóa, nhưng chú ý đến nó khi giải mã không?


Còn về trình chuyển đổi tùy chỉnh: bạn có thể sử dụng nó làm thuộc tính trên thuộc tính của mình, ghi đè ReadJson và WriteJson bằng các biên dịch khác nhau, phải không? Ví dụ (không phải chính xác những gì bạn cần, nhưng ...) weblogs.asp.net/thangchung/archive/2010/08/26/…
Raphaël Althaus

Thuộc tính OnDeserialized có thể là một công việc xung quanh cho bạn
Estefany Velez

Điều đó có thể xảy ra bằng cách sử dụng thuộc tính `[JsonIgnore] 'không ?? james.newtonking.com/archive/2009/10/23/…
Juri

Bạn có thể mở rộng về cách điều này có thể được sử dụng chỉ theo một hướng, theo đoạn cuối của q?
Will Dean

Có thể sử dụng [JsonIgnore] kết hợp với bộ định vị riêng thứ cấp được trang trí bằng thuộc tính [JsonProperty]. Cũng có một số giải pháp đơn giản khác. Tôi đã thêm một bản ghi chi tiết.
Brian Rogers

Câu trả lời:


133

Thực tế có một số cách tiếp cận khá đơn giản mà bạn có thể sử dụng để đạt được kết quả như mong muốn.

Ví dụ, giả sử rằng bạn có các lớp của mình hiện được định nghĩa như thế này:

class Config
{
    public Fizz ObsoleteSetting { get; set; }
    public Bang ReplacementSetting { get; set; }
}

enum Fizz { Alpha, Beta, Gamma }

class Bang
{
    public string Value { get; set; }
}

Và bạn muốn làm điều này:

string json = @"{ ""ObsoleteSetting"" : ""Gamma"" }";

// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);

// migrate
config.ReplacementSetting = 
    new Bang { Value = config.ObsoleteSetting.ToString() };

// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);

Để có được điều này:

{"ReplacementSetting":{"Value":"Gamma"}}

Phương pháp 1: Thêm phương pháp ShouldSerialize

Json.NET có khả năng tuần tự hóa các thuộc tính có điều kiện bằng cách tìm kiếm ShouldSerialize phương thức trong lớp.

Để sử dụng tính năng này, hãy thêm một ShouldSerializeBlah()phương thức boolean vào lớp của bạn, nơi Blahđược thay thế bằng tên của thuộc tính mà bạn không muốn tuần tự hóa. Làm cho việc thực hiện phương pháp này luôn quay trở lại false.

class Config
{
    public Fizz ObsoleteSetting { get; set; }

    public Bang ReplacementSetting { get; set; }

    public bool ShouldSerializeObsoleteSetting()
    {
        return false;
    }
}

Lưu ý: nếu bạn thích cách tiếp cận này nhưng bạn không muốn làm xáo trộn giao diện công khai của lớp bằng cách giới thiệu một ShouldSerializephương thức, bạn có thể sử dụng một IContractResolverđể làm điều tương tự theo chương trình. Xem Serialization thuộc tính có điều kiện thuộc trong tài liệu.

Phương pháp 2: Thao tác JSON với JObjects

Thay vì sử dụng JsonConvert.SerializeObjectđể thực hiện tuần tự hóa, hãy tải đối tượng cấu hình vào a JObject, sau đó chỉ cần xóa thuộc tính không mong muốn khỏi JSON trước khi viết ra. Nó chỉ là một vài dòng mã bổ sung.

JObject jo = JObject.FromObject(config);

// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();

json = jo.ToString();

Cách tiếp cận 3: Sử dụng các thuộc tính thông minh (ab)

  1. Áp dụng một [JsonIgnore] thuộc tính cho thuộc tính mà bạn không muốn được tuần tự hóa.
  2. Thêm một thay thế, riêng tư thiết lập thuộc tính vào lớp có cùng kiểu với thuộc tính ban đầu. Làm cho việc thực hiện thuộc tính đó đặt thuộc tính ban đầu.
  3. Áp dụng một [JsonProperty]thuộc tính cho setter thay thế, đặt tên JSON giống như thuộc tính ban đầu.

Đây là Configlớp đã sửa đổi :

class Config
{
    [JsonIgnore]
    public Fizz ObsoleteSetting { get; set; }

    [JsonProperty("ObsoleteSetting")]
    private Fizz ObsoleteSettingAlternateSetter
    {
        // get is intentionally omitted here
        set { ObsoleteSetting = value; }
    }

    public Bang ReplacementSetting { get; set; }
}

7
Chúng tôi đã giải quyết nó trong dự án của mình (sử dụng siêu tập cụ thể tích hợp nội bộ của mô hình cơ sở, nơi không có thuộc tính lớp cha nào được tuần tự hóa), bằng cách đặt thuộc tính get thành nội bộ. Việc có các bộ thiết lập công khai cho phép Web Api thiết lập các thuộc tính, nhưng ngăn nó tuần tự hóa chúng.
Daniel Saidi

7
Cùng với việc sử dụng JsonPropertyAttribute, từ C # 6.0, bạn có thể sử dụng nameoftừ khóa thay vì sử dụng "chuỗi ma thuật". Điều này làm cho việc tái cấu trúc dễ dàng hơn rất nhiều và chống đánh lừa - thêm vào đó, nếu bạn lỡ đổi tên bất kỳ lần xuất hiện nào, dù sao thì trình biên dịch cũng sẽ cảnh báo bạn. Sử dụng ví dụ của @ Brian, cách sử dụng sẽ là:[JsonProperty(nameof(ObsoleteSetting))]
Geoff James

1
Bạn không nên sử dụng nameof () trong khai báo JsonProperty, đặc biệt là trong kịch bản kế thừa này. JSON đại diện cho một hợp đồng bên ngoài (và hy vọng là vĩnh cửu) với một giao diện khác và bạn chắc chắn KHÔNG muốn thay đổi tên của thuộc tính JSON nếu bạn cấu trúc lại. Bạn sẽ phá vỡ khả năng tương thích của tất cả các tệp JSON hiện có và các thành phần tạo JSON ở định dạng này. Trên thực tế, tốt hơn hết bạn nên đặt JsonProperty (…) với tên đầy đủ trên mọi thuộc tính được tuần tự hóa để đảm bảo chúng không thay đổi nếu sau này bạn đổi tên thuộc tính.
Đạn Goettsch

36

Đối với bất kỳ tình huống nào có thể chấp nhận được việc đánh dấu thuộc tính chỉ deserialization của bạn là nội bộ, có một giải pháp cực kỳ đơn giản mà hoàn toàn không phụ thuộc vào thuộc tính. Chỉ cần đánh dấu thuộc tính là nội bộ nhận được, nhưng đặt công khai:

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

Điều này dẫn đến việc deserialization chính xác bằng cách sử dụng cài đặt / trình phân giải / v.v. mặc định, nhưng thuộc tính bị tước khỏi đầu ra tuần tự hóa.


Giải pháp đơn giản nhưng thông minh.
hbulens

Lưu ý rằng thuộc tính cũng sẽ bị bỏ qua bởi mô-đun xác thực. (Vì vậy, bạn không thể đánh dấu nó là [Bắt buộc] cho deserialization nữa, vì điều này phụ thuộc vào một getphương thức công khai ).
Martin Hansen

2
Điều này không hoạt động với hoặc internalcũng không private. Nó luôn được nối tiếp.
Paul

Điều này đã không làm việc cho tôi. Có lỗi không tìm thấy thuộc tính khi giải kích thước.
Drew Sumido

31

Tôi thích gắn bó với các thuộc tính trên cái này, đây là phương pháp tôi sử dụng khi cần giải mã hóa một thuộc tính nhưng không tuần tự hóa nó hoặc ngược lại.

BƯỚC 1 - Tạo thuộc tính tùy chỉnh

public class JsonIgnoreSerializationAttribute : Attribute { }

BƯỚC 2 - Tạo Trình phục hồi hợp đồng tùy chỉnh

class JsonPropertiesResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        //Return properties that do NOT have the JsonIgnoreSerializationAttribute
        return objectType.GetProperties()
                         .Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
                         .ToList<MemberInfo>();
    }
}

BƯỚC 3 - Thêm thuộc tính khi không cần tuần tự hóa nhưng không cần tuần tự hóa

    [JsonIgnoreSerialization]
    public string Prop1 { get; set; } //Will be skipped when serialized

    [JsonIgnoreSerialization]
    public string Prop2 { get; set; } //Also will be skipped when serialized

    public string Prop3 { get; set; } //Will not be skipped when serialized

BƯỚC 4 - Sử dụng nó

var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });

Hi vọng điêu nay co ich! Ngoài ra, điều đáng chú ý là điều này cũng sẽ bỏ qua các thuộc tính khi Deserialization xảy ra, khi tôi đang derserializing, tôi chỉ sử dụng bộ chuyển đổi theo cách thông thường.

JsonConvert.DeserializeObject<MyType>(myString);

Cảm ơn vì sự triển khai hữu ích này. Có cách nào để mở rộng triển khai cơ sở GetSerializableMembersthay vì ghi đè nó hoàn toàn không?
Alain

4
Đừng bận tâm, chỉ cần nhận ra nó đơn giản như sau:return base.GetSerializableMembers(objectType).Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute))).ToList();
Alain

không chắc tại sao đây không phải là câu trả lời được xếp hạng cao nhất. nó sạch sẽ, tuân theo các mẫu của newtonsoft và dễ thực hiện. Điều duy nhất tôi muốn nói thêm là bạn có thể thiết lập nó trên toàn cầu sử dụngJsonConvert.DefaultSettings = () => new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() }
Matt M

1
đừng bận tâm, điều này thực sự không làm những gì người hỏi đang yêu cầu. về cơ bản đây là tạo lại JsonIgnore. nó không bỏ qua thuộc tính để tuần tự hóa nhưng đặt thuộc tính trong quá trình giải mã hóa vì không có cách nào để phương thức GetSerializableMembers biết đó là đọc hay ghi nên bạn đang loại trừ các thuộc tính cho cả hai. giải pháp này không hoạt động.
Matt M

7

Sử dụng thuộc tính setter:

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }

[JsonIgnore]
private string _ignoreOnSerializing;

[JsonIgnore]
public string IgnoreOnSerializing
{
    get { return this._ignoreOnSerializing; }
    set { this._ignoreOnSerializing = value; }
}

Hy vọng điều này giúp đỡ.


1
Cảm ơn. Lưu ý rằng JsonProperty phải có chữ hoa IgnoreOnSerializing, bằng với thuộc tính. Tôi khuyên bạn nên sử dụng nameof(IgnoreOnSerializing)để tránh chuỗi ma thuật, trong trường hợp đổi tên.
Bendik August Nesbø

5

Sau khi tôi đã dành một thời gian khá dài để tìm kiếm cách gắn cờ một thuộc tính lớp là De-Serializable và NOT Serializable, tôi thấy rằng không có thứ gì để làm điều đó cả; vì vậy tôi đã nghĩ ra một giải pháp kết hợp hai thư viện hoặc kỹ thuật tuần tự hóa khác nhau (System.Runtime.Serialization.Json & Newtonsoft.Json) và nó hoạt động với tôi như sau:

  • gắn cờ tất cả các lớp và lớp con của bạn là "DataContract".
  • gắn cờ tất cả các thuộc tính của lớp và các lớp con của bạn là "DataMember".
  • gắn cờ tất cả các thuộc tính của lớp và các lớp con của bạn là "JsonProperty" ngoại trừ những thuộc tính mà bạn muốn chúng không được tuần tự hóa.
  • bây giờ gắn cờ các thuộc tính mà bạn KHÔNG muốn nó được tuần tự hóa thành "JsonIgnore".
  • sau đó Serialize bằng cách sử dụng "Newtonsoft.Json.JsonConvert.SerializeObject" và De-Serialize bằng "System.Runtime.Serialization.Json.DataContractJsonSerializer".

    using System;
    using System.Collections.Generic;
    using Newtonsoft.Json;
    using System.Runtime.Serialization;
    using System.IO;
    using System.Runtime.Serialization.Json;
    using System.Text;
    
    namespace LUM_Win.model
    {
        [DataContract]
        public class User
        {
            public User() { }
            public User(String JSONObject)
            {
                MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
                DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
    
                User user = (User)dataContractJsonSerializer.ReadObject(stream);
                this.ID = user.ID;
                this.Country = user.Country;
                this.FirstName = user.FirstName;
                this.LastName = user.LastName;
                this.Nickname = user.Nickname;
                this.PhoneNumber = user.PhoneNumber;
                this.DisplayPicture = user.DisplayPicture;
                this.IsRegistred = user.IsRegistred;
                this.IsConfirmed = user.IsConfirmed;
                this.VerificationCode = user.VerificationCode;
                this.Meetings = user.Meetings;
            }
    
            [DataMember(Name = "_id")]
            [JsonProperty(PropertyName = "_id")]
            public String ID { get; set; }
    
            [DataMember(Name = "country")]
            [JsonProperty(PropertyName = "country")]
            public String Country { get; set; }
    
            [DataMember(Name = "firstname")]
            [JsonProperty(PropertyName = "firstname")]
            public String FirstName { get; set; }
    
            [DataMember(Name = "lastname")]
            [JsonProperty(PropertyName = "lastname")]
            public String LastName { get; set; }
    
            [DataMember(Name = "nickname")]
            [JsonProperty(PropertyName = "nickname")]
            public String Nickname { get; set; }
    
            [DataMember(Name = "number")]
            [JsonProperty(PropertyName = "number")]
            public String PhoneNumber { get; set; }
    
            [DataMember(Name = "thumbnail")]
            [JsonProperty(PropertyName = "thumbnail")]
            public String DisplayPicture { get; set; }
    
            [DataMember(Name = "registered")]
            [JsonProperty(PropertyName = "registered")]
            public bool IsRegistred { get; set; }
    
            [DataMember(Name = "confirmed")]
            [JsonProperty(PropertyName = "confirmed")]
            public bool IsConfirmed { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "verification_code")]
            public String VerificationCode { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "meeting_ids")]
            public List<Meeting> Meetings { get; set; }
    
            public String toJSONString()
            {
                return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
            }
        }
    }

Hy vọng rằng sẽ giúp ...


1
Bravo Ahmed Abulazm. cảm ơn nó đã giúp tôi khỏi rất nhiều công việc. :)
Sike 12

2

có tham chiếu đến giải pháp của @ ThoHo, sử dụng setter thực sự là tất cả những gì cần thiết, không có thẻ bổ sung.

Đối với tôi, trước đây tôi có một Id tham chiếu duy nhất, tôi muốn tải và thêm vào bộ sưu tập Id tham chiếu mới. Bằng cách thay đổi định nghĩa của Id tham chiếu để chỉ chứa một phương thức setter, phương thức này đã thêm giá trị vào bộ sưu tập mới. Json không thể ghi lại giá trị nếu Thuộc tính không có giá trị nhận; phương pháp.

// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }

// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }

Lớp này hiện tương thích ngược với phiên bản trước và chỉ lưu các RefIds cho các phiên bản mới.


1

Để xây dựng dựa trên câu trả lời của Tho Ho, điều này cũng có thể được sử dụng cho các lĩnh vực.

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }

[JsonIgnore]
public string IgnoreOnSerializing;

1

Tùy thuộc vào nơi trong ứng dụng này diễn ra và nếu nó chỉ là một thuộc tính, một tay cách bạn có thể làm điều này là bằng cách thiết lập giá trị tài sản để null và sau đó trên mô hình, bạn có thể xác định rằng tài sản được bỏ qua nếu giá trị là null:

[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }

Nếu bạn đang làm việc trên một ứng dụng web ASP.NET Core, bạn có thể đặt điều này trên toàn cầu cho tất cả các thuộc tính trong tất cả các mô hình bằng cách đặt điều này trong tệp Startup.cs của bạn:

public void ConfigureServices(IServiceCollection services) {
    // other configuration here
    services.AddMvc()
        .AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}

0

Nếu bạn sử dụng JsonConvert, thì lờ đi là ok. Thư viện tiêu chuẩn của tôi không phải là refrence Newton.Json, và tôi sử dụng [ignoreDataMember] để kiểm soát tuần tự hóa đối tượng.

Từ tài liệu trợ giúp Newton.net .


0

Có cách nào đơn giản để đánh dấu (ví dụ: với các thuộc tính) thuộc tính của một đối tượng C #, để Json.net CHỈ bỏ qua nó khi tuần tự hóa, nhưng chú ý đến nó khi giải mã không?

Cách dễ nhất mà tôi tìm thấy khi viết bài này là đưa logic này vào IContractResolver của bạn .

Mã mẫu từ liên kết trên được sao chép vào đây cho hậu thế:

public class Employee
{
    public string Name { get; set; }
    public Employee Manager { get; set; }

    public bool ShouldSerializeManager()
    {
        // don't serialize the Manager property if an employee is their own manager
        return (Manager != this);
    }
}

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
        {
            property.ShouldSerialize =
                instance =>
                {
                    Employee e = (Employee)instance;
                    return e.Manager != e;
                };
        }

        return property;
    }
}

Tất cả các câu trả lời đều tốt nhưng cách tiếp cận này có vẻ là cách sạch sẽ nhất. Tôi thực sự đã triển khai điều này bằng cách tìm kiếm một thuộc tính trên thuộc tính cho SkipSerialize và SkipDeserialize để bạn có thể đánh dấu bất kỳ lớp nào bạn kiểm soát. Câu hỏi tuyệt vời!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.