Is it possible to deserialize JSON from different web sources with C# derived classes?

40 Views Asked by At

Intent: Receive data in json format, from diferent web sources, returning diferent list of data, and deserializing it.

Almost sure this is a newbie C# question :( No experience with derived classes ... Maybe i'm trying an impossible data manipulation ...

1 - Created an abstract Data class with and abstract index (to access data via named string). Then created 2 derived classes with the fields from each source. No problem so far.

    public abstract class Data
    {
        public abstract object this[string index] { get; set; }
    }
    public class Type1 : Data
    {
        public DateTime DATA_COTA { get; set; }
        public string MOEDABASE { get; set; }
        public override object this[string index]
        {
            get
            {
                switch (index.Substring(1))
                {
                    case "DATA_COTA":
                        return DATA_COTA;

                    case "MOEDABASE":
                        return MOEDABASE;

                    default:
                        throw new InvalidOperationException($"campo {index} inválido");
                }
            }
            set
            {
                ;
            }
        }
    }
    public class Type2 : Data
    {
        public string MOEDA { get; set; }
        public decimal VALFIX { get; set; }
        public override object this[string index]
        {
            get
            {
                switch (index.Substring(1))
                {
                    case "MOEDA":
                        return MOEDA;

                    case "VALFIX":
                        return VALFIX;

                    default:
                        throw new InvalidOperationException($"campo {index} inválido");
                }
            }
            set
            {
                ;
            }
        }
    }

2 - Create an abstract Root class with a common string field (odatacontext) and a list of fields returned from each site. As i dont´mention each type in each class definition, i supose it will never work. I've tried so many variation/tricks, but this is the only version that compiles, but doesn't work :(

    public abstract class Root
    {
        [JsonProperty("@odata.context")]
        public string odatacontext { get; set; }
        public abstract List<Data> value { get; set; }
    }
    public class RootType1 : Root
    {
        public override List<Data> value
        {
            get
            {
                return this.value;
            }
            set
            {
                //List<Type1> value = value.Select(s => (Type1)s).ToList();
                this.value = value;
            }
        }
    }
    public class RootType2 : Root
    {
        public override List<Data> value
        {
            get
            {
                return this.value;
            }
            set
            {
                //List<Type2> value = value.Select(s => (Type2)s).ToList();
                this.value = value;
            }
        }
    }

3 - Finally the code ... getting a stack overflow when refering to obj.value

                           using (HttpClient httpClient = new HttpClient())
                            {
                                HttpResponseMessage httpResponseMessage = httpClient.SendAsync(httpRequestMessage).Result;

                                string json = httpResponseMessage.Content.ReadAsStringAsync().Result;
                                if (!httpResponseMessage.IsSuccessStatusCode)
                                    throw new InvalidOperationException(json);

                                Root obj;
                                switch (name)
                                {
                                    case "Type1":
                                        obj = JsonConvert.DeserializeObject<RootType1>(json);
                                        break;

                                    case "Type2":
                                        obj = JsonConvert.DeserializeObject<RootType2>(json);
                                        break;

                                    default:
                                        throw new InvalidOperationException($"{name} não previsto");
                                }

                                foreach (var item in **obj.value**)
                                {
                                    foreach (SqlParameter param in cmdUPD.Parameters)
                                    {
                                        string paramName = param.ParameterName;
                                        if (name != "@RETURN_VALUE")
                                            cmdUPD.Parameters[paramName].Value = item[paramName];
                                    }

                                    cmdUPD.ExecuteNonQuery();
                                }
                            }

As i've been asked for the json ... then i've to send "the real thing" ... I'm sending the actual solution (1 and 2 ... working, but with duplicated code) ... then the json streams ... and then the intended solution (4 and 5). I'm not showing the derived classes (or any other solution), because i dont know how :( The intent is to have ALL (i've several) the classes Root* to be derived from a Root class, and a indistinct piece of code, only changing the json deserialization.

1 - the ACTUAL classes definition

   public abstract class Data
    {
        public abstract object this[string index] { get; set; }
    }
    public class Câmbios : Data
    {
        public int SGC_ID { get; set; }
        public DateTime DtCâmbio { get; set; }
        public string MdaBase { get; set; }
        public string Mda { get; set; }
        public decimal Fixing { get; set; }
        public override object this[string index]
        {
            get
            {
                switch (index.Substring(1))
                {
                    case "DtCâmbio":
                        return DtCâmbio;

                    case "MdaBase":
                        return MdaBase;

                    case "Mda":
                        return Mda;

                    case "Fixing":
                        return Fixing;

                    default:
                        throw new InvalidOperationException($"campo {index} inválido");
                }
            }
            set
            {
                ;
            }
        }
    }
    public class RootCâmbios
    {
        [JsonProperty("@odata.context")]
        public string odatacontext { get; set; }
        public List<Câmbios> value { get; set; }
    }
   public class Índice : Data
    {
        public int SGC_ID { get; set; }
        public string Indexante { get; set; }
        public DateTime Data { get; set; }
        public decimal Valor { get; set; }
        public string OmitirLista { get; set; }
        public override object this[string index]
        {
            get
            {
                switch (index.Substring(1))
                {
                    case "Indexante":
                        return Indexante;

                    case "Data":
                        return Data;

                    case "Valor":
                        return Valor;

                    case "OmitirLista":
                        return OmitirLista;

                    default:
                        throw new InvalidOperationException($"campo {index} inválido");
                }
            }
            set
            {
                ;
            }
        }
    }
    public class RootÍndices
    {
        [JsonProperty("@odata.context")]
        public string odatacontext { get; set; }
        public List<Índice> value { get; set; }
    }

2 - the ACTUAL program (duplication :()

        switch (name)
        {
            case "Câmbios":
                //RESOLVER ... analisar se há forma de consumir o registo no caso  do odatacontext ser null ... ler Root ???
                //Root obj = JsonConvert.DeserializeObject<RootCâmbios>(json);
                RootCâmbios objCâmbios = JsonConvert.DeserializeObject<RootCâmbios>(json);
                if (!objCâmbios.odatacontext.StartsWith($"{urlBase}/$metadata#{SGC_name}"))
                    throw new InvalidOperationException("falha na validação do contexto");

                foreach (Data item in objCâmbios.value)
                {
                    foreach (SqlParameter param in cmdINS.Parameters)
                    {
                        string paramName = param.ParameterName;
                        if (!(param.ParameterName == "@RETURN_VALUE" || param.ParameterName == "@EXEC_Dia"))
                            cmdINS.Parameters[paramName].Value = item[paramName];
                    }

                    cmdINS.ExecuteNonQuery();
                }
                break;

            case "Índices":
                RootÍndices objÍndices = JsonConvert.DeserializeObject<RootÍndices>(json);
                if (objÍndices.odatacontext != null)
                {
                    if (!objÍndices.odatacontext.StartsWith($"{urlBase}/$metadata#{SGC_name}"))
                        throw new InvalidOperationException("falha na validação do contexto");

                    foreach (Data item in objÍndices.value)
                    {
                        foreach (SqlParameter param in cmdINS.Parameters)
                        {
                            string paramName = param.ParameterName;
                            if (!(param.ParameterName == "@RETURN_VALUE" || param.ParameterName == "@EXEC_Dia"))
                                cmdINS.Parameters[paramName].Value = item[paramName];
                        }

                        cmdINS.ExecuteNonQuery();
                    }
                }
                break;

            default:
                throw new InvalidOperationException($"{name} não previsto");
        }

3 - json data

Câmbios {"@odata.context":"http://gasgc.montepio.com/odatacustom/maintenancedata/$metadata#fixingrates#9ee42a020a03ff123ae687ecfc1ce91ee9e2768e0a1274c518516e62c66fcc175b8adda21cf69cd59a70afcc8a08c334cda15b00f1c78239c8e1430f5f363da3","value":[{"fixingrates_ID":1,"DtC\u00e2mbio":"2023-05-26T00:00:00+01:00","MdaBase":"USD","Mda":"EUR","Fixing":1.075100000000000},{"fixingrates_ID":2,"DtC\u00e2mbio":"2023-05-26T00:00:00+01:00","MdaBase":"GBP","Mda":"EUR","Fixing":0.868130000000000},{"fixingrates_ID":3,"DtC\u00e2mbio":"2023-05-26T00:00:00+01:00","MdaBase":"JPY","Mda":"EUR","Fixing":150.240000000000000},{"fixingrates_ID":4,"DtC\u00e2mbio":"2023-05-26T00:00:00+01:00","MdaBase":"SEK","Mda":"EUR","Fixing":11.528000000000000},{"fixingrates_ID":5,"DtC\u00e2mbio":"2023-05-26T00:00:00+01:00","MdaBase":"CHF","Mda":"EUR","Fixing":0.970700000000000},{"fixingrates_ID":6,"DtC\u00e2mbio":"2023-05-26T00:00:00+01:00","MdaBase":"NOK","Mda":"EUR","Fixing":11.821800000000000},{"fixingrates_ID":7,"DtC\u00e2mbio":"2023-05-26T00:00:00+01:00","MdaBase":"DKK","Mda":"EUR","Fixing":7.448900000000000},{"fixingrates_ID":8,"DtC\u00e2mbio":"2023-05-26T00:00:00+01:00","MdaBase":"BRL","Mda":"EUR","Fixing":5.387800000000000}]}

Índices {"@odata.context":"http://gasgc.montepio.com/odatacustom/maintenancedata/$metadata#interestindexvalues#8f2b6731ceade8d332dec84a53057e906a421270f07a52de17d3fc5e1160c1700c454af1437737f8e674fd151e4df4ad57da7ea78a66d81aa54177609cb7fd77","value":[{"interestindexvalues_ID":1,"Indexante":"77","Data":"2023-05-25T00:00:00+01:00","Valor":3.45700000,"OmitirLista":"N"}]}

4 - the intended Root base class

public abstract class Root
{
    [JsonProperty("@odata.context")]
    public string odatacontext { get; set; }
    public abstract List<Data> value { get; set; }
}

5 - the intended code

    Root obj;
    switch (name)
    {
        case "Câmbios":
            obj = JsonConvert.DeserializeObject<RootCâmbios>(json);
            break;

        case "Índices":
            obj = JsonConvert.DeserializeObject<RootÍndices>(json);
            break;

        default:
            throw new InvalidOperationException($"{name} não previsto");
    }
    if (!obj.odatacontext.StartsWith($"{urlBase}/$metadata#{SGC_name}"))
          throw new InvalidOperationException("falha na validação do contexto");

    foreach (Data item in obj.value)
    {
         foreach (SqlParameter param in cmdINS.Parameters)
         {
               string paramName = param.ParameterName;
               if (!(param.ParameterName == "@RETURN_VALUE" || param.ParameterName == "@EXEC_Dia"))
                    cmdINS.Parameters[paramName].Value = item[paramName];
         }

         cmdINS.ExecuteNonQuery();
      }
0

There are 0 best solutions below