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

时间:2012-07-19 15:25:29

标签: c# json.net

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

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

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

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

12 个答案:

答案 0 :(得分:96)

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

例如,让我们假设您的类目前定义如下:

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以编程方式执行相同的操作。请参阅文档中的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)使用属性

  1. [JsonIgnore]属性应用于您不希望序列化的属性。
  2. 将替代的私有属性设置器添加到与原始属性具有相同类型的类中。使该属性的实现设置为原始属性。
  3. [JsonProperty]属性应用于备用setter,为其提供与原始属性相同的JSON名称。
  4. 以下是修订后的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; }
    }
    

答案 1 :(得分:22)

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

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

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

答案 2 :(得分:14)

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

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

这会导致使用默认设置/解析器/等正确反序列化,但该属性将从序列化输出中删除。

答案 3 :(得分:7)

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

希望得到这个帮助。

答案 4 :(得分:5)

在我花了很长时间搜索如何将类属性标记为De-Serializable而不是Serializable之后,我发现根本就没有这样做的事情;所以我提出了一个结合了两种不同库或序列化技术的解决方案(System.Runtime.Serialization.Json&amp; 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 });
            }
        }
    }
    

希望有帮助...

答案 5 :(得分:1)

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

答案 6 :(得分:0)

在Tho Ho的答案的基础上,这也可以用于字段。

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

[JsonIgnore]
public string IgnoreOnSerializing;

答案 7 :(得分:0)

如果您使用JsonConvert,IgnoreDataMemberAttribute没问题。我的标准库不引用Newton.Json,我使用[IgnoreDataMember]控制对象序列化。

来自Newton.net帮助文档。

答案 8 :(得分:0)

这取决于应用程序中发生的位置,如果它只是一个属性,则可以通过将属性值设置为null然后在模型上指定一个 manual 来实现此目的。如果该值为null,则忽略该属性:

eb.querySelector('h2').textContent.trim()

如果您使用的是ASP.NET Core Web应用程序,则可以通过在Startup.cs文件中进行设置为所有模型中的所有属性进行全局设置:

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

答案 9 :(得分:0)

  

是否有任何简单的方法来标记(例如,带有属性)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的属性上查找属性来实现的,因此您可以标记任何您控制的类。好问题!

答案 10 :(得分:0)

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;
    }
}

答案 11 :(得分:0)

在模型类的 public 属性中使用 select t.*, ( sum(value) over () + sum(a + b) over (order by to_date(month, 'Mon')) - (a + b) ) as imputed_value from t; 属性。