.NET NewtonSoft JSON deserialize map ke nama properti yang berbeda

294

Saya telah mengikuti string JSON yang diterima dari pihak eksternal.

{
   "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"
         }
      }
   ]
}

Kelas pemetaan saya:

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; }
}

Pertanyaannya adalah saya tidak suka Attributes nama kelas danattributes bidang di Teamkelas. Sebagai gantinya, saya ingin diberi nama TeamScoredan juga untuk menghapus _dari nama bidang dan memberikan nama yang tepat.

JsonConvert.DeserializeObject<RootObject>(jsonText);

Saya bisa mengganti nama Attributes menjadi TeamScore, tetapi jika saya mengubah nama bidang ( attributesdi Teamkelas), itu tidak akan deserialize dengan benar dan memberi saya null. Bagaimana saya bisa mengatasi ini?

JenonD
sumber

Jawaban:

572

Json.NET memiliki JsonPropertyAttributeyang memungkinkan Anda menentukan nama properti JSON, jadi kode Anda harus:

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; }
}

Dokumentasi: Atribut Serialisasi

outcoldman
sumber
2
dapatkah saya menggunakan dua JsonProperty untuk satu yang diajukan?
Ali Yousefi
1
@ AliYousefie Jangan berpikir begitu. Tapi pertanyaan baiknya adalah, apa yang Anda harapkan dari hal itu?
outcoldman
5
saya memiliki antarmuka, dua kelas digunakan antarmuka ini, tetapi data server memiliki dua nama properti untuk dua kelas, saya ingin menggunakan dua JsonProperty untuk satu properti di antarmuka saya.
Ali Yousefi
bagaimana kita dapat memastikan untuk memiliki respons [objek yang disterilkan] memiliki nilai untuk EightyMinScore dan bukan eighty_min_score
Gaurravs
Dalam kasus saya, saya mengirim RootObject sebagai respons akhir, tetapi ketika saya membacanya sebagai json dari respons akhir, eighty_min_score ditampilkan dengan nilai dan bukan dengan EightyMinScore
Gaurravs
115

Jika Anda ingin menggunakan pemetaan dinamis, dan tidak ingin mengacaukan model Anda dengan atribut, pendekatan ini bekerja untuk saya

Pemakaian:

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

Logika:

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);
    }
}
Mendongkrak
sumber
1
Apakah menyederhanakannya sedikit untuk tujuan saya tetapi ini adalah solusi yang lebih baik maka "mengacaukan Anda model / domain";)
Andreas
4
Wow. Itu epik; jauh lebih baik secara arsitektur dalam melakukannya.
David Betz
1
Mungkin (jika Anda membuat lebih dari satu di antaranya) layak untuk memindahkan kamus, cari kode ke kelas dasar untuk semua pemetaan properti dan biarkan mereka menambahkan properti tetapi abaikan detail bagaimana pemetaan terjadi. Mungkin layak hanya menambahkan itu ke Json.Net sendiri.
James White
Ini harus menjadi jawaban yang dapat diterima karena, seperti yang dikatakan @DavidBetz, ini adalah desain terbaik.
im1dermike
Apakah solusi ini juga berfungsi dengan properti bersarang? Saya mencoba untuk menghilangkan bakteri objek dengan properti bersarang dan tidak berfungsi.
Avi K.
8

Menambah solusi Jacks. Saya perlu Deserialize menggunakan JsonProperty dan Serialize sambil mengabaikan JsonProperty (atau sebaliknya). ReflectionHelper dan Attribute Helper hanyalah kelas pembantu yang mendapatkan daftar properti atau atribut untuk properti. Saya dapat menyertakan jika ada yang benar-benar peduli. Dengan menggunakan contoh di bawah ini Anda dapat membuat serial viewmodel dan mendapatkan "Jumlah" meskipun JsonProperty adalah "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);
        }
    }

Pemakaian:

        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);

Model:

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; }
}
Rentering.com
sumber
2
Terima kasih atas IgnoreJsonPropertyResolver Anda, karena saya ingin melakukan hal yang sama (abaikan JsonProperty hanya pada serialisasi). Sayangnya solusi Anda hanya berfungsi untuk atribut tingkat atas dan bukan tipe bersarang. Cara yang tepat untuk mengabaikan semua atribut JsonProperty ketika membuat serial adalah dengan mengesampingkan CreatePropertydalam ContractResolver. Ada panggilan pangkalan: var jsonProperty = base.CreateProperty(memberInfo, memberSerialization);dan kemudian atur jsonProperty.PropertyName = memberInfo.Name;. Akhirnya hanya return jsonProperty;itu yang kamu butuhkan.
Nate Cook
1
apa pembantu ini?
deadManN
1
@NateCook dapatkah Anda menunjukkan kepada saya sampel? saya sangat membutuhkannya sekarang
deadManN
4

Memperluas jawaban Rentering.com , dalam skenario di mana seluruh grafik berbagai jenis harus dijaga, dan Anda sedang mencari solusi yang sangat diketik, kelas ini dapat membantu, lihat penggunaan (lancar) di bawah ini. Ini beroperasi sebagai daftar hitam atau daftar putih per jenis. Tipe tidak boleh keduanya ( Intisari - juga berisi daftar abaikan global).

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;
  }
}

Pemakaian:

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
Shimmy Weitzhandler
sumber
0

Saya menggunakan atribut JsonProperty ketika membuat cerita bersambung tetapi mengabaikannya saat melakukan deserialisasi menggunakan ini 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;
        }
    }

The ContractResolverhanya menetapkan setiap kembali properti untuk nama properti kelas (disederhanakan dari solusi Shimmy ini). Pemakaian:

var airplane= JsonConvert.DeserializeObject<Airplane>(json, 
    new JsonSerializerSettings { ContractResolver = new IgnoreJsonPropertyContractResolver() });
Periang
sumber