Сериализовать свойство, но не десериализовать свойство в 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
вызывается для десериализации каждого объекта. Таким образом, вы можете условно пропустить только некоторые свойства. Но здесь я сделал это просто, например.