La API volvió a ajustar el objeto json como debajo de 2 formularios.
Formulario 1
{ "Pricing": [ { "total": 27, "currency": "USD", "charges": [ //Chargers Array { "code": "C1", "currency": "USD", "rate": 15 }, { "code": "C45", "currency": "USD", "rate": 12 } ] } ] }Formulario 2
{ "Pricing": [ { "total": 12, "currency": "USD", "charges": { //Chargers single object "code": "C1", "currency": "USD", "rate": 12 } } ] }Como puede ver, a veces los objetos de los cargadores regresan con la matriz y otras veces no. Mi pregunta es ¿cómo analizar esto en un objeto de clase C#? Si agregué la clase C # como se muestra a continuación, no se puede analizar correctamente para el Formulario 2. (El formulario 1 se analiza correctamente)
public class Charge { public string code { get; set; } public string currency { get; set; } public decimal rate { get; set; } } public class Pricing { public decimal total { get; set; } public string currency { get; set; } public List<Charge> charges { get; set; } //In Form 2, this should be single object } public class MainObj { public List<Pricing> Pricing { get; set; } }Se produjo un error al analizar con la deserialización de Newtonsoft.
MainObj obj = JsonConvert.DeserializeObject<MainObj>(json);Error
No se puede deserializar el objeto JSON actual (por ejemplo, {"nombre":"valor"}) en el tipo 'System.Collections.Generic.List`1[Charge]' porque el tipo requiere una matriz JSON (por ejemplo, [1,2,3] ) para deserializar correctamente. Para corregir este error, cambie el JSON a una matriz JSON (por ejemplo, [1,2,3]) o cambie el tipo deserializado para que sea un tipo .NET normal (por ejemplo, no un tipo primitivo como un entero, no un tipo de colección como una matriz o lista) que se puede deserializar desde un objeto JSON. JsonObjectAttribute también se puede agregar al tipo para obligarlo a deserializarse de un objeto JSON. Ruta 'Pricing[0].charges.code', línea 1, posición 69.
¿Algún método común para analizar, al recibir diferentes tipos de objetos con C#?
(También analicé esto , pero es para Java. Y la mayoría de este tipo de preguntas se plantearon para Java pero no para C#).
Podría optar por un enfoque para intentar analizar el objeto de Form1 en json, si falla, usará el objeto de Form2 en json.
Ejemplo aquí y abajo: https://dotnetfiddle.net/F1Yh25
using System; using System.Collections.Generic; using Newtonsoft.Json; public class Program { public static void Main() { string form1 = "{\"Pricing\":[{\"total\":27,\"currency\":\"USD\",\"charges\":[{\"code\":\"C1\",\"currency\":\"USD\",\"rate\":15},{\"code\":\"C45\",\"currency\":\"USD\",\"rate\":12}]}]}"; string form2 = "{\"Pricing\":[{\"total\":12,\"currency\":\"USD\",\"charges\":{\"code\":\"C1\",\"currency\":\"USD\",\"rate\":12}}]}"; string json = form1;//Change form1 to form2 and it will also work try { Form1.Root Object = JsonConvert.DeserializeObject<Form1.Root>(json); Console.WriteLine("Item 1's Pricing: " + Object.Pricing[0].total); } catch//If Form1's json is Form2 it will catch here { Form2.Root Object = JsonConvert.DeserializeObject<Form2.Root>(json); Console.WriteLine("Item 1's Pricing: " + Object.Pricing[0].total); } } public class Form1 { public class Charge { public string code { get; set; } public string currency { get; set; } public int rate { get; set; } } public class Pricing { public int total { get; set; } public string currency { get; set; } public List<Charge> charges { get; set; } } public class Root { public List<Pricing> Pricing { get; set; } } } public class Form2 { public class Charges { public string code { get; set; } public string currency { get; set; } public int rate { get; set; } } public class Pricing { public int total { get; set; } public string currency { get; set; } public Charges charges { get; set; } } public class Root { public List<Pricing> Pricing { get; set; } } } }Ok, aquí hay otra forma de hacer esto sin tener que usar dos clases y sin tener que intentar atrapar. Básicamente, solo actualice la clase de precios a la siguiente y funciona para ambos casos. Probablemente sea una mejor manera, pero esto es mejor (al menos en mi opinión) y tener dos clases y tratar de atrapar tu bifurcación. Si tuviera diez propiedades con este problema, ¿crearía 10? clases para manejar cada combo? De ninguna manera jaja!
public class Pricing { public int total { get; set; } public string currency { get; set; } private List<Charge> _charges; public object charges { get { return _charges; } set { if(value.GetType().Name == "JArray") { _charges = JsonConvert.DeserializeObject<List<Charge>>(value.ToString()); } else { _charges = new List<Charge>(); var charge = JsonConvert.DeserializeObject<Charge>(value.ToString()); _charges.Add(charge); } } } }Otra forma de lidiar con este problema es definir un JsonConverter personalizado que pueda manejar ambos casos.
class ArrayOrObjectConverter<T> : JsonConverter { public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var token = JToken.Load(reader); return token.Type == JTokenType.Array ? token.ToObject<List<T>>() : new List<T> { token.ToObject<T>() }; } public override bool CanConvert(Type objectType) => objectType == typeof(List<T>); public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) =>throw new NotImplementedException(); }ReadJson primero obtenemos un JToken para poder determinar el Type (tipo) del valor de lecturaToObject<List<T>> o ToObject<T>CanConvert , examinamos que el tipo de propiedad que se va a completar es List<T>JsonConverter<T> genérico en el que no tiene que definir el CanConvert , su ReadJson se puede implementar de una manera un poco más complicada.WriteJsonCanWrite de la clase base para que siempre devuelva false Con esta clase en nuestra mano, puede decorar sus propiedades con un JsonConverterAttribute para decirle a Json.NET cómo tratar con esas propiedades.
public class Pricing { public decimal total { get; set; } public string currency { get; set; } [JsonConverter(typeof(ArrayOrObjectConverter<Charge>))] public List<Charge> charges { get; set; } ... }