사용자 지정 속성 (json.net)을 통해 직렬화에서 속성 제외
클래스의 특정 속성이 직렬화되는 방식 / 여부를 제어 할 수 있어야합니다. 가장 간단한 경우는 [ScriptIgnore]
. 그러나 저는이 속성이 제가 작업중인 특정 직렬화 상황에 대해서만 적용되기를 원합니다. 애플리케이션의 다른 모듈이 이러한 객체를 직렬화하려는 경우 이러한 속성이 방해가되지 않아야합니다.
그래서 내 생각은 속성 MyAttribute
에 사용자 정의 속성을 사용하고 해당 속성을 찾는 것을 알고있는 후크로 JsonSerializer의 특정 인스턴스를 초기화하는 것입니다.
언뜻보기에는 JSON.NET에서 사용 가능한 후크 포인트가 PropertyInfo
현재 속성에 대해 이러한 검사를 수행하는 데 제공되지 않으며 속성의 값만 제공됩니다. 내가 뭔가를 놓치고 있습니까? 아니면 이것에 접근하는 더 나은 방법?
몇 가지 옵션이 있습니다. 아래를 읽기 전에 주제에 대한 Json.Net 문서 기사 를 읽어 보는 것이 좋습니다 .
이 기사는 두 가지 방법을 제시합니다.
bool
Json.Net이 속성을 직렬화할지 여부를 결정하기 위해 따르는 명명 규칙에 따라 값 을 반환하는 메서드를 만듭니다 .- 속성을 무시하는 사용자 지정 계약 확인자를 만듭니다.
두 가지 중에서 후자를 선호합니다. 속성을 모두 건너 뛰고 모든 형식의 직렬화에서 속성을 무시할 때만 사용합니다. 대신 해당 속성을 무시하는 사용자 지정 계약 해결 프로그램을 만들고 속성을 무시하고 싶을 때만 계약 해결 프로그램을 사용하여 클래스의 다른 사용자가 속성을 직렬화 할 수 있는지 여부를 자유롭게 설정하십시오.
편집 링크 부패를 방지하기 위해 기사에서 문제의 코드를 게시하고 있습니다.
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 =>
{
// replace this logic with your own, probably just
// return false;
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
다음 은 허용 된 답변을 기반으로 재사용 가능한 일반 "속성 무시"해결 프로그램입니다 .
/// <summary>
/// Special JsonConvert resolver that allows you to ignore properties. See https://stackoverflow.com/a/13588192/1037948
/// </summary>
public class IgnorableSerializerContractResolver : DefaultContractResolver {
protected readonly Dictionary<Type, HashSet<string>> Ignores;
public IgnorableSerializerContractResolver() {
this.Ignores = new Dictionary<Type, HashSet<string>>();
}
/// <summary>
/// Explicitly ignore the given property(s) for the given type
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName">one or more properties to ignore. Leave empty to ignore the type entirely.</param>
public void Ignore(Type type, params string[] propertyName) {
// start bucket if DNE
if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet<string>();
foreach (var prop in propertyName) {
this.Ignores[type].Add(prop);
}
}
/// <summary>
/// Is the given property for the given type ignored?
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public bool IsIgnored(Type type, string propertyName) {
if (!this.Ignores.ContainsKey(type)) return false;
// if no properties provided, ignore the type entirely
if (this.Ignores[type].Count == 0) return true;
return this.Ignores[type].Contains(propertyName);
}
/// <summary>
/// The decision logic goes here
/// </summary>
/// <param name="member"></param>
/// <param name="memberSerialization"></param>
/// <returns></returns>
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (this.IsIgnored(property.DeclaringType, property.PropertyName)
// need to check basetype as well for EF -- @per comment by user576838
|| this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)) {
property.ShouldSerialize = instance => { return false; };
}
return property;
}
}
그리고 사용법 :
var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };
JsonIgnore
속성을 사용하십시오 .
예를 들어, 제외 할 Id
:
public class Person {
[JsonIgnore]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Here is a method based on drzaus' excellent serializer contract which uses lambda expressions. Simply add it to the same class. After all, who doesn't prefer the compiler to do the checking for them?
public IgnorableSerializerContractResolver Ignore<TModel>(Expression<Func<TModel, object>> selector)
{
MemberExpression body = selector.Body as MemberExpression;
if (body == null)
{
UnaryExpression ubody = (UnaryExpression)selector.Body;
body = ubody.Operand as MemberExpression;
if (body == null)
{
throw new ArgumentException("Could not get property name", "selector");
}
}
string propertyName = body.Member.Name;
this.Ignore(typeof (TModel), propertyName);
return this;
}
You can now ignore properties easily and fluently:
contract.Ignore<Node>(node => node.NextNode)
.Ignore<Node>(node => node.AvailableNodes);
I don't care to set the property names as strings, in case they ever change it would break my other code.
I had several "view modes" on the objects I needed to serialized, so I ended up doing something like this in the contract resolver (view mode provided by constructor argument):
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (viewMode == ViewModeEnum.UnregisteredCustomer && member.GetCustomAttributes(typeof(UnregisteredCustomerAttribute), true).Length == 0)
{
property.ShouldSerialize = instance => { return false; };
}
return property;
}
Where my objects look like this:
public interface IStatement
{
[UnregisteredCustomer]
string PolicyNumber { get; set; }
string PlanCode { get; set; }
PlanStatus PlanStatus { get; set; }
[UnregisteredCustomer]
decimal TotalAmount { get; }
[UnregisteredCustomer]
ICollection<IBalance> Balances { get; }
void SetBalances(IBalance[] balances);
}
The downside to this would be the bit of reflection in the resolver, but I think it's worth it to have more maintainable code.
I had good results with the combination of both drzaus and Steve Rukuts answers. However, I face a problem when I set JsonPropertyAttribute with a different name or caps for the property. For example:
[JsonProperty("username")]
public string Username { get; set; }
Include UnderlyingName into consideration solves the problem:
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (this.IsIgnored(property.DeclaringType, property.PropertyName)
|| this.IsIgnored(property.DeclaringType, property.UnderlyingName)
|| this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)
|| this.IsIgnored(property.DeclaringType.BaseType, property.UnderlyingName))
{
property.ShouldSerialize = instance => { return false; };
}
return property;
}
If you are willing to use F# (or simply use an API not optimized for C#), the FSharp.JsonSkippable library allows you to control in a simple and strongly typed manner whether to include a given property when serializing (and determine whether a property was included when deserializing), and moreover, to control/determine exclusion separately of nullability. (Full disclosure: I'm the author of the library.)
ReferenceURL : https://stackoverflow.com/questions/13588022/exclude-property-from-serialization-via-custom-attribute-json-net
'development' 카테고리의 다른 글
JavaScript에서 단일 비트를 어떻게 설정하고, 지우고, 토글합니까? (0) | 2020.12.27 |
---|---|
SQLite 매개 변수 대체 문제 (0) | 2020.12.27 |
NavigationViewController 신속한 ViewController 제공 (0) | 2020.12.27 |
애플리케이션 컨텍스트와 함께 글라이드 이미지 로딩 (0) | 2020.12.27 |
Gson :지도를 직렬화하는 더 쉬운 방법이 있습니까? (0) | 2020.12.27 |