我们有一些配置文件,这些文件是通过使用 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"}}
方法 1:添加 ShouldSerialize 方法
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
以编程方式执行相同的操作。 请参阅文档中的条件属性序列化。
方法 2:使用 JObjects 操作 JSON
不要使用 JsonConvert.SerializeObject
进行序列化,而是将 config 对象加载到JObject
中,然后只需在写出之前从 JSON 中删除不需要的属性。 这只是几行额外的代码。
JObject jo = JObject.FromObject(config);
// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();
json = jo.ToString();
方法3:巧妙(滥用(使用属性
- 将
[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; }
}
对于将仅反序列化属性标记为内部的任何可以接受的情况,有一个非常简单的解决方案,它根本不依赖于属性。只需将属性标记为内部 get,但公共设置:
public class JsonTest {
public string SomeProperty { internal get; set; }
}
这会导致使用默认设置/解析程序/等进行正确的反序列化,但该属性将从序列化输出中删除。
坚持使用属性,这是我在需要反序列化属性但不序列化它时使用的方法,反之亦然。
步骤 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
第 4 步 - 使用它
var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });
希望这有帮助!另外值得注意的是,当反序列化发生时,这也将忽略属性,当我进行反序列化时,我只是以传统方式使用转换器。
JsonConvert.DeserializeObject<MyType>(myString);
使用二传手属性:
[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(的解决方案,它对我的工作方式如下:
- 将所有类和子类标记为"数据协定"。
- 将类和子类的所有属性标记为"数据成员"。
- 将类和子类的所有属性标记为"JsonProperty",但您希望它们不序列化的属性除外。
- 现在标记您不希望将其序列化为"JsonIgnore"的属性。
-
然后使用"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);
}
<</div>
div class="one_answers"> 参考 @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; }
此类现在向后兼容以前的版本,并且仅保存新版本的 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 仅在序列化时忽略它,但在反序列化时关注它?
在撰写本文时,我发现的最简单方法是将此逻辑包含在您的 IContractResolve 程序中。
上面链接中的示例代码复制在这里供后人使用:
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]
属性。