web-dev-qa-db-fra.com

Désérialisation des résultats Elasticsearch via JSON.NET

J'ai une application .NET que je souhaite utiliser pour interroger Elasticsearch. J'interroge avec succès mon index Elasticsearch. Le résultat ressemble à ceci:

{
  "took":31,
  "timed_out":false,
  "_shards": {
    "total":91,
    "successful":91,
    "skipped":0,
    "failed":0
  },
  "hits":{
    "total":1,
    "max_score":1.0,
    "hits":[
      {
        "_index":"my-index",
        "_type":"doc",
        "_id":"TrxrZGYQRaDom5XaZp23",
        "_score":1.0,
        "_source":{
          "my_id":"65a107ed-7325-342d-adab-21fec0a97858",
          "Host":"something",
          "Zip":"12345"
        }
      },
    ]
  }
}

Pour le moment, ces données sont disponibles via la propriété Body sur la StringResponse Je reviens d'Elasticsearch. Je souhaite désérialiser les enregistrements réels (je ne veux pas ou n'ai pas besoin des took, timed_out, etc.) dans un objet C # nommé results. Pour tenter de le faire, j'ai:

var results = JsonConvert.DeserializeObject<List<Result>>(response.Body);

La classe Result ressemble à ceci:

public class Result
{
  [JsonProperty(PropertyName = "my_id")]
  public string Id { get; set; }

  [JsonProperty(PropertyName = "Host")]
  public string Host { get; set; }

  [JsonProperty(PropertyName = "Zip")]
  public string PostalCode { get; set; }
}

Lorsque j'exécute cela, j'obtiens l'erreur suivante:

Impossible de désérialiser l'objet JSON en cours dans le type 'System.Collections.Generic.List`1 [Résultat]' car le type nécessite un tableau JSON pour désérialiser correctement.

Bien que l'erreur soit logique, je ne sais pas comment analyser le hits pour simplement extraire le _source Les données. Le _source La propriété contient les données que je veux désérialiser. Tout le reste n'est que des métadonnées dont je me fiche.

Y a-t-il un moyen de faire cela? Si c'est le cas, comment?

19
user687554

Vous pouvez utiliser l'API LINQ-to-JSON de Json.Net pour obtenir uniquement les nœuds qui vous intéressent, puis les convertir en une liste de résultats:

var results = JToken.Parse(response.Body)
                    .SelectTokens("hits.hits[*]._source")
                    .Select(t => t.ToObject<Result>())
                    .ToList();

Démo de travail: https://dotnetfiddle.net/OkEpPA

12
Brian Rogers

Eh bien, vous êtes DeserializeObject<T>T ne correspond pas au Json. Votre Json commence par un { donc votre T doit être une classe (pas un type IEnumerable).

Commençons dehors et travaillons à l'intérieur:

{
  "took":31,
  "timed_out":false,
  "_shards": <object>
  "hits": <object>
}

donc:

public class SearchResult
{
  [JsonProperty("took")]
  public int Took { get; set; }
  [JsonProperty("timed_out")]
  public bool TimedOut { get; set; }
  [JsonProperty("_shards")]
  public Shards Shards { get; set; }
  [JsonProperty("hits")]
  public Hits Hits { get; set; }
}

est ensuite _shards

"_shards": {
  "total":91,
  "successful":91,
  "skipped":0,
  "failed":0
},

donc

public class Shards 
{
  [JsonProperty("total")]
  public int Total { get; set; }
  // etc...
}

Puis hits

{
  "total":1,
  "max_score":1.0,
  "hits": <IEnumerable because []>
}

donc

public class Hits
{
  [JsonProperty("total")]
  public int Total { get; set; }
  [JsonProperty("max_score")]
  public int MaxScore { get; set; }
  [JsonProperty("hits")]
  public List<Hit> Hits { get; set; }
}

puis Hits liste:

{
    "_index":"my-index",
    "_type":"doc",
    "_id":"TrxrZGYQRaDom5XaZp23",
    "_score":1.0,
    "_source":  <object>
},

donc

public class Hit
{
  [JsonProperty("_index")]
  public string Index { get; set; }
  // etc
}

Et une fois que vous avez créé tous ceux dont vous avez besoin, vous désérialisez:

JsonConvert.DeserializeObject<SearchResult>(json);
8
Erik Philips

Vous devrez d'abord désérialiser en un JToken ou JObject générique, comme ceci:

var token = JsonConvert.DeserializeObject<JToken>(jsonString);

Et puis vous pouvez accéder à la propriété _ source qui contient les données de votre intérêt:

var hitsArray = token["hits"]["hits"] as JArray;
var result = hitsArray[0]["_source"].ToObject<Result>();
5
thepirat000

essayez la structure suivante générée par la fonction de collage spécial VS:

    public class Rootobject
{
    public int took { get; set; }
    public bool timed_out { get; set; }
    public _Shards _shards { get; set; }
    public Hits hits { get; set; }
}

public class _Shards
{
    public int total { get; set; }
    public int successful { get; set; }
    public int skipped { get; set; }
    public int failed { get; set; }
}

public class Hits
{
    public int total { get; set; }
    public float max_score { get; set; }
    public Hit[] hits { get; set; }
}

public class Hit
{
    public string _index { get; set; }
    public string _type { get; set; }
    public string _id { get; set; }
    public float _score { get; set; }
    public _Source _source { get; set; }
}

public class _Source
{
    public string my_id { get; set; }
    public string Host { get; set; }
    public string Zip { get; set; }
}
2
MoeZir

j'ai utilisé http://json2csharp.com/ pour convertir un json en classes c # et pour mon test, j'ai obtenu une chaîne json à partir de la conversion effectuée sur http://easyonlineconverter.com/converters /dot-net-string-escape.html

alors j'ai créé une application console avec cette classe:

using System.Collections.Generic;
using Newtonsoft.Json;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string json = "{  \"took\":31,  \"timed_out\":false,  \"_shards\": {    \"total\":91,    \"successful\":91,    \"skipped\":0,    \"failed\":0  },  \"hits\":{    \"total\":1,    \"max_score\":1.0,    \"hits\":[      {        \"_index\":\"my-index\",        \"_type\":\"doc\",        \"_id\":\"TrxrZGYQRaDom5XaZp23\",        \"_score\":1.0,        \"_source\":{          \"my_id\":\"65a107ed-7325-342d-adab-21fec0a97858\",          \"Host\":\"something\",          \"Zip\":\"12345\"        }      },    ]  }}";
            RootObject t = JsonConvert.DeserializeObject<RootObject>(json);
        }

        public class Shards
        {
            public int total { get; set; }
            public int successful { get; set; }
            public int skipped { get; set; }
            public int failed { get; set; }
        }

        public class Source
        {
            public string my_id { get; set; }
            public string Host { get; set; }
            public string Zip { get; set; }
        }

        public class Hit
        {
            public string _index { get; set; }
            public string _type { get; set; }
            public string _id { get; set; }
            public double _score { get; set; }
            public Source _source { get; set; }
        }

        public class Hits
        {
            public int total { get; set; }
            public double max_score { get; set; }
            public List<Hit> hits { get; set; }
        }

        public class RootObject
        {
            public int took { get; set; }
            public bool timed_out { get; set; }
            public Shards _shards { get; set; }
            public Hits hits { get; set; }
        }
    }
}

j'espère que cela t'aides

2
Rena