Создание свойства десериализовать, но не сериализовать с помощью json.net


У нас есть некоторые файлы конфигурации, которые были созданы путем сериализации объектов C# с помощью Json.net.

мы хотели бы перенести одно свойство сериализованного класса из простого свойства перечисления в свойство класса.

одним из простых способов сделать это, было бы оставить старое свойство перечисления в классе и организовать Json.net чтобы прочитать это свойство при загрузке конфигурации, но не сохранить его снова при следующей сериализации объекта. Мы разберемся с этим создание нового класса из старого перечисления отдельно.

есть ли простой способ отметить (например, с помощью атрибутов) свойство объекта C#, так что Json.net будет игнорировать его только при сериализации, но следить за ним при десериализации?

7 74

7 ответов:

на самом деле есть несколько довольно простых подходов, которые вы можете использовать для достижения желаемого результата.

предположим, например, что ваши классы в настоящее время определены следующим образом:

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: Управление JSON с помощью JObjects

вместо 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. добавить альтернативное, частная свойство setter к классу с тем же типом, что и исходное свойство. Сделайте реализацию этого свойства установить исходное свойство.
  3. применить [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; }
}

мне нравится придерживаться атрибутов на этом, вот метод, который я использую при необходимости десериализовать свойство, но не сериализовать его или наоборот.

Шаг 1-Создайте пользовательский атрибут

public class JsonIgnoreSerializationAttribute : Attribute { }

Шаг 2-создание пользовательского контракта 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() });

надеюсь, что это помогает! Также стоит отметить, что это также будет игнорировать свойства, когда происходит десериализация, когда я derserializing я просто использую конвертер обычным способом.

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.Во время выполнения.Сериализация.Json & Newtonsoft.Json) и это сработало для меня следующим образом:

  • отметьте все ваши классы и подклассы как "DataContract".
  • отметьте все свойства вашего класс и подклассы как "DataMember".
  • отметьте все свойства вашего класса и подклассов как "JsonProperty", за исключением тех, которые вы хотите, чтобы они не были сериализованы.
  • теперь отметьте свойства, которые вы не хотите, чтобы он был сериализован как "JsonIgnore".
  • затем сериализовать с помощью "Newtonsoft.формат JSON.JsonConvert.SerializeObject " и Де-сериализации с помощью "Система.Во время выполнения.Сериализация.формат 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 });
            }
        }
    }
    

надеюсь, что это поможет ...

для любой ситуации, когда допустимо, чтобы ваше свойство десериализации было помечено как внутреннее, есть удивительно простое решение, которое вообще не зависит от атрибутов. Просто отметьте свойство как внутреннее get, но public set:

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

это приводит к правильной десериализации с использованием настроек по умолчанию / resolvers / etc., но свойство удаляется из сериализованного вывода.

со ссылкой на решение @ThoHo, используя сеттер на самом деле все, что нужно, без дополнительных тегов.

для меня ранее у меня был один идентификатор ссылки, который я хотел загрузить и добавить в новую коллекцию идентификаторов ссылок. Изменив определение идентификатора ссылки, чтобы он содержал только метод setter, который добавил значение в новую коллекцию. Json не может записать значение обратно, если свойство не имеет get; метод.

// 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 для новых версий.

чтобы построить ответ Tho Ho, это также может быть использовано для полей.

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

[JsonIgnore]
public string IgnoreOnSerializing;