.NET NewtonSoft JSON deserialisiert die Zuordnung zu einem anderen Eigenschaftsnamen

294

Ich habe folgende JSON-Zeichenfolge, die von einer externen Partei empfangen wird.

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

Meine Mapping-Klassen:

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

Die Frage ist, dass mir der Attributes Klassenname und die attributes Feldnamen in der TeamKlasse nicht gefallen . Stattdessen möchte ich, dass es benannt wird TeamScoreund auch _aus den Feldnamen entfernt und Eigennamen angegeben werden.

JsonConvert.DeserializeObject<RootObject>(jsonText);

Ich kann umbenennen Attributesin TeamScore, aber wenn ich den Feldnamen ( attributesin der TeamKlasse) ändere , wird er nicht richtig deserialisiert und gibt mir null. Wie kann ich das überwinden?

JenonD
quelle

Antworten:

572

Json.NET hat eine , JsonPropertyAttributedie Sie den Namen einer JSON - Eigenschaft angeben kann, so sollten Sie den Code sein:

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

Dokumentation: Serialisierungsattribute

Outcoldman
quelle
2
Kann ich zwei JsonProperty für eine Datei verwenden?
Ali Yousefi
1
@AliYousefie Glaube nicht. Aber die gute Frage wird sein, was erwarten Sie davon?
Outcoldman
5
Ich habe eine Schnittstelle, zwei Klassen werden diese Schnittstelle verwendet, aber Serverdaten haben zwei Eigenschaftsnamen für zwei Klassen, ich möchte zwei JsonProperty für eine Eigenschaft in meinen Schnittstellen verwenden.
Ali Yousefi
Wie können wir sicherstellen, dass die Antwort [deserilisiertes Objekt] einen Wert für EightyMinScore und nicht für Eighty_min_score hat
Gaurravs
In meinem Fall sende ich das RootObject als endgültige Antwort, aber wenn ich es als json aus der endgültigen Antwort lese, wird achtzig_min_score mit Wert und nicht mit EightyMinScore
Gaurravs
115

Wenn Sie dynamisches Mapping verwenden möchten und Ihr Modell nicht mit Attributen überladen möchten, hat dieser Ansatz für mich funktioniert

Verwendungszweck:

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

Logik:

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);
    }
}
Jack
quelle
1
Hat es für meinen Zweck ein wenig vereinfacht, aber dies ist eine bessere Lösung als "Unordnung deines Modells / deiner Domain";)
Andreas
4
Beeindruckend. Das ist episch; viel architektonisch fundierter.
David Betz
1
Es kann sich lohnen (wenn Sie mehr als eine davon erstellen), das Wörterbuch zu verschieben, den Code für alle Eigenschaftszuordnungen in eine Basisklasse zu verschieben und sie Eigenschaften hinzufügen zu lassen, aber die Details der Zuordnung zu ignorieren. Könnte es wert sein, das einfach zu Json.Net selbst hinzuzufügen.
James White
Dies sollte die akzeptable Antwort sein, da es, wie @DavidBetz sagte, das beste Design ist.
Im1dermike
Funktioniert diese Lösung auch mit verschachtelten Eigenschaften? Ich habe versucht, ein Objekt mit verschachtelten Eigenschaften zu deserialisieren, aber es funktioniert nicht.
Avi K.
8

Hinzufügen zu Jacks Lösung. Ich muss mit JsonProperty und Serialize deserialisieren, während ich JsonProperty ignoriere (oder umgekehrt). ReflectionHelper und Attribute Helper sind nur Hilfsklassen, die eine Liste von Eigenschaften oder Attributen für eine Eigenschaft abrufen. Ich kann einschließen, wenn es jemanden wirklich interessiert. Mit dem folgenden Beispiel können Sie das Ansichtsmodell serialisieren und "Amount" abrufen, obwohl die JsonProperty "RecurringPrice" lautet.

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

Verwendungszweck:

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

Modell:

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
quelle
2
Vielen Dank für Ihren IgnoreJsonPropertyResolver, da ich das Gleiche tun wollte (ignorieren Sie JsonProperty nur bei der Serialisierung). Leider funktioniert Ihre Lösung nur für Attribute der obersten Ebene und nicht für verschachtelte Typen. Der richtige Weg, alle JsonProperty-Attribute beim Serialisieren zu ignorieren, besteht darin, CreatePropertyden ContractResolver zu überschreiben . Dort ruf die Basis an: var jsonProperty = base.CreateProperty(memberInfo, memberSerialization);und setze dann jsonProperty.PropertyName = memberInfo.Name;. Endlich ist return jsonProperty;das alles was du brauchst.
Nate Cook
1
Was sind diese Helfer?
deadManN
1
@NateCook kannst du mir ein Beispiel zeigen? Ich brauche es jetzt
dringend
4

Wenn Sie die Antwort von Rentering.com in Szenarien erweitern, in denen ein ganzes Diagramm mit vielen Typen erstellt werden muss und Sie nach einer stark typisierten Lösung suchen, kann diese Klasse helfen, siehe Verwendung (fließend) unten. Es fungiert entweder als schwarze Liste oder als weiße Liste pro Typ. Ein Typ kann nicht beides sein ( Gist - enthält auch eine globale Ignorierliste).

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

Verwendungszweck:

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
quelle
0

Ich verwende JsonProperty-Attribute beim Serialisieren, ignoriere sie jedoch beim Deserialisieren mit den folgenden Attributen 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;
        }
    }

Mit ContractResolverjust setzt jede Eigenschaft auf den Namen der Klasseneigenschaft zurück (vereinfacht aus Shimmys Lösung). Verwendung:

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