我们有一些配置文件,这些文件是使用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; }
}
nameof
关键字来配合使用JsonPropertyAttribute
。这使得重构变得更加容易和可靠 - 如果您错过了任何重命名的情况,编译器也会提出警告。以@Brian的示例为例,使用方式如下:[JsonProperty(nameof(ObsoleteSetting))]
。 - Geoff James[JsonProperty(nameof(ObsoleteSetting))]
基本上是一个无操作。使用 JsonProperty
的主要原因是确保 JSON 属性名称固定,而 C# 属性名称可以更改。这使您能够进行重构而不会破坏 API 合同。 - Florian Winter对于任何情况,如果将只进行反序列化的属性标记为internal是可以接受的,那么有一个非常简单的解决方案,完全不依赖于属性。只需将该属性标记为internal get,但public set:
public class JsonTest {
public string SomeProperty { internal get; set; }
}
这将导致使用默认设置/解析器等进行正确反序列化,但该属性将从序列化输出中剥离。
internal
和private
中都不起作用,它总是被序列化。 - Paul我喜欢在这里坚持使用属性,以下是我在需要反序列化属性但不需要序列化它或反之时使用的方法。
步骤1 - 创建自定义属性
public class JsonIgnoreSerializationAttribute : Attribute { }
步骤 2 - 创建自定义合约解析器
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>();
}
}
步骤三 - 在不需要序列化但需要反序列化的情况下添加属性
[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
步骤四 - 使用它
var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });
希望这可以帮助到您!另外值得注意的是,当反序列化发生时,它也将忽略属性,在我进行反序列化时,我只是按照通常的方式使用转换器。
JsonConvert.DeserializeObject<MyType>(myString);
GetSerializableMembers
的基本实现,而不是完全覆盖它? - Alainreturn base.GetSerializableMembers(objectType).Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute))).ToList();
- AlainJsonConvert.DefaultSettings = () => new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() }
进行全局设置。 - Matt M使用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; }
}
希望这能帮到您。
IgnoreOnSerializing
,等于属性。我建议使用nameof(IgnoreOnSerializing)
来避免魔术字符串,在重命名时更加方便。谢谢。 - Bendik August Nesbø在我花费相当长的时间搜索如何将类属性标记为可反序列化但不可序列化后,我发现根本没有这样的事情。所以我想到了一个解决方案,结合两种不同的库或序列化技术 (System.Runtime.Serialization.Json & Newtonsoft.Json),并且我的解决方案如下:
then Serialize using "Newtonsoft.Json.JsonConvert.SerializeObject" and De-Serialize using "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方法,该方法将值添加到新的集合中。如果属性没有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; }
现在,这个类已经向后兼容到之前的版本,并且只保存新版本的RefIds。
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }
[JsonIgnore]
public string IgnoreOnSerializing;
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;
}
}
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;
}
}
[JsonIgnore]
属性不应该可以实现吗??http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size.aspx - Juri