Estoy tratando de escribir un JsonConverter personalizado para las geometrías de NetTopologySuite.
Mi modelo:
public class MyModel { public string myName { get; set; } [JsonConverter(typeof(MyPolygonConverter))] public Polygon myPolygon { get; set; } }Mi convertidor:
public class MyPolygonConverter : JsonConverter<Polygon> { public override Polygon ReadJson(JsonReader reader, Type objectType, Polygon existingValue, bool hasExistingValue, JsonSerializer serializer) { var geoJson = (string)reader.Value; // ... not relevant, because reader.Value is null. } }Que estoy haciendo:
var deSerialized = JsonConvert.DeserializeObject<MyModel>("{\"myName\":\"My Name\",\"myPolygon\":{\"type\":\"Polygon\",\"coordinates\":[[[-100.0,45.0],[-98.0,45.0],[-99.0,46.0],[-100.0,45.0]]]}}");Y lo que está pasando:
Se está llamando a mi convertidor, pero reader.Value es nulo.
Los ejemplos que he visto en línea usan (string)reader.Value para acceder a la cadena json que debe convertirse. Pero en mi caso, reader.Value es nulo.
¿Cómo se supone que debo acceder a la cadena JSON que necesito convertir?
Para ser claros, lo que tengo es una clase modelo que contiene una propiedad de una clase, Polygon, que convierto a GeoJson en la serialización que necesito volver a convertir a esa clase, desde GeoJson.
Entonces, mi json inicial es:
{ "myName" : "My Name", "myPolygon" : { "type" : "Polygon", "coordinates" : [ [ [-100.0, 45.0], [-98.0, 45.0], [-99.0, 46.0], [-100.0, 45.0] ] ] } }Y necesito tomar todo lo que es un elemento secundario de "myPolygon" y transferirlo a mi código de conversión. En los ejemplos simples que he visto, reader.Value proporcionó el valor como una cadena. En este caso no es así, muy probablemente porque el hijo de "myPolygon" no es un valor único, sino un objeto json complejo.
Ya tengo un código para analizar el valor cuando se proporciona como una sola cadena json. Entonces, ¿cómo puedo obtener al niño como una sola cadena json?
Creo que tienes una idea errónea sobre cómo funcionan JsonConverters. El lector no te da el JSON sin procesar. En su lugar, le permite recorrer los tokens en el JSON uno por uno.
reader.Value es nulo porque su convertidor está manejando un objeto, no una cadena, y en el momento en que se llama a su convertidor, el lector se coloca en el token StartObject . Debe llamar a reader.Read() en un bucle para avanzar por el objeto y obtener los nombres y valores de las propiedades hasta llegar al token EndObject .
Aquí hay un violín que demuestra ese proceso, aunque en realidad no llena el Polígono. https://dotnetfiddle.net/MQE6N5
Si realmente desea la cadena JSON dentro del convertidor, puede cargar un JObject desde el lector y luego llamar a su método ToString():
public override Polygon ReadJson(JsonReader reader, Type objectType, Polygon existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); string json = jo.ToString(Formatting.None); Polygon poly = ConvertJsonToPolygon(json); // your conversion method return poly; }En mi opinión, una mejor solución es utilizar las instalaciones de JObject para realizar la conversión. Puede seleccionar fácilmente valores del JObject y completar su clase Polygon a partir de él:
public override Polygon ReadJson(JsonReader reader, Type objectType, Polygon existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); Polygon poly = new Polygon(); poly.type = (string)jo["type"]; poly.coordinates = jo["coordinates"].ToObject<double[][][]>(serializer); return poly; }Violín: https://dotnetfiddle.net/OFSP1h
Como señaló Brian Rogers, estaba mirando las cosas incorrectamente.
Pero realmente no quería tener que recorrer el JsonReader, un token a la vez, para construir el objeto json que necesito procesar.
Afortunadamente, no tengo que hacerlo. Json.Net ya proporciona herramientas para hacer esto:
public override Polygon ReadJson(JsonReader reader, Type objectType, Polygon existingValue, bool hasExistingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); var geoJson = jObject.ToString(); // Work with the geoJson string... }Nota: esto funciona cuando el objeto no es una matriz, lo que nunca será en mi caso de uso. Si está haciendo algo donde está, es posible que deba usar JArray en su lugar. Si su caso de uso establece que su objeto puede ser una matriz o un objeto, es posible que pueda distinguir cuál necesita usar al verificar si reader.TokenType == JsonToken.StartObject. No he explorado eso, porque no es posible en mi caso de uso.