[英]Making a property deserialize but not serialize with json.net
我們有一些配置文件是通過使用 Json.net 序列化 C# 對象生成的。
我們希望將序列化類的一個屬性從簡單的枚舉屬性遷移到類屬性中。
一種簡單的方法是在類中保留舊的枚舉屬性,並安排 Json.net 在加載配置時讀取此屬性,但在下一次序列化對象時不再保存它。 我們將分別處理從舊枚舉生成新類。
是否有任何簡單的方法來標記(例如使用屬性)C# 對象的屬性,以便 Json.net 僅在序列化時忽略它,但在反序列化時關注它?
實際上,您可以使用幾種相當簡單的方法來實現您想要的結果。
例如,假設您的類當前定義如下:
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
}
enum Fizz { Alpha, Beta, Gamma }
class Bang
{
public string Value { get; set; }
}
你想這樣做:
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);
要得到這個:
{"ReplacementSetting":{"Value":"Gamma"}}
Json.NET 能夠通過在類中查找對應的ShouldSerialize
方法來有條件地序列化屬性。
要使用此功能,請在您的類中添加一個布爾ShouldSerializeBlah()
方法,其中Blah
將替換為您不想序列化的屬性的名稱。 使此方法的實現始終返回false
。
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
public bool ShouldSerializeObsoleteSetting()
{
return false;
}
}
注意:如果您喜歡這種方法,但又不想通過引入ShouldSerialize
方法來混淆類的公共接口,則可以使用IContractResolver
以編程方式執行相同的操作。 請參閱文檔中的條件屬性序列化。
與其使用JsonConvert.SerializeObject
進行序列化,不如將配置對象加載到JObject
中,然后在寫出之前從 JSON 中刪除不需要的屬性。 這只是幾行額外的代碼。
JObject jo = JObject.FromObject(config);
// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();
json = jo.ToString();
[JsonIgnore]
屬性應用於您不想被序列化的屬性。[JsonProperty]
屬性應用到備用設置器,為其提供與原始屬性相同的 JSON 名稱。 這是修改后的Config
類:
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; }
}
對於任何可以將僅反序列化的屬性標記為內部的情況,有一個非常簡單的解決方案,它根本不依賴於屬性。 只需將屬性標記為內部獲取,但公共設置:
public class JsonTest {
public string SomeProperty { internal get; set; }
}
這會導致使用默認設置/解析器/等進行正確的反序列化,但該屬性會從序列化輸出中剝離。
我喜歡在這個屬性上堅持,這是我需要反序列化屬性但不序列化它時使用的方法,反之亦然。
第 1 步 - 創建自定義屬性
public class JsonIgnoreSerializationAttribute : Attribute { }
第 2 步 - 創建一個自定義的 Contract Reslover
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>();
}
}
第 3 步 - 添加不需要序列化但需要反序列化的屬性
[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
第 4 步 - 使用它
var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });
希望這可以幫助! 另外值得注意的是,當反序列化發生時,這也會忽略屬性,當我反序列化時,我只是以傳統方式使用轉換器。
JsonConvert.DeserializeObject<MyType>(myString);
使用 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; }
}
希望這有幫助。
在我花了很長時間搜索如何將類屬性標記為可反序列化和不可序列化之后,我發現根本沒有這樣的事情可以做到; 所以我想出了一個結合兩個不同庫或序列化技術(System.Runtime.Serialization.Json & Newtonsoft.Json)的解決方案,它對我有用,如下所示:
然后使用“Newtonsoft.Json.JsonConvert.SerializeObject”進行序列化,並使用“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 }); } } }
希望有幫助...
根據應用程序中發生這種情況的位置以及如果它只是一個屬性,您可以手動執行此操作的一種方法是將屬性值設置為 null,然后在模型上您可以指定如果值為 null,則忽略該屬性:
[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }
如果您正在使用 ASP.NET Core Web 應用程序,則可以通過在 Startup.cs 文件中設置它來為所有模型中的所有屬性全局設置它:
public void ConfigureServices(IServiceCollection services) {
// other configuration here
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}
參考@ThoHo 的解決方案,實際上只需要使用setter,不需要額外的標簽。
對我來說,我以前有一個引用 ID,我想加載並添加到新的引用 ID 集合中。 通過將引用 Id 的定義更改為僅包含一個 setter 方法,該方法將值添加到新集合中。 如果 Property 沒有get,則 Json 無法將值寫回; 方法。
// 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; }
這個類現在向后兼容以前的版本,並且只保存新版本的RefId 。
為了建立在 Tho Ho 的答案之上,這也可以用於字段。
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }
[JsonIgnore]
public string IgnoreOnSerializing;
如果你使用 JsonConvert, IgnoreDataMemberAttribute是可以的。我的標准庫不引用 Newton.Json,我使用 [IgnoreDataMember] 來控制對象序列化。
來自Newton.net幫助文檔。
是否有任何簡單的方法來標記(例如使用屬性)C# 對象的屬性,以便 Json.net 僅在序列化時忽略它,但在反序列化時關注它?
在撰寫本文時,我發現的最簡單的方法是將此邏輯包含在您的IContractResolver中。
上述鏈接中的示例代碼復制到此處以供后代使用:
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;
}
}
所有的答案都很好,但這種方法似乎是最干凈的方法。 實際上,我通過在屬性上查找 SkipSerialize 和 SkipDeserialize 的屬性來實現這一點,這樣您就可以標記您控制的任何類。 好問題!
Jraco11 的回答非常簡潔。 如果您想對序列化和反序列化使用相同的 IContractResolver,則可以使用以下命令:
public class JsonPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.IsDefined(typeof(JsonIgnoreSerializationAttribute)))
{
property.ShouldSerialize = instance => false;
}
return property;
}
}
這就是訣竅,只用 set 創建一個屬性
示例 1: https ://dotnetfiddle.net/IxMXcG
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => IsPartialResult = (value != null && value.HasValues); }
public bool IsPartialResult { get; private set; }
示例 2:
private JArray _disabledProtections;
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => _disabledProtections = value; }
public bool IsPartialResult => _disabledProtections != null && _disabledProtections.HasValues;
在模型類的公共屬性中使用[JsonIgnore]
屬性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.