.NET NewtonSoft JSON은 다른 속성 이름으로 맵을 역 직렬화합니다.


294

외부 당사자로부터받은 다음 JSON 문자열이 있습니다.

{
   "team":[
      {
         "v1":"",
         "attributes":{
            "eighty_min_score":"",
            "home_or_away":"home",
            "score":"22",
            "team_id":"500"
         }
      },
      {
         "v1":"",
         "attributes":{
            "eighty_min_score":"",
            "home_or_away":"away",
            "score":"30",
            "team_id":"600"
         }
      }
   ]
}

내 매핑 클래스 :

public class Attributes
{
    public string eighty_min_score { get; set; }
    public string home_or_away { get; set; }
    public string score { get; set; }
    public string team_id { get; set; }
}

public class Team
{
    public string v1 { get; set; }
    public Attributes attributes { get; set; }
}

public class RootObject
{
    public List<Team> team { get; set; }
}

문제는 내가 좋아하지 않는 것입니다 Attributes 클래스 이름attributes 필드 이름 에서 Team클래스를. 대신 이름을 지정 TeamScore하고 _필드 이름에서 제거 하고 적절한 이름 을 지정 하고 싶습니다 .

JsonConvert.DeserializeObject<RootObject>(jsonText);

로 이름 Attributes을 바꿀 수 TeamScore는 있지만 클래스 attributes에서 필드 이름을 변경하면 Team제대로 직렬화 해제되지 않고 나에게 제공 null됩니다. 이걸 어떻게 극복 할 수 있습니까?


답변:


572

Json.NET 에는 JsonPropertyAttributeJSON 속성의 이름을 지정할 수있는 코드가 있으므로 코드는 다음과 같아야합니다.

public class TeamScore
{
    [JsonProperty("eighty_min_score")]
    public string EightyMinScore { get; set; }
    [JsonProperty("home_or_away")]
    public string HomeOrAway { get; set; }
    [JsonProperty("score ")]
    public string Score { get; set; }
    [JsonProperty("team_id")]
    public string TeamId { get; set; }
}

public class Team
{
    public string v1 { get; set; }
    [JsonProperty("attributes")]
    public TeamScore TeamScores { get; set; }
}

public class RootObject
{
    public List<Team> Team { get; set; }
}

설명서 : 직렬화 속성


2
하나의 파일에 두 개의 JsonProperty를 사용할 수 있습니까?
알리 Yousefi

1
@AliYousefie 그렇게 생각하지 마십시오. 그러나 좋은 질문은, 그로부터 무엇을 얻을 수 있을까요?
outcoldman

5
인터페이스가 있고 두 개의 클래스 가이 인터페이스를 사용하지만 서버 데이터에는 두 개의 클래스에 대한 두 개의 속성 이름이 있습니다. 내 인터페이스의 한 속성에 두 개의 JsonProperty를 사용하고 싶습니다.
알리 Yousefi

응답 [deserilized object]가 eighty_min_score가 아닌 EightyMinScore에 대한 가치를 갖도록하는 방법
Gaurravs

내 경우에는 내가 최종 응답으로 RootObject을 보내고 있지만 최종 응답에서 JSON로 읽을 때, eighty_min_score 값으로 표시하지 EightyMinScore 함께
Gaurravs

115

동적 매핑을 사용하고 속성으로 모델을 복잡하게 만들고 싶지 않다면이 접근법이 효과적이었습니다.

용법:

var settings = new JsonSerializerSettings();
settings.DateFormatString = "YYYY-MM-DD";
settings.ContractResolver = new CustomContractResolver();
this.DataContext = JsonConvert.DeserializeObject<CountResponse>(jsonString, settings);

논리:

public class CustomContractResolver : DefaultContractResolver
{
    private Dictionary<string, string> PropertyMappings { get; set; }

    public CustomContractResolver()
    {
        this.PropertyMappings = new Dictionary<string, string> 
        {
            {"Meta", "meta"},
            {"LastUpdated", "last_updated"},
            {"Disclaimer", "disclaimer"},
            {"License", "license"},
            {"CountResults", "results"},
            {"Term", "term"},
            {"Count", "count"},
        };
    }

    protected override string ResolvePropertyName(string propertyName)
    {
        string resolvedName = null;
        var resolved = this.PropertyMappings.TryGetValue(propertyName, out resolvedName);
        return (resolved) ? resolvedName : base.ResolvePropertyName(propertyName);
    }
}

1
내 목적을 위해 약간 단순화했지만 이것이 더 나은 솔루션입니다. "모델 / 도메인을
Andreas

4
와. 그것은 서사시입니다. 훨씬 더 건축 적으로 건전한 방식입니다.
David Betz

1
사전을 이동시키는 것이 가치가있을 수 있습니다 (모든 속성 매핑에 대한 기본 클래스까지 코드를 조회하고 속성을 추가하도록하지만 매핑이 수행되는 방법에 대한 세부 사항은 무시하십시오). Json.Net 자체에 추가하는 것이 좋습니다.
제임스 화이트

@DavidBetz가 말했듯이 이것이 최고의 디자인이기 때문에 이것은 받아 들일만한 대답이어야합니다.
im1dermike

이 솔루션은 중첩 속성에서도 작동합니까? 중첩 속성을 사용하여 객체를 직렬화 해제하려고 시도했지만 작동하지 않습니다.
Avi K.

8

잭 솔루션에 추가. JsonProperty를 무시하면서 JsonProperty와 Serialize를 사용하여 직렬화를 해제해야합니다 (또는 그 반대). ReflectionHelper 및 Attribute Helper는 속성의 속성 또는 속성 목록을 가져 오는 도우미 클래스입니다. 누군가가 실제로 걱정한다면 포함시킬 수 있습니다. 아래 예제를 사용하면 JsonProperty가 "RecurringPrice"인 경우에도 뷰 모델을 직렬화하고 "Amount"를 얻을 수 있습니다.

    /// <summary>
    /// Ignore the Json Property attribute. This is usefule when you want to serialize or deserialize differently and not 
    /// let the JsonProperty control everything.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class IgnoreJsonPropertyResolver<T> : DefaultContractResolver
    {
        private Dictionary<string, string> PropertyMappings { get; set; }

        public IgnoreJsonPropertyResolver()
        {
            this.PropertyMappings = new Dictionary<string, string>();
            var properties = ReflectionHelper<T>.GetGetProperties(false)();
            foreach (var propertyInfo in properties)
            {
                var jsonProperty = AttributeHelper.GetAttribute<JsonPropertyAttribute>(propertyInfo);
                if (jsonProperty != null)
                {
                    PropertyMappings.Add(jsonProperty.PropertyName, propertyInfo.Name);
                }
            }
        }

        protected override string ResolvePropertyName(string propertyName)
        {
            string resolvedName = null;
            var resolved = this.PropertyMappings.TryGetValue(propertyName, out resolvedName);
            return (resolved) ? resolvedName : base.ResolvePropertyName(propertyName);
        }
    }

용법:

        var settings = new JsonSerializerSettings();
        settings.DateFormatString = "YYYY-MM-DD";
        settings.ContractResolver = new IgnoreJsonPropertyResolver<PlanViewModel>();
        var model = new PlanViewModel() {Amount = 100};
        var strModel = JsonConvert.SerializeObject(model,settings);

모델:

public class PlanViewModel
{

    /// <summary>
    ///     The customer is charged an amount over an interval for the subscription.
    /// </summary>
    [JsonProperty(PropertyName = "RecurringPrice")]
    public double Amount { get; set; }

    /// <summary>
    ///     Indicates the number of intervals between each billing. If interval=2, the customer would be billed every two
    ///     months or years depending on the value for interval_unit.
    /// </summary>
    public int Interval { get; set; } = 1;

    /// <summary>
    ///     Number of free trial days that can be granted when a customer is subscribed to this plan.
    /// </summary>
    public int TrialPeriod { get; set; } = 30;

    /// <summary>
    /// This indicates a one-time fee charged upfront while creating a subscription for this plan.
    /// </summary>
    [JsonProperty(PropertyName = "SetupFee")]
    public double SetupAmount { get; set; } = 0;


    /// <summary>
    /// String representing the type id, usually a lookup value, for the record.
    /// </summary>
    [JsonProperty(PropertyName = "TypeId")]
    public string Type { get; set; }

    /// <summary>
    /// Billing Frequency
    /// </summary>
    [JsonProperty(PropertyName = "BillingFrequency")]
    public string Period { get; set; }


    /// <summary>
    /// String representing the type id, usually a lookup value, for the record.
    /// </summary>
    [JsonProperty(PropertyName = "PlanUseType")]
    public string Purpose { get; set; }
}

2
같은 일을하려고했기 때문에 IgnoreJsonPropertyResolver에 감사드립니다 (JsonProperty는 직렬화에서만 무시하십시오). 불행히도 솔루션은 최상위 속성에만 작동하며 중첩 유형에는 적합하지 않습니다. 직렬화 할 때 모든 JsonProperty 속성을 무시하는 올바른 방법 CreateProperty은 ContractResolver에서 재정의 하는 것입니다. base :를 호출 var jsonProperty = base.CreateProperty(memberInfo, memberSerialization);한 다음을 설정하십시오 jsonProperty.PropertyName = memberInfo.Name;. 마지막으로 return jsonProperty;필요한 전부입니다.
Nate Cook

1
이 조력자들은 무엇입니까?
deadManN

1
@NateCook 샘플을 보여 주시겠습니까? 지금 당장
필요해

4

다양한 유형의 전체 그래프를 관리 해야하는 시나리오에서 Rentering.com의 답변을 확장 하고 강력한 유형의 솔루션을 찾고 있다면이 클래스가 도움이 될 수 있습니다. 유형별로 블랙리스트 또는 화이트리스트로 작동합니다. 유형은 둘 다 될 수 없습니다 ( 요점 -전역 무시 목록도 포함 함).

public class PropertyFilterResolver : DefaultContractResolver
{
  const string _Err = "A type can be either in the include list or the ignore list.";
  Dictionary<Type, IEnumerable<string>> _IgnorePropertiesMap = new Dictionary<Type, IEnumerable<string>>();
  Dictionary<Type, IEnumerable<string>> _IncludePropertiesMap = new Dictionary<Type, IEnumerable<string>>();
  public PropertyFilterResolver SetIgnoredProperties<T>(params Expression<Func<T, object>>[] propertyAccessors)
  {
    if (propertyAccessors == null) return this;

    if (_IncludePropertiesMap.ContainsKey(typeof(T))) throw new ArgumentException(_Err);

    var properties = propertyAccessors.Select(GetPropertyName);
    _IgnorePropertiesMap[typeof(T)] = properties.ToArray();
    return this;
  }

  public PropertyFilterResolver SetIncludedProperties<T>(params Expression<Func<T, object>>[] propertyAccessors)
  {
    if (propertyAccessors == null)
      return this;

    if (_IgnorePropertiesMap.ContainsKey(typeof(T))) throw new ArgumentException(_Err);

    var properties = propertyAccessors.Select(GetPropertyName);
    _IncludePropertiesMap[typeof(T)] = properties.ToArray();
    return this;
  }

  protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
  {
    var properties = base.CreateProperties(type, memberSerialization);

    var isIgnoreList = _IgnorePropertiesMap.TryGetValue(type, out IEnumerable<string> map);
    if (!isIgnoreList && !_IncludePropertiesMap.TryGetValue(type, out map))
      return properties;

    Func<JsonProperty, bool> predicate = jp => map.Contains(jp.PropertyName) == !isIgnoreList;
    return properties.Where(predicate).ToArray();
  }

  string GetPropertyName<TSource, TProperty>(
  Expression<Func<TSource, TProperty>> propertyLambda)
  {
    if (!(propertyLambda.Body is MemberExpression member))
      throw new ArgumentException($"Expression '{propertyLambda}' refers to a method, not a property.");

    if (!(member.Member is PropertyInfo propInfo))
      throw new ArgumentException($"Expression '{propertyLambda}' refers to a field, not a property.");

    var type = typeof(TSource);
    if (!type.GetTypeInfo().IsAssignableFrom(propInfo.DeclaringType.GetTypeInfo()))
      throw new ArgumentException($"Expresion '{propertyLambda}' refers to a property that is not from type '{type}'.");

    return propInfo.Name;
  }
}

용법:

var resolver = new PropertyFilterResolver()
  .SetIncludedProperties<User>(
    u => u.Id, 
    u => u.UnitId)
  .SetIgnoredProperties<Person>(
    r => r.Responders)
  .SetIncludedProperties<Blog>(
    b => b.Id)
  .Ignore(nameof(IChangeTracking.IsChanged)); //see gist

0

직렬화 할 때 JsonProperty 속성을 사용하고 있지만 이것을 사용하여 역 직렬화 할 때는 무시합니다 ContractResolver.

public class IgnoreJsonPropertyContractResolver: DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            var properties = base.CreateProperties(type, memberSerialization);
            foreach (var p in properties) { p.PropertyName = p.UnderlyingName; }
            return properties;
        }
    }

ContractResolver단지 (이상 진동의 솔루션에서 간체) 클래스의 속성 이름에 대한 모든 속성 등을 설정합니다. 용법:

var airplane= JsonConvert.DeserializeObject<Airplane>(json, 
    new JsonSerializerSettings { ContractResolver = new IgnoreJsonPropertyContractResolver() });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.