.NET NewtonSoft JSON deserialize map เป็นชื่อคุณสมบัติอื่น


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ชั้นเรียน) ก็จะไม่ deserialize nullอย่างถูกต้องและให้ฉัน ฉันจะเอาชนะสิ่งนี้ได้อย่างไร


คำตอบ:


572

Json.NETมีชื่อJsonPropertyAttributeที่อนุญาตให้คุณระบุชื่อของคุณสมบัติ JSON ดังนั้นรหัสของคุณควรเป็น:

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 สองรายการสำหรับยื่นได้หรือไม่
Ali Yousefi

1
@AliYousefie ไม่คิดอย่างนั้น แต่คำถามที่ดีคือคุณคาดหวังอะไรจากสิ่งนั้น
outcoldman

5
ฉันมีอินเทอร์เฟซสองคลาสใช้อินเทอร์เฟซนี้ แต่ข้อมูลเซิร์ฟเวอร์มีชื่อคุณสมบัติสองสำหรับสองคลาสฉันต้องการใช้ JsonProperty สองสำหรับคุณสมบัติหนึ่งในอินเทอร์เฟซของฉัน
Ali Yousefi

เราจะแน่ใจได้อย่างไรว่าจะมีการตอบสนอง [วัตถุที่ถูกทำให้ปลอดโปร่ง] มีค่าสำหรับ EightyMinScore และไม่ใช่ eighty_min_score
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 เข้าไปเอง
James White

นี่ควรเป็นคำตอบที่ได้รับการยอมรับเพราะอย่างที่ @DavidBetz กล่าวว่ามันเป็นการออกแบบที่ดีที่สุด
im1dermike

โซลูชันนี้ทำงานกับคุณสมบัติที่ซ้อนกันได้หรือไม่ ฉันพยายามลบวัตถุที่มีคุณสมบัติซ้อนกันและไม่ทำงาน
Avi K.

8

การเพิ่มโซลูชัน Jacks ฉันต้อง Deserialize โดยใช้ JsonProperty และทำให้เป็นอนุกรมในขณะที่ละเว้น JsonProperty (หรือกลับกัน) ReflectionHelper และ Attribute Helper เป็นเพียงคลาสตัวช่วยที่รับรายการคุณสมบัติหรือคุณสมบัติสำหรับคุณสมบัติ ฉันสามารถรวมถ้าใครสนใจจริง ๆ การใช้ตัวอย่างด้านล่างนี้คุณสามารถทำให้เป็นอันดับต่อมาโมเดลดูและรับ "จำนวนเงิน" แม้ว่า JsonProperty คือ "RecurringPrice"

    /// <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 มีฐานโทรติดต่อ: แล้วตั้งvar jsonProperty = base.CreateProperty(memberInfo, memberSerialization); jsonProperty.PropertyName = memberInfo.Name;ในที่สุดreturn jsonProperty;นั่นคือทั้งหมดที่คุณต้องการ
Nate Cook

1
ผู้ช่วยเหลือเหล่านี้คืออะไร?
deadManN

1
@NateCook คุณช่วยแสดงตัวอย่างให้ฉันดูได้ไหม? ฉันต้องการมันไม่ดีตอนนี้
deadManN

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 เมื่อทำให้เป็นอันดับ แต่ไม่สนใจเมื่อ deserializing ใช้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เพียงแค่ตั้งค่าคุณสมบัติกลับทุกชื่อคุณสมบัติชั้น (จีนจากการแก้ปัญหาของ Shimmy) การใช้งาน:

var airplane= JsonConvert.DeserializeObject<Airplane>(json, 
    new JsonSerializerSettings { ContractResolver = new IgnoreJsonPropertyContractResolver() });
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.