使属性反序列化但不使用 json.net 序列化

Posted

技术标签:

【中文标题】使属性反序列化但不使用 json.net 序列化【英文标题】:Making a property deserialize but not serialize with json.net 【发布时间】:2012-07-18 19:54:32 【问题描述】:

我们有一些配置文件是通过使用 Json.net 序列化 C# 对象生成的。

我们希望将序列化类的一个属性从简单的枚举属性迁移到类属性中。

一种简单的方法是,将旧的枚举属性保留在类上,并安排 Json.net 在加载配置时读取此属性,但在下一次序列化对象时不再保存它.我们将分别处理从旧枚举生成新类。

是否有任何简单的方法来标记(例如使用属性)C# 对象的属性,以便 Json.net 仅在序列化时忽略它,但在反序列化时关注它?

【问题讨论】:

自定义转换器怎么样:您可以将其用作属性的属性,用不同的行为覆盖 ReadJson 和 WriteJson,不是吗?示例(不完全是您需要的,但是...)weblogs.asp.net/thangchung/archive/2010/08/26/… OnDeserialized attribute 可以为您解决问题 不应该使用 `[JsonIgnore]' 属性吗? james.newtonking.com/archive/2009/10/23/… 你是否可以根据 q 的最后一段来扩展如何仅在一个方向上使用它? 可以将 [JsonIgnore] 与用 [JsonProperty] 属性修饰的辅助私有设置器结合使用。还有其他一些简单的解决方案。我已经添加了详细的文章。 【参考方案1】:

在模型类的public属性中使用[JsonIgnore]属性。

【讨论】:

【参考方案2】:

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;
    

【讨论】:

【参考方案3】:

是否有任何简单的方法来标记(例如使用属性)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 的属性来实现这一点,这样您就可以标记您控制的任何类。好问题!

【讨论】:

【参考方案4】:

根据应用程序中发生这种情况的位置,如果它只是一个属性,一种手动方法是您可以通过将属性值设置为 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);

【讨论】:

【参考方案5】:

如果你使用 JsonConvert,IgnoreDataMemberAttribute 是可以的。我的标准库不引用 Newton.Json,我使用 [IgnoreDataMember] 来控制对象序列化。

来自Newton.net帮助文档。

【讨论】:

【参考方案6】:

对于任何可以将您的仅反序列化属性标记为内部的情况,有一个非常简单的解决方案,它根本不依赖于属性。只需将属性标记为内部获取,但公共设置:

public class JsonTest 

    public string SomeProperty  internal get; set; 


这会导致使用默认设置/解析器/等进行正确的反序列化,但该属性会从序列化输出中剥离。

【讨论】:

简单而巧妙的解决方案。 请注意,验证模块也会忽略该属性。 (因此您不能再将其标记为 [Required] 以进行反序列化,因为这依赖于公共 get 方法)。 这不适用于internalprivate。它总是被序列化的。 这对我不起作用。反序列化时出现属性未找到错误。 我不禁想知道最后两个评论者是否将内部访问修饰符放在 setter 而不是 getter 上。这与使用 Json.NET 时为我宣传的完全一样。也许 System.Text.Json 的工作方式有所不同。【参考方案7】:

以 Tho Ho 的回答为基础,这也可用于字段。

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

[JsonIgnore]
public string IgnoreOnSerializing;

【讨论】:

【参考方案8】:

使用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; 

希望对您有所帮助。

【讨论】:

谢谢。请注意,JsonProperty 应该有一个大写的IgnoreOnSerializing,等于该属性。我建议使用nameof(IgnoreOnSerializing) 来避免使用魔法字符串,以防重命名。【参考方案9】:

参考@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; 

这个类现在向后兼容以前的版本,并且只保存新版本的 RefIds

【讨论】:

【参考方案10】:

我喜欢在这个属性上坚持,这是我需要反序列化属性但不序列化它时使用的方法,反之亦然。

第 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);

【讨论】:

感谢这个有用的实现。有没有办法扩展GetSerializableMembers 的基本实现而不是完全覆盖它? 没关系,刚刚意识到它很简单:return base.GetSerializableMembers(objectType).Where(pi =&gt; !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute))).ToList(); 不知道为什么这不是最受好评的答案。它很干净,遵循 newtonsoft 的模式并且很容易做到。我唯一要补充的是,您可以使用 JsonConvert.DefaultSettings = () =&gt; new JsonSerializerSettings ContractResolver = new JsonPropertiesResolver() 全局设置它 没关系,这实际上并不能满足提问者的要求。这基本上是在重新创建 JsonIgnore。它不会跳过序列化的属性,而是在反序列化期间设置属性,因为 GetSerializableMembers 方法无法知道它是读取还是写入,因此您排除了两者的属性。此解决方案不起作用。 这正是我搜索时想到的解决方案。【参考方案11】:

实际上,您可以使用几种相当简单的方法来实现您想要的结果。

假设,例如,您的类当前定义如下:

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"

方法一:添加 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 以编程方式执行相同的操作。请参阅文档中的Conditional Property Serialization。

方法 2:使用 JObjects 操作 JSON

不要使用JsonConvert.SerializeObject 进行序列化,而是将配置对象加载到JObject 中,然后在写出之前从JSON 中删除不需要的属性。这只是几行额外的代码。

JObject jo = JObject.FromObject(config);

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

json = jo.ToString();

方法 3:巧妙(ab)使用属性

    [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-properties 设置为 internal。拥有公共设置器允许 Web Api 设置属性,但阻止它序列化它们。 结合使用 JsonPropertyAttribute,从 C# 6.0 开始,您可以使用 nameof 关键字而不是使用“魔术字符串”。这使得重构 lot 变得更加容易和万无一失——另外,如果你确实错过了重命名任何出现的地方,编译器无论如何都会警告你。以@Brian 为例,用法如下:[JsonProperty(nameof(ObsoleteSetting))] 在 JsonProperty 声明中使用 nameof() 是个坏主意,尤其是在这种遗留场景中。 JSON 代表与另一个接口的外部(并且希望是永恒的)合同,如果您重构,您肯定不想更改 JSON 属性的名称。您将破坏所有现有 JSON 文件和以这种格式生成 JSON 的组件的兼容性。实际上,最好将 JsonProperty(...) 与每个序列化属性放在一起,以确保以后重命名属性时它们不会更改。 [JsonProperty(nameof(ObsoleteSetting))] 在这种情况下基本上是无操作的。使用JsonProperty 的主要原因是确保JSON 属性名称是固定的,而C# 属性名称可以更改。这允许您在不破坏 API 合同的情况下进行重构。【参考方案12】:

在我花了很长时间搜索如何将类属性标记为可反序列化和不可序列化之后,我发现根本没有这样的事情可以做到;所以我想出了一个解决方案,它结合了两种不同的库或序列化技术(System.Runtime.Serialization.Json 和 Newtonsoft.Json),它对我有用,如下所示:

将所有类和子类标记为“DataContract”。 将您的类和子类的所有属性标记为“DataMember”。 将您的类和子类的所有属性标记为“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 );
        
    

希望对您有所帮助...

【讨论】:

布拉沃·艾哈迈德·阿布拉兹姆。谢谢它确实使我免于大量工作。 :) 我不会积极反对使用两个序列化框架。

以上是关于使属性反序列化但不使用 json.net 序列化的主要内容,如果未能解决你的问题,请参考以下文章

使用 Json.NET 转换器反序列化属性

使用带有 ItemRequired = Required.Always 的 Json.Net 反序列化时忽略属性

如何使用 Json.Net 序列化/反序列化具有附加属性的自定义集合

Json.NET - CustomCreationConverter 中单个属性的默认反序列化行为

是否可以确定在 Json.Net 中反序列化了哪些属性? [复制]

使用 json.net 反序列化没有类型信息的多态 json 类