1

I'm trying to deserialize json result to wanted object. The result I'm getting is:

{
    "base": "EUR",
    "date": "2017-06-30",
    "rates": {
        "AUD": 1.4851,
        "BGN": 1.9558,
        "BRL": 3.76,
        "CAD": 1.4785
    }
}

I want this result to deserialize to my object:

public class ExchangeRates
{
    public string Base { get; set; }

    public DateTime Date { get; set; }

    public IList<Rates> Rates { get; set; }
}

public class Rates
{
    public string Name { get; set; }

    public decimal Value { get; set; }
}

my deserialization looks like this:

 using (var httpClient = new HttpClient())
 {
     var response = httpClient.GetAsync("http://api.fixer.io/latest").Result;
     var result = response.Content.ReadAsStringAsync().Result;
     var values = JsonConvert.DeserializeObject<ExchangeRates>(result);
 }

When I run the program I get following exception:

Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[ConsoleApp4.Rates]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'rates.AUD', line 1, position 49.'

How can I deserialize JSON to my wanted object??

UPDATE 1

Or maybe I can just deserialize 'rates' list?

6
  • 3
    Basically, you don't have a List<Rates> - you have a Dictionary<string, decimal> Commented Jul 3, 2017 at 8:32
  • @JonSkeet Yeah I saw that using Dictionary<string, decimal> works fine, but after deserialization I need to map that dictionary to list, because all of those exchange rates I need to put to DB, so I thought maybe I can deserialize JSON to my wanted object so I don't need to map Commented Jul 3, 2017 at 8:34
  • 2
    There's nothing about them being in a dictionary which would stop you putting them into a Db Commented Jul 3, 2017 at 8:36
  • 3
    And assuming you don't care about the order, you can easily create a list: rateDictionary.Select(kvp => new Rates { Name = kvp.Key, Value = kvp.Value }).ToList() Commented Jul 3, 2017 at 8:39
  • Can you change the Json? If not, a Dictionary is the way to go Commented Jul 3, 2017 at 8:47

2 Answers 2

2

Take a look at your JSON, specifically rates:

"rates": {
    "AUD": 1.4851,
    "BGN": 1.9558,
    "BRL": 3.76,
    "CAD": 1.4785
}

This is very clearly a JSON object, as it has key-value pairs. However, looking at your code, you have defined the corresponding property (Rates) as an IList:

public IList<Rates> Rates { get; set; }

I understand your reasoning behind defining the Rates class. You think that by defining that class, NewtonSoft will deserialize rates the way you want it to. However, this is impossible because rates is not an array, and therefore deserializing it into any kind of IList is impossible.

The easiest and most clear cut solution is to use a dictionary:

public Dictionary<string, decimal> Rates { get; set; }

However, if you don't want to use a dictionary, you need to modify your JSON like so and your solution will work:

"rates":[  
  {  
     "Name":"AUD",
     "Value":1.4851
  },
  {  
     "Name":"BGN",
     "Value":1.9558
  },
  {  
     "Name":"BRL",
     "Value":3.76
  },
  {  
     "Name":"CAD",
     "Value":1.4785
  }
]

By converting rates to an array, and making its contents objects instead of key-value pairs, NewtonSoft can deserialize rates as a list, and its contents as instances of the Rates class.

Sign up to request clarification or add additional context in comments.

4 Comments

That second piece of JSON isn't valid - you'd need each element to be an object.
Fiddeling around with the json document is a lot more of work and error prone.
@SirRufo True, but OP mentioned they didn't want to use a dictionary.
OP specified decimal in rate object, which makes sense for currency, suggested dictionary in this reply is float. Just saying :)
0

I agree with the other guys comments: you should use a Dictionary. To achieve the conversion to your final object structure you can use for example an intermediary class with an explicit cast operator.

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

public class Program
{
    public void Main()
    {
        var result = @"{
        ""base"": ""EUR"",
        ""date"": ""2017-06-30"",
        ""rates"": {
            ""AUD"": 1.4851,
            ""BGN"": 1.9558,
            ""BRL"": 3.76,
            ""CAD"": 1.4785
        }}";

        var values = (ExchangeRates) JsonConvert.DeserializeObject<TempExchangeRates>(result);


        Console.WriteLine(values.Base);
        Console.WriteLine(values.Date);

        foreach(var rate in values.Rates)
            Console.WriteLine(rate.Name + ": " + rate.
    }
}

public class TempExchangeRates
{
    public string Base { get; set; }
    public DateTime Date { get; set; }
    public Dictionary<string,decimal> Rates { get; set; }
    public static explicit operator ExchangeRates(TempExchangeRates tmpRates)
    {
        var xRate = new ExchangeRates();

        xRate.Base = tmpRates.Base;
        xRate.Date = tmpRates.Date;
        xRate.Rates = new List<Rates>();

        foreach(var de in tmpRates.Rates)
            xRate.Rates.Add(new Rates{Name = de.Key, Value = de.Value});

        return xRate;
    }
}


public class ExchangeRates
{
    public string Base { get; set; }
    public DateTime Date { get; set; }
    public IList<Rates> Rates { get; set; }
}

public class Rates
{
    public string Name { get; set; }
    public decimal Value { get; set; }
}

1 Comment

The end of Main() is unfinished: Console.WriteLine(rate.Name + ": " + rate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.