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

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

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

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

Есть ли какой-либо простой способ отметить (например, с атрибутами) свойство объекта С#, чтобы Json.net игнорировал его ТОЛЬКО при сериализации, но рассматривал его при десериализации?

Ответ 1

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

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

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: Добавить метод Shouldererizeize

Json.NET имеет возможность условно сериализовать свойства, ища соответствующие методы ShouldSerialize в классе.

Чтобы использовать эту функцию, добавьте в класс класс boolean 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) использование атрибутов

  • Примените атрибут [JsonIgnore] к свойству, которое вы не хотите сериализовать.
  • Добавить альтернативный, private property setter в класс с тем же типом, что и исходное свойство. Внесите в реализацию этого свойства исходное свойство.
  • Примените атрибут [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; }
}

Ответ 2

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

ШАГ 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() });

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

JsonConvert.DeserializeObject<MyType>(myString);

Ответ 3

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

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

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

Ответ 4

Использовать свойство 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; }
}

Надеюсь на эту помощь.

Ответ 5

После того, как я потратил довольно много времени на поиск того, как помечать свойство класса De-Serializable и NOT Serializable, я обнаружил, что вообще этого не делать; поэтому я придумал решение, которое объединяет две разные библиотеки или методы сериализации (System.Runtime.Serialization.Json и Newtonsoft.Json), и это сработало для меня следующим образом:

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

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

Ответ 6

со ссылкой на решение @ThoHo, использование setter - это фактически все, что необходимо, без дополнительных тегов.

Для меня у меня ранее был единственный идентификатор ссылки, который я хотел загрузить и добавить в новую коллекцию ссылочных идентификаторов. Изменив определение ссылочного идентификатора, будет только содержать метод 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 для новых версий.

Ответ 7

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

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

[JsonIgnore]
public string IgnoreOnSerializing;

Ответ 8

Если вы используете JsonConvert, IgnoreDataMemberAttribute в порядке. Моя стандартная библиотека не является ссылкой Newton.Json, и я использую [IgnoreDataMember] для управления сериализацией объекта.

Из справочного документа Newton.net.

Ответ 9

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

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

Если вы работаете с веб-приложением ASP.NET Core, вы можете глобально установить его для всех свойств во всех моделях, установив его в файле Startup.cs:

public void ConfigureServices(IServiceCollection services) {
    // other configuration here
    services.AddMvc()
        .AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}

Ответ 10

Есть ли простой способ пометить (например, с помощью атрибутов) свойство объекта С#, чтобы 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, чтобы вы могли просто разметить любой класс под вашим контролем. Отличный вопрос!