So behandeln Sie mit JSON.net sowohl ein einzelnes Element als auch ein Array für dieselbe Eigenschaft

101

Ich versuche, meine SendGridPlus-Bibliothek so zu reparieren, dass sie mit SendGrid-Ereignissen umgehen kann, habe jedoch Probleme mit der inkonsistenten Behandlung von Kategorien in der API.

In der folgenden Beispielnutzlast aus der SendGrid-API-Referenz werden Sie feststellen, dass die categoryEigenschaft für jedes Element entweder eine einzelne Zeichenfolge oder ein Array von Zeichenfolgen sein kann.

[
  {
    "email": "[email protected]",
    "timestamp": 1337966815,
    "category": [
      "newuser",
      "transactional"
    ],
    "event": "open"
  },
  {
    "email": "[email protected]",
    "timestamp": 1337966815,
    "category": "olduser",
    "event": "open"
  }
]

Es scheint, dass meine Optionen, um JSON.NET so zu machen, darin bestehen, die Zeichenfolge zu korrigieren, bevor sie eingeht, oder JSON.NET so zu konfigurieren, dass die falschen Daten akzeptiert werden. Ich würde lieber keine String-Analyse durchführen, wenn ich damit durchkommen kann.

Gibt es eine andere Möglichkeit, mit Json.Net damit umzugehen?

Robert McLaws
quelle

Antworten:

203

Der beste Weg, um mit dieser Situation umzugehen, ist die Verwendung eines benutzerdefinierten JsonConverter.

Bevor wir zum Konverter gelangen, müssen wir eine Klasse definieren, in die die Daten deserialisiert werden sollen. CategoriesDefinieren Sie die Eigenschaft, die zwischen einem einzelnen Element und einem Array variieren kann, als List<string>und markieren Sie sie mit einem [JsonConverter]Attribut, damit JSON.Net den benutzerdefinierten Konverter für diese Eigenschaft verwenden kann. Ich würde auch empfehlen, [JsonProperty]Attribute zu verwenden, damit den Elementeigenschaften unabhängig von der Definition in JSON aussagekräftige Namen zugewiesen werden können.

class Item
{
    [JsonProperty("email")]
    public string Email { get; set; }

    [JsonProperty("timestamp")]
    public int Timestamp { get; set; }

    [JsonProperty("event")]
    public string Event { get; set; }

    [JsonProperty("category")]
    [JsonConverter(typeof(SingleOrArrayConverter<string>))]
    public List<string> Categories { get; set; }
}

Hier ist, wie ich den Konverter implementieren würde. Beachten Sie, dass ich den Konverter generisch gemacht habe, damit er bei Bedarf mit Zeichenfolgen oder anderen Objekttypen verwendet werden kann.

class SingleOrArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(List<T>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Array)
        {
            return token.ToObject<List<T>>();
        }
        return new List<T> { token.ToObject<T>() };
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Hier ist ein kurzes Programm, das den Konverter in Aktion mit Ihren Beispieldaten demonstriert:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        [
          {
            ""email"": ""[email protected]"",
            ""timestamp"": 1337966815,
            ""category"": [
              ""newuser"",
              ""transactional""
            ],
            ""event"": ""open""
          },
          {
            ""email"": ""[email protected]"",
            ""timestamp"": 1337966815,
            ""category"": ""olduser"",
            ""event"": ""open""
          }
        ]";

        List<Item> list = JsonConvert.DeserializeObject<List<Item>>(json);

        foreach (Item obj in list)
        {
            Console.WriteLine("email: " + obj.Email);
            Console.WriteLine("timestamp: " + obj.Timestamp);
            Console.WriteLine("event: " + obj.Event);
            Console.WriteLine("categories: " + string.Join(", ", obj.Categories));
            Console.WriteLine();
        }
    }
}

Und schließlich ist hier die Ausgabe der oben genannten:

email: [email protected]
timestamp: 1337966815
event: open
categories: newuser, transactional

email: [email protected]
timestamp: 1337966815
event: open
categories: olduser

Geige: https://dotnetfiddle.net/lERrmu

BEARBEITEN

Wenn Sie in die andere Richtung gehen müssen, dh serialisieren, während Sie das gleiche Format beibehalten, können Sie die WriteJson()unten gezeigte Methode des Konverters implementieren . (Stellen Sie sicher, dass Sie die CanWriteÜberschreibung entfernen oder in "Zurückgeben" ändern, da sie truesonst WriteJson()niemals aufgerufen wird.)

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        List<T> list = (List<T>)value;
        if (list.Count == 1)
        {
            value = list[0];
        }
        serializer.Serialize(writer, value);
    }

Geige: https://dotnetfiddle.net/XG3eRy

Brian Rogers
quelle
5
Perfekt! Du bist der Mann. Glücklicherweise hatte ich bereits alle anderen Dinge über die Verwendung von JsonProperty getan, um die Eigenschaften aussagekräftiger zu machen. Vielen Dank für eine erstaunlich vollständige Antwort. :)
Robert McLaws
Kein Problem; Ich bin froh, dass du es hilfreich fandest.
Brian Rogers
1
Ausgezeichnet! Das ist es, wonach ich gesucht habe. @BrianRogers, wenn Sie jemals in Amsterdam sind, sind Getränke auf mich!
Mad Dog Tannen
2
@israelaltar Sie müssen den Konverter nicht zum DeserializeObjectAufruf hinzufügen, wenn Sie das [JsonConverter]Attribut für die List-Eigenschaft in Ihrer Klasse verwenden, wie in der obigen Antwort gezeigt. Wenn Sie das Attribut nicht verwenden, müssen Sie den Konverter an übergeben DeserializeObject.
Brian Rogers
1
@ShaunLangley Damit der Konverter ein Array anstelle einer Liste verwendet, ändern Sie alle Verweise auf List<T>im Konverter auf T[]und ändern Sie .Countauf .Length. dotnetfiddle.net/vnCNgZ
Brian Rogers
6

Ich habe ewig daran gearbeitet und danke Brian für seine Antwort. Alles, was ich hinzufüge, ist die Antwort von vb.net!:

Public Class SingleValueArrayConverter(Of T)
sometimes-array-and-sometimes-object
    Inherits JsonConverter
    Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
        Throw New NotImplementedException()
    End Sub

    Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
        Dim retVal As Object = New [Object]()
        If reader.TokenType = JsonToken.StartObject Then
            Dim instance As T = DirectCast(serializer.Deserialize(reader, GetType(T)), T)
            retVal = New List(Of T)() From { _
                instance _
            }
        ElseIf reader.TokenType = JsonToken.StartArray Then
            retVal = serializer.Deserialize(reader, objectType)
        End If
        Return retVal
    End Function
    Public Overrides Function CanConvert(objectType As Type) As Boolean
        Return False
    End Function
End Class

dann in deiner Klasse:

 <JsonProperty(PropertyName:="JsonName)> _
 <JsonConverter(GetType(SingleValueArrayConverter(Of YourObject)))> _
    Public Property YourLocalName As List(Of YourObject)

Hoffe das spart dir etwas Zeit

Grantay
quelle
Tippfehler: <JsonConverter (GetType (SingleValueArrayConverter (Of YourObject)))> _ Öffentliches Eigentum YourLocalName As List (Of YourObject)
GlennG
3

Als kleine Variation der großartigen Antwort von Brian Rogers sind hier zwei optimierte Versionen von SingleOrArrayConverter<T>.

Erstens ist hier eine Version, die für alle List<T>für jeden Typ funktioniert T, der selbst keine Sammlung ist:

public class SingleOrArrayListConverter : JsonConverter
{
    // Adapted from this answer https://stackoverflow.com/a/18997172
    // to /programming/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n
    // by Brian Rogers https://stackoverflow.com/users/10263/brian-rogers
    readonly bool canWrite;
    readonly IContractResolver resolver;

    public SingleOrArrayListConverter() : this(false) { }

    public SingleOrArrayListConverter(bool canWrite) : this(canWrite, null) { }

    public SingleOrArrayListConverter(bool canWrite, IContractResolver resolver)
    {
        this.canWrite = canWrite;
        // Use the global default resolver if none is passed in.
        this.resolver = resolver ?? new JsonSerializer().ContractResolver;
    }

    static bool CanConvert(Type objectType, IContractResolver resolver)
    {
        Type itemType;
        JsonArrayContract contract;
        return CanConvert(objectType, resolver, out itemType, out contract);
    }

    static bool CanConvert(Type objectType, IContractResolver resolver, out Type itemType, out JsonArrayContract contract)
    {
        if ((itemType = objectType.GetListItemType()) == null)
        {
            itemType = null;
            contract = null;
            return false;
        }
        // Ensure that [JsonObject] is not applied to the type.
        if ((contract = resolver.ResolveContract(objectType) as JsonArrayContract) == null)
            return false;
        var itemContract = resolver.ResolveContract(itemType);
        // Not implemented for jagged arrays.
        if (itemContract is JsonArrayContract)
            return false;
        return true;
    }

    public override bool CanConvert(Type objectType) { return CanConvert(objectType, resolver); }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        Type itemType;
        JsonArrayContract contract;

        if (!CanConvert(objectType, serializer.ContractResolver, out itemType, out contract))
            throw new JsonSerializationException(string.Format("Invalid type for {0}: {1}", GetType(), objectType));
        if (reader.MoveToContent().TokenType == JsonToken.Null)
            return null;
        var list = (IList)(existingValue ?? contract.DefaultCreator());
        if (reader.TokenType == JsonToken.StartArray)
            serializer.Populate(reader, list);
        else
            // Here we take advantage of the fact that List<T> implements IList to avoid having to use reflection to call the generic Add<T> method.
            list.Add(serializer.Deserialize(reader, itemType));
        return list;
    }

    public override bool CanWrite { get { return canWrite; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var list = value as ICollection;
        if (list == null)
            throw new JsonSerializationException(string.Format("Invalid type for {0}: {1}", GetType(), value.GetType()));
        // Here we take advantage of the fact that List<T> implements IList to avoid having to use reflection to call the generic Count method.
        if (list.Count == 1)
        {
            foreach (var item in list)
            {
                serializer.Serialize(writer, item);
                break;
            }
        }
        else
        {
            writer.WriteStartArray();
            foreach (var item in list)
                serializer.Serialize(writer, item);
            writer.WriteEndArray();
        }
    }
}

public static partial class JsonExtensions
{
    public static JsonReader MoveToContent(this JsonReader reader)
    {
        while ((reader.TokenType == JsonToken.Comment || reader.TokenType == JsonToken.None) && reader.Read())
            ;
        return reader;
    }

    internal static Type GetListItemType(this Type type)
    {
        // Quick reject for performance
        if (type.IsPrimitive || type.IsArray || type == typeof(string))
            return null;
        while (type != null)
        {
            if (type.IsGenericType)
            {
                var genType = type.GetGenericTypeDefinition();
                if (genType == typeof(List<>))
                    return type.GetGenericArguments()[0];
            }
            type = type.BaseType;
        }
        return null;
    }
}

Es kann wie folgt verwendet werden:

var settings = new JsonSerializerSettings
{
    // Pass true if you want single-item lists to be reserialized as single items
    Converters = { new SingleOrArrayListConverter(true) },
};
var list = JsonConvert.DeserializeObject<List<Item>>(json, settings);

Anmerkungen:

  • Der Konverter vermeidet die Notwendigkeit, den gesamten JSON-Wert als JTokenHierarchie in den Speicher vorzuladen.

  • Der Konverter gilt nicht für Listen, deren Elemente auch als Sammlungen serialisiert sind, z List<string []>

  • Das canWritean den Konstruktor übergebene boolesche Argument steuert, ob Einzelelementlisten als JSON-Werte oder als JSON-Arrays neu serialisiert werden sollen.

  • Der Konverter ReadJson() verwendet das existingValuevorab zugewiesene if, um das Auffüllen von Mitgliedern der Nur-Get-Liste zu unterstützen.

Zweitens ist hier eine Version, die mit anderen generischen Sammlungen wie z ObservableCollection<T> :

public class SingleOrArrayCollectionConverter<TCollection, TItem> : JsonConverter
    where TCollection : ICollection<TItem>
{
    // Adapted from this answer https://stackoverflow.com/a/18997172
    // to /programming/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n
    // by Brian Rogers https://stackoverflow.com/users/10263/brian-rogers
    readonly bool canWrite;

    public SingleOrArrayCollectionConverter() : this(false) { }

    public SingleOrArrayCollectionConverter(bool canWrite) { this.canWrite = canWrite; }

    public override bool CanConvert(Type objectType)
    {
        return typeof(TCollection).IsAssignableFrom(objectType);
    }

    static void ValidateItemContract(IContractResolver resolver)
    {
        var itemContract = resolver.ResolveContract(typeof(TItem));
        if (itemContract is JsonArrayContract)
            throw new JsonSerializationException(string.Format("Item contract type {0} not supported.", itemContract));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        ValidateItemContract(serializer.ContractResolver);
        if (reader.MoveToContent().TokenType == JsonToken.Null)
            return null;
        var list = (ICollection<TItem>)(existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());
        if (reader.TokenType == JsonToken.StartArray)
            serializer.Populate(reader, list);
        else
            list.Add(serializer.Deserialize<TItem>(reader));
        return list;
    }

    public override bool CanWrite { get { return canWrite; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        ValidateItemContract(serializer.ContractResolver);
        var list = value as ICollection<TItem>;
        if (list == null)
            throw new JsonSerializationException(string.Format("Invalid type for {0}: {1}", GetType(), value.GetType()));
        if (list.Count == 1)
        {
            foreach (var item in list)
            {
                serializer.Serialize(writer, item);
                break;
            }
        }
        else
        {
            writer.WriteStartArray();
            foreach (var item in list)
                serializer.Serialize(writer, item);
            writer.WriteEndArray();
        }
    }
}

Wenn Ihr Modell dann beispielsweise eine verwendet ObservableCollection<T> für einigeT können Sie es wie folgt anwenden:

class Item
{
    public string Email { get; set; }
    public int Timestamp { get; set; }
    public string Event { get; set; }

    [JsonConverter(typeof(SingleOrArrayCollectionConverter<ObservableCollection<string>, string>))]
    public ObservableCollection<string> Category { get; set; }
}

Anmerkungen:

  • Neben den Hinweisen und Einschränkungen für SingleOrArrayListConverter muss der TCollectionTyp lesen / schreiben und einen parameterlosen Konstruktor haben.

Demo-Geige mit grundlegenden Unit-Tests hier .

dbc
quelle
0

Ich hatte ein sehr ähnliches Problem. Meine Json-Anfrage war mir völlig unbekannt. Ich wusste es nur.

Darin befinden sich eine objectId und einige anonyme Schlüsselwertpaare UND Arrays.

Ich habe es für ein EAV-Modell verwendet, das ich gemacht habe:

Meine JSON-Anfrage:

{objectId ": 2," firstName ":" Hans "," email ": [" [email protected] "," [email protected] "]," name ":" Andre "," etwas ": [" 232 "," 123 "]}

Meine Klasse habe ich definiert:

[JsonConverter(typeof(AnonyObjectConverter))]
public class AnonymObject
{
    public AnonymObject()
    {
        fields = new Dictionary<string, string>();
        list = new List<string>();
    }

    public string objectid { get; set; }
    public Dictionary<string, string> fields { get; set; }
    public List<string> list { get; set; }
}

und jetzt, da ich unbekannte Attribute mit ihrem Wert und den darin enthaltenen Arrays deserialisieren möchte, sieht mein Konverter so aus:

   public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        AnonymObject anonym = existingValue as AnonymObject ?? new AnonymObject();
        bool isList = false;
        StringBuilder listValues = new StringBuilder();

        while (reader.Read())
        {
            if (reader.TokenType == JsonToken.EndObject) continue;

            if (isList)
            {
                while (reader.TokenType != JsonToken.EndArray)
                {
                    listValues.Append(reader.Value.ToString() + ", ");

                    reader.Read();
                }
                anonym.list.Add(listValues.ToString());
                isList = false;

                continue;
            }

            var value = reader.Value.ToString();

            switch (value.ToLower())
            {
                case "objectid":
                    anonym.objectid = reader.ReadAsString();
                    break;
                default:
                    string val;

                    reader.Read();
                    if(reader.TokenType == JsonToken.StartArray)
                    {
                        isList = true;
                        val = "ValueDummyForEAV";
                    }
                    else
                    {
                        val = reader.Value.ToString();
                    }
                    try
                    {
                        anonym.fields.Add(value, val);
                    }
                    catch(ArgumentException e)
                    {
                        throw new ArgumentException("Multiple Attribute found");
                    }
                    break;
            }

        }

        return anonym;
    }

Jedes Mal, wenn ich ein AnonymObject erhalte, kann ich das Wörterbuch durchlaufen und jedes Mal, wenn mein Flag "ValueDummyForEAV" vorhanden ist, wechsle ich zur Liste, lese die erste Zeile und teile die Werte auf. Danach lösche ich den ersten Eintrag aus der Liste und fahre mit der Iteration aus dem Wörterbuch fort.

Vielleicht hat jemand das gleiche Problem und kann es benutzen :)

Grüße Andre

Andre Fritzsche
quelle
0

Sie können a JSONConverterAttributewie hier verwenden: http://james.newtonking.com/projects/json/help/

Vorausgesetzt, Sie haben eine Klasse, die aussieht

public class RootObject
{
    public string email { get; set; }
    public int timestamp { get; set; }
    public string smtpid { get; set; }
    public string @event { get; set; }
    public string category[] { get; set; }
}

Sie würden die Kategorie Eigenschaft wie hier gezeigt dekorieren:

    [JsonConverter(typeof(SendGridCategoryConverter))]
    public string category { get; set; }

public class SendGridCategoryConverter : JsonConverter
{
  public override bool CanConvert(Type objectType)
  {
    return true; // add your own logic
  }

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  {
   // do work here to handle returning the array regardless of the number of objects in 
  }

  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  {
    // Left as an exercise to the reader :)
    throw new NotImplementedException();
  }
}
Tim Gabrhel
quelle
Vielen Dank dafür, aber es behebt das Problem immer noch nicht. Wenn ein tatsächliches Array eingeht, wird immer noch ein Fehler ausgegeben, bevor mein Code überhaupt für ein Objekt ausgeführt werden kann, das ein tatsächliches Array hat. 'Zusätzliche Informationen: Unerwartetes Token beim Deserialisieren des Objekts: String. Pfad '[2] .Kategorie [0]', Zeile 17, Position 27. '
Robert McLaws
+ "" Ereignis ":" verarbeitet ", \ n" + "} \ n" + "]";
Robert McLaws
Es verarbeitete das erste Objekt fein und behandelte kein Array schön. Aber als ich ein Array für das 2. Objekt erstellt habe, ist es fehlgeschlagen.
Robert McLaws
@AdvancedREI Ohne Ihren Code zu sehen, würde ich vermuten, dass Sie den Reader nach dem Lesen des JSON falsch positioniert lassen. Anstatt zu versuchen, den Reader direkt zu verwenden, ist es besser, ein JToken-Objekt vom Reader zu laden und von dort fortzufahren. Siehe meine Antwort für eine funktionierende Implementierung des Konverters.
Brian Rogers
Viel detaillierter in Brians Antwort. Verwenden Sie diese :)
Tim Gabrhel
0

Um dies zu handhaben, müssen Sie einen benutzerdefinierten JsonConverter verwenden. Aber das haben Sie wahrscheinlich schon gedacht. Sie suchen nur einen Konverter, den Sie sofort verwenden können. Und das bietet mehr als nur eine Lösung für die beschriebene Situation. Ich gebe ein Beispiel mit der gestellten Frage.

So benutze ich meinen Konverter:

Platzieren Sie ein JsonConverter-Attribut über der Eigenschaft. JsonConverter(typeof(SafeCollectionConverter))

public class SendGridEvent
{
    [JsonProperty("email")]
    public string Email { get; set; }

    [JsonProperty("timestamp")]
    public long Timestamp { get; set; }

    [JsonProperty("category"), JsonConverter(typeof(SafeCollectionConverter))]
    public string[] Category { get; set; }

    [JsonProperty("event")]
    public string Event { get; set; }
}

Und das ist mein Konverter:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;

namespace stackoverflow.question18994685
{
    public class SafeCollectionConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return true;
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            //This not works for Populate (on existingValue)
            return serializer.Deserialize<JToken>(reader).ToObjectCollectionSafe(objectType, serializer);
        }     

        public override bool CanWrite => false;

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
}

Und dieser Konverter verwendet die folgende Klasse:

using System;

namespace Newtonsoft.Json.Linq
{
    public static class SafeJsonConvertExtensions
    {
        public static object ToObjectCollectionSafe(this JToken jToken, Type objectType)
        {
            return ToObjectCollectionSafe(jToken, objectType, JsonSerializer.CreateDefault());
        }

        public static object ToObjectCollectionSafe(this JToken jToken, Type objectType, JsonSerializer jsonSerializer)
        {
            var expectArray = typeof(System.Collections.IEnumerable).IsAssignableFrom(objectType);

            if (jToken is JArray jArray)
            {
                if (!expectArray)
                {
                    //to object via singel
                    if (jArray.Count == 0)
                        return JValue.CreateNull().ToObject(objectType, jsonSerializer);

                    if (jArray.Count == 1)
                        return jArray.First.ToObject(objectType, jsonSerializer);
                }
            }
            else if (expectArray)
            {
                //to object via JArray
                return new JArray(jToken).ToObject(objectType, jsonSerializer);
            }

            return jToken.ToObject(objectType, jsonSerializer);
        }

        public static T ToObjectCollectionSafe<T>(this JToken jToken)
        {
            return (T)ToObjectCollectionSafe(jToken, typeof(T));
        }

        public static T ToObjectCollectionSafe<T>(this JToken jToken, JsonSerializer jsonSerializer)
        {
            return (T)ToObjectCollectionSafe(jToken, typeof(T), jsonSerializer);
        }
    }
}

Was macht es genau? Wenn Sie das Konverterattribut platzieren, wird der Konverter für diese Eigenschaft verwendet. Sie können es für ein normales Objekt verwenden, wenn Sie ein JSON-Array mit 1 oder keinem Ergebnis erwarten. Oder Sie verwenden es an einem Ort, an IEnumerabledem Sie ein JSON-Objekt oder ein JSON-Array erwarten. (Wissen, dass ein array- object[]- ein ist IEnumerable) Ein Nachteil ist, dass dieser Konverter nur über einer Eigenschaft platziert werden kann, weil er glaubt, alles konvertieren zu können. Und sei gewarnt . A stringist auch ein IEnumerable.

Und es bietet mehr als eine Antwort auf die Frage: Wenn Sie nach etwas anhand der ID suchen, wissen Sie, dass Sie ein Array mit einem oder keinem Ergebnis zurückerhalten. Die ToObjectCollectionSafe<TResult>()Methode kann das für Sie erledigen.

Dies kann für Single Result vs Array mit JSON.net verwendet werden und behandelt sowohl ein einzelnes Element als auch ein Array für dieselbe Eigenschaft und kann ein Array in ein einzelnes Objekt konvertieren.

Ich habe dies für REST-Anforderungen auf einem Server mit einem Filter gemacht, der ein Ergebnis in einem Array zurückgegeben hat, aber das Ergebnis als einzelnes Objekt in meinem Code zurückbekommen wollte. Und auch für eine OData-Ergebnisantwort mit erweitertem Ergebnis mit einem Element in einem Array.

Viel Spass damit.

Roberto B.
quelle
-2

Ich habe eine andere Lösung gefunden, die die Kategorie mithilfe von Objekten als Zeichenfolge oder Array behandeln kann. Auf diese Weise muss ich mich nicht mit dem JSON-Serializer anlegen.

Bitte schauen Sie mal rein, wenn Sie Zeit haben und sagen Sie mir, was Sie denken. https://github.com/MarcelloCarreira/sendgrid-csharp-eventwebhook

Es basiert auf der Lösung bei https://sendgrid.com/blog/tracking-email-using-azure-sendgrid-event-webhook-part-1/ aber ich habe auch die Datumskonvertierung vom Zeitstempel hinzugefügt und die Variablen entsprechend aktualisiert aktuelles SendGrid-Modell (und dafür gesorgt, dass Kategorien funktionieren).

Ich habe auch einen Handler mit Basisauthentifizierung als Option erstellt. Siehe die ashx-Dateien und die Beispiele.

Danke dir!

MarcelloCarreira
quelle