Сериализовать свойство, но не десериализовать свойство в Json.Net

Хотя я нашел множество подходов к десериализации определенных свойств, не допуская их сериализации, я ищу противоположное поведение.

Я нашел много вопросов, спрашивающих обратный:

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

Могу ли я дать Json.NET возможность десериализации, но не сериализации, конкретных свойств?

JSON.Net - используйте JsonIgnoreAttribute только для сериализации (но не при десериализации)

Как я могу сериализовать определенное свойство, но предотвратить его десериализацию обратно в POCO? Есть ли атрибут, который я могу использовать для украшения конкретного свойства?

В основном я ищу эквивалент методам ShouldSerialize * для десериализации.

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

Edit:

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

public class Address : IAddress
{
    /// <summary>
    /// Gets or sets the two character country code
    /// </summary>
    [JsonProperty("countryCode")]
    [Required]
    public string CountryCode { get; set; }

    /// <summary>
    /// Gets or sets the country code, and province or state code delimited by a vertical pipe: <c>US|MI</c>
    /// </summary>
    [JsonProperty("countryProvinceState")]
    public string CountryProvinceState
    {
        get
        {
            return string.Format("{0}|{1}", this.CountryCode, this.ProvinceState);
        }
        set
        {
            if (!string.IsNullOrWhiteSpace(value) && value.Contains("|"))
            {
                string[] valueParts = value.Split('|');
                if (valueParts.Length == 2)
                {
                    this.CountryCode = valueParts[0];
                    this.ProvinceState = valueParts[1];
                }
            }
        }
    }

    [JsonProperty("provinceState")]
    [Required]
    public string ProvinceState { get; set; }
}

Мне нужно свойство CountryProvinceState для запроса, но я не хочу, чтобы он десериализовался и запускал логику setter.

Ответ 1

Простейшим методом было бы пометить реальное свойство как [JsonIgnore] и создать свойство прокси-сервера get-only:

    /// <summary>
    /// Gets or sets the country code, and province or state code delimited by a vertical pipe: <c>US|MI</c>
    /// </summary>
    [JsonIgnore]
    public string CountryProvinceState
    {
        get
        {
            return string.Format("{0}|{1}", this.CountryCode, this.ProvinceState);
        }
        set
        {
            if (!string.IsNullOrWhiteSpace(value) && value.Contains("|"))
            {
                string[] valueParts = value.Split('|');
                if (valueParts.Length == 2)
                {
                    this.CountryCode = valueParts[0];
                    this.ProvinceState = valueParts[1];
                }
            }
        }
    }

    [JsonProperty("countryProvinceState")]
    string ReadCountryProvinceState
    {
        get { return CountryProvinceState; } 
    }

Свойство прокси-сервера может быть приватным, если вы хотите.

Обновление

Если вам нужно сделать это для множества свойств во многих классах, может быть проще создать собственный ContractResolver, который проверяет собственный атрибут. Если он найден, атрибут будет сигнализировать, что свойство имеет только get-only:

[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]
public class GetOnlyJsonPropertyAttribute : Attribute
{
}

public class GetOnlyContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (property != null && property.Writable)
        {
            var attributes = property.AttributeProvider.GetAttributes(typeof(GetOnlyJsonPropertyAttribute), true);
            if (attributes != null && attributes.Count > 0)
                property.Writable = false;
        }
        return property;
    }
}

Затем используйте его как:

[JsonProperty("countryProvinceState")]
[GetOnlyJsonProperty]
public string CountryProvinceState { get; set; }

И затем:

        var settings = new JsonSerializerSettings { ContractResolver = new GetOnlyContractResolver() };

        var address = JsonConvert.DeserializeObject<Address>(jsonString, settings);

Ответ 2

В вашем вопросе у вас есть простое строковое свойство. Но это немного сложнее, когда у вас есть объект. Решение с .Writeable = false не будет работать, так как десериализация перейдет к свойствам объекта. Рассмотрим следующий код:

public class Constants
{
    public Address Headquarters { get; set; }

    public static Constants Instance = new Constants
    {
        Headquarters = new Address { Street = "Baker Street" }
    };
}
public class Address
{
    public string Street { get; set; }
}

public class Data
{
    [GetOnlyJsonProperty]
    // we want this to be included in the response, but not deserialized back
    public Address HqAddress { get { return Constants.Instance.Headquarters; } }
}

// somewhere in your code:
var data = JsonConvert.DeserializeObject<Data>(jsonString, settings);

Теперь JSON будет еще не пытаться создать новый Addreess объект для HqAddress собственности, так как он имеет только геттер. Но затем (хотя .Writeable == false) он углубляется и десериализует свойство Street, устанавливая его в объекте Constants.Instance.Heqdquarters, перезаписывая данные в константах вашего приложения.

Решение: в новой версии Newtonsoft.JSON (я пробовал в v10), есть новое свойство ShouldDeserialize. Таким образом, решатель должен быть:

public class GetOnlyContractResolver : DefaultContractResolver
    {
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            var property = base.CreateProperty(member, memberSerialization);
            if (property != null) // Change here (1)
            {
                var attributes = property.AttributeProvider.GetAttributes(typeof(GetOnlyJsonPropertyAttribute), true);
                if (attributes != null && attributes.Count > 0)
                    property.ShouldDeserialize = (a) => false;  // Change here (2)
            }
            return property;
        }
    }

(1) Я удалил условие для && property.Writeable, поэтому он обрабатывает HqAddress и пропускает десериализацию для полного дерева. (2) ShouldDeserialize - это предикат, ShouldDeserialize вызывается для десериализации каждого объекта. Таким образом, вы можете условно пропустить только некоторые свойства. Но здесь я сделал это просто, например.