JSON-Objektarray mit Json.net deserialisieren

116

Ich versuche, eine API zu verwenden, die die folgende Beispielstruktur für ihren zurückgegebenen JSON verwendet

[
   {
      "customer":{
         "first_name":"Test",
         "last_name":"Account",
         "email":"[email protected]",
         "organization":"",
         "reference":null,
         "id":3545134,
         "created_at":"2013-08-06T15:51:15-04:00",
         "updated_at":"2013-08-06T15:51:15-04:00",
         "address":"",
         "address_2":"",
         "city":"",
         "state":"",
         "zip":"",
         "country":"",
         "phone":""
      }
   },
   {
      "customer":{
         "first_name":"Test",
         "last_name":"Account2",
         "email":"[email protected]",
         "organization":"",
         "reference":null,
         "id":3570462,
         "created_at":"2013-08-12T11:54:58-04:00",
         "updated_at":"2013-08-12T11:54:58-04:00",
         "address":"",
         "address_2":"",
         "city":"",
         "state":"",
         "zip":"",
         "country":"",
         "phone":""
      }
   }
]

JSON.net würde mit so etwas wie der folgenden Struktur großartig funktionieren

{
    "customer": {
        ["field1" : "value", etc...],
        ["field1" : "value", etc...],
    }
}

Aber ich kann nicht herausfinden, wie ich es schaffen kann, mit der bereitgestellten Struktur zufrieden zu sein.

Bei Verwendung des Standardwerts JsonConvert.DeserializeObject (Inhalt) wird die richtige Anzahl von Kunden ermittelt, aber alle Daten sind null.

Wenn Sie eine Kundenliste (unten) ausführen, wird die Ausnahme "Das aktuelle JSON-Array kann nicht deserialisiert werden" ausgelöst

public class CustomerList
{
    public List<Customer> customer { get; set; }
}

Gedanken?

Shawn C.
quelle
Beantwortet das deine Frage? Deserialisieren Sie JSON mit C #
GetFookedWeeb

Antworten:

185

Sie können ein neues Modell erstellen, um Ihren Json zu deserialisieren CustomerJson:

public class CustomerJson
{
    [JsonProperty("customer")]
    public Customer Customer { get; set; }
}

public class Customer
{
    [JsonProperty("first_name")]
    public string Firstname { get; set; }

    [JsonProperty("last_name")]
    public string Lastname { get; set; }

    ...
}

Und Sie können Ihren JSON einfach deserialisieren:

JsonConvert.DeserializeObject<List<CustomerJson>>(json);

Ich hoffe es hilft !

Dokumentation: Serialisieren und Deserialisieren von JSON

Joffrey Kern
quelle
1
Vielen Dank. War über das Thema nachgedacht. Als Sie zuerst geantwortet haben, wurde Ihre Antwort akzeptiert.
Shawn C.
2
JsonConvert.DeserializeObject <List <CustomerJson >> (json); Funktioniert perfekt für String-Eingaben.
Markel Mairs
DeserializeObject()ist langsam auf Android-Handys mit ARM. Gibt es eine bessere Lösung für diesen Fall?
Tadej
1
Versuchen Sie, mit einem JObject zu navigierenJObject.Parse(json);
Joffrey Kern
47

Verwenden Sie für diejenigen, die keine Modelle erstellen möchten, den folgenden Code:

var result = JsonConvert.DeserializeObject<
  List<Dictionary<string, 
    Dictionary<string, string>>>>(content);

Hinweis: Dies funktioniert nicht für Ihre JSON-Zeichenfolge. Dies ist keine allgemeine Lösung für eine JSON-Struktur.

Tyler Long
quelle
10
Dies ist eine schreckliche Lösung. Wenn Sie stattdessen keine Modelle erstellen möchten, verwenden Sie var result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);
stattdessen
1
@ a11smiles Bitte erklären Sie, warum es eine schreckliche Lösung ist.
Tyler Long
2
Erstens unnötige Speicherzuweisung für die verschiedenen Arten von IEnumerableImplementierungen (3 im Vergleich zu einer Liste <Tupel>). Zweitens impliziert Ihre Lösung zwei unterschiedliche Schlüssel - 1 für jedes Wörterbuch. Was passiert, wenn mehrere Kunden denselben Vornamen haben? Es würde keine Unterscheidung bei den Schlüsseln geben. Ihre Lösung berücksichtigt diesen Konflikt nicht.
a11smiles
2
@ a11smiles Jeder Kunde ist ein separates Wörterbuch. Es gibt also kein Problem, auch wenn mehrere Kunden denselben Vornamen haben.
Tyler Long
1
@ a11smiles Ich frage mich, warum hast du gedacht, var result = JsonConvert.DeserializeObject<Tuple<string, string, string>>(content);würde funktionieren. Anscheinend funktioniert es nicht
Tyler Long
1

Wenn Sie die akzeptierte Antwort verwenden, müssen Sie mit jedem auf jeden Datensatz zugreifen Customers[i].customer, und Sie benötigen eine zusätzliche CustomerJsonKlasse, was etwas ärgerlich ist. Wenn Sie das nicht möchten, können Sie Folgendes verwenden:

public class CustomerList
{
    [JsonConverter(typeof(MyListConverter))]
    public List<Customer> customer { get; set; }
}

Beachten Sie, dass ich List<>ein Array und kein Array verwende. Erstellen Sie nun die folgende Klasse:

class MyListConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        var list = Activator.CreateInstance(objectType) as System.Collections.IList;
        var itemType = objectType.GenericTypeArguments[0];
        foreach (var child in token.Values())
        {
            var childToken = child.Children().First();
            var newObject = Activator.CreateInstance(itemType);
            serializer.Populate(childToken.CreateReader(), newObject);
            list.Add(newObject);
        }
        return list;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
    }
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
AlexDev
quelle
1

Leichte Änderung an dem, was oben angegeben wurde. Mein Json-Format, das validiert wurde, war

{
    mycollection:{[
           {   
               property0:value,
               property1:value,
             },
             {   
               property0:value,
               property1:value,
             }
           ]

         }
       }

Mit der Antwort von AlexDev habe ich dieses Looping für jedes Kind durchgeführt und daraus einen Leser erstellt

 public partial class myModel
{
    public static List<myModel> FromJson(string json) => JsonConvert.DeserializeObject<myModelList>(json, Converter.Settings).model;
}

 public class myModelList {
    [JsonConverter(typeof(myModelConverter))]
    public List<myModel> model { get; set; }

}

class myModelConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        var list = Activator.CreateInstance(objectType) as System.Collections.IList;
        var itemType = objectType.GenericTypeArguments[0];
        foreach (var child in token.Children())  //mod here
        {
            var newObject = Activator.CreateInstance(itemType);
            serializer.Populate(child.CreateReader(), newObject); //mod here
            list.Add(newObject);
        }
        return list;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
    }
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();

}
JC_VA
quelle
0

Weitere Modifikationen von JC_VA, nehmen Sie, was er hat, und ersetzen Sie den MyModelConverter durch ...

public class MyModelConverter : JsonConverter
{
    //objectType is the type as specified for List<myModel> (i.e. myModel)
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader); //json from myModelList > model
        var list = Activator.CreateInstance(objectType) as System.Collections.IList; // new list to return
        var itemType = objectType.GenericTypeArguments[0]; // type of the list (myModel)
        if (token.Type.ToString() == "Object") //Object
        {
            var child = token.Children();
            var newObject = Activator.CreateInstance(itemType);
            serializer.Populate(token.CreateReader(), newObject);
            list.Add(newObject);
        }
        else //Array
        {
            foreach (var child in token.Children())
            {
                var newObject = Activator.CreateInstance(itemType);
                serializer.Populate(child.CreateReader(), newObject);
                list.Add(newObject);
            }
        }
        return list;

    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
    }
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

Dies sollte für json funktionieren

myModelList{
 model: [{ ... object ... }]
}

oder

myModelList{
 model: { ... object ... }
}

Sie werden beide analysiert, als ob sie es wären

myModelList{
 model: [{ ... object ... }]
}
andmar8
quelle