c# - Deserialización de los resultados de Elasticsearch a través de JSON.NET
(5)
Bueno, eres DeserializeObject<T>
T
no coincide con el Json. Su Json comienza con un {
por lo que su T debe ser una clase (no un tipo IEnumerable
).
Empecemos afuera y trabajemos en nuestro camino en:
{
"took":31,
"timed_out":false,
"_shards": <object>
"hits": <object>
}
asi que:
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; }
}
el siguiente es
"_shards": {
"total":91,
"successful":91,
"skipped":0,
"failed":0
},
asi que
public class Shards
{
[JsonProperty("total")]
public int Total { get; set; }
// etc...
}
Entonces hits
{
"total":1,
"max_score":1.0,
"hits": <IEnumerable because []>
}
asi que
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; }
}
entonces Hits
lista:
{
"_index":"my-index",
"_type":"doc",
"_id":"TrxrZGYQRaDom5XaZp23",
"_score":1.0,
"_source": <object>
},
asi que
public class Hit
{
[JsonProperty("_index")]
public string Index { get; set; }
// etc
}
Y una vez que haya creado todos los que necesita, deserialice:
JsonConvert.DeserializeObject<SearchResult>(json);
Tengo una aplicación .NET que quiero usar para consultar Elasticsearch. Estoy consultando con éxito mi índice de Elasticsearch. El resultado se parece a esto:
{
"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"
}
},
]
}
}
En este momento, estos datos están disponibles a través de la propiedad Body
en el StringResponse
que estoy recibiendo de Elasticsearch. Quiero deserializar los registros reales (no quiero ni necesito las propiedades timed_out
, timed_out
, etc.) en un objeto C # denominado results
. En un intento de hacer esto, tengo:
var results = JsonConvert.DeserializeObject<List<Result>>(response.Body);
La clase de Result
ve así:
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; }
}
Cuando ejecuto esto, me sale el siguiente error:
No se puede deserializar el objeto JSON actual en el tipo ''System.Collections.Generic.List`1 [Result]'' porque el tipo requiere una matriz JSON para deserializar correctamente.
Si bien el error tiene sentido, no sé cómo analizar los hits
para simplemente extraer los datos de _source
. La propiedad _source
contiene los datos que quiero deserializar. Todo lo demás son solo metadatos que no me importan.
¿Hay alguna forma de hacer esto? ¿Si es así, cómo?
Primero deberá deserializar a un JToken
genérico o un JToken
JObject
, como este:
var token = JsonConvert.DeserializeObject<JToken>(jsonString);
Y luego puede navegar a la propiedad _source que contiene los datos de su interés:
var hitsArray = token["hits"]["hits"] as JArray;
var result = hitsArray[0]["_source"].ToObject<Result>();
Prueba la siguiente estructura generada por la función de pegado especial de 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; }
}
Puede usar la API LINQ-to-JSON de Json.Net para obtener solo los nodos en los que está interesado y luego convertirlos en una lista de resultados:
var results = JToken.Parse(response.Body)
.SelectTokens("hits.hits[*]._source")
.Select(t => t.ToObject<Result>())
.ToList();
Demostración de trabajo: https://dotnetfiddle.net/OkEpPA
http://json2csharp.com/ para convertir un json a clases c # y para mi prueba obtuve una cadena json de la conversión realizada en http://easyonlineconverter.com/converters/dot-net-string-escape.html
entonces he creado una aplicación de consola con esta clase:
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; }
}
}
}
espero que esto ayude