I am trying to deserialize a json string from an api.
All works except for a nested item - Location which always ends up null
This is the string structure:
[
{
"CompanyProfile": "<p>A&B Inc etc...</p>",
"Address": "56 Test Street, Test, UK",
"Location": {
"Latitude": 61.52787,
"Longitude": -4.32095,
"Zoom": 13,
"Icon": null,
"Format": 0,
"Api": null,
"SearchTyped": null
},
"Id": 1723,
"Name": "A&B Inc"
},
{
"CompanyProfile": "<p>B&C Inc etc...</p>",
"Address": "57 Test Street, Test, UK",
"Location": {
"Latitude": 61.2122,
"Longitude": -4.31111,
"Zoom": 13,
"Icon": null,
"Format": 0,
"Api": null,
"SearchTyped": null
},
"Id": 1723,
"Name": "B&C Inc"
},
]
These are the classes to map to:
public class MemberDto
{
public int Id { get; set; }
public string? Name { get; set; }
public string? CompanyProfile { get; set; }
public string? Address { get; set; }
public Location? Location { get; internal set; }
}
public class Location
{
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }
}
This is the deserialize code:
var result = await response.Content.ReadAsStringAsync();
var members = JsonConvert.DeserializeObject<List<MemberDto>>(result);
I know I can use ReadFromJsonAsync<List<MemberDto>>() as well but using ReadFromString so I can check the json before deserializing. Anyway the result is exactly the same for ReadFromJsonAsync.
Everything except Location is deserialized successfully
Anyone know what the issue is?
Remove the internal access modifier in the setter of Location.
public Location? Location { get; set; }
Related
I want to deserialize this json to a List of Product objects but i get this error:
Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[ShoppingList.Product]' 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.
This is my code:
{
"records": [
{
"id": "60",
"name": "Rolex Watch",
"description": "Luxury watch.",
"price": "25000",
"category_id": "1",
"category_name": "Fashion"
},
{
"id": "48",
"name": "Bristol Shoes",
"description": "Awesome shoes.",
"price": "999",
"category_id": "5",
"category_name": "Movies"
},
{
"id": "42",
"name": "Nike Shoes for Men",
"description": "Nike Shoes",
"price": "12999",
"category_id": "3",
"category_name": "Motors"
}
]
}
public class Product
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public decimal price { get; set; }
public int category_id { get; set; }
public string category_name { get; set; }
}
public async Task<List<Product>> GetProductsAsync()
{
Products = new List<Product>();
var uri = new Uri("https://hostname/api/product/read.php");
try
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
Products = JsonConvert.DeserializeObject<List<Product>>(content);
}
}
catch (Exception )
{
throw;
}
return Products;
}
Your Json is not a List<Product>, its an object with a single property called records which is a List<Product>.
So your actual C# model is this:
public class RootObject
{
public List<Product> records { get; set; }
}
And you deserialize like this:
RootObject productsRoot = JsonConvert.DeserializeObject<RootObject>(content);
The issue is that your response JSON you've provided is an object which contains a list, but you are trying to deserialize the data into a straight list. I think all you need to do is add a second class which contains the list into your code, then deserialize using that list.
public class Product
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public decimal price { get; set; }
public int category_id { get; set; }
public string category_name { get; set; }
}
public class RootProduct
{
public List<Product> Products {get;set;}
}
Then you change your deserialization portion from List to
RootProduct response = JsonConvert.DeserializeObject<RootProduct>(content);
The usage would then simply be products. response.Products to access the list of items.
The problem is your json structure. Below is an example of a json array - notice i removed the property "records".
[
{
"id": "60",
"name": "Rolex Watch",
"description": "Luxury watch.",
"price": "25000",
"category_id": "1",
"category_name": "Fashion"
},
{
"id": "48",
"name": "Bristol Shoes",
"description": "Awesome shoes.",
"price": "999",
"category_id": "5",
"category_name": "Movies"
},
{
"id": "42",
"name": "Nike Shoes for Men",
"description": "Nike Shoes",
"price": "12999",
"category_id": "3",
"category_name": "Motors"
}
]
Given this data
[
{
"id": "60",
"name": "Rolex Watch",
"description": "Luxury watch.",
"price": "25000",
"category_id": "1",
"category_name": "Fashion"
},
{
"id": "48",
"name": "Bristol Shoes",
"description": "Awesome shoes.",
"price": "999",
"category_id": "5",
"category_name": "Movies"
},
{
"id": "42",
"name": "Nike Shoes for Men",
"description": "Nike Shoes",
"price": "12999",
"category_id": "3",
"category_name": "Motors"
}
]
And this class definition
public class Order
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public string price { get; set; }
public string category_id { get; set; }
public string category_name { get; set; }
}
The code to convert it from JSON to a C# List<Order> is:
public static List<Order> Map(string json)
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<List<Order>>(json);
}
I'm calling the ethplorer.io api, and it returns the below json. I have generated the classes in visual studio via 'paste special -> paste json as classes'. My problem is that Tokeninfo declares price as an object, this is because it can either be false if it has no price information, or a dictionary if it has values. While I have successfully deserialised the response using JsonConvert.DeserializeObject(rawJSON), I’m struggling to convert price in to c# dictionary if it has values.
public class Tokeninfo
{
public string address { get; set; }
public string name { get; set; }
public object decimals { get; set; }
public string symbol { get; set; }
public string totalSupply { get; set; }
public string owner { get; set; }
public long lastUpdated { get; set; }
public int issuancesCount { get; set; }
public int holdersCount { get; set; }
public object price { get; set; }
public string description { get; set; }
public float totalIn { get; set; }
public float totalOut { get; set; }
}
JSON response:
{
"address": "0xd8f41f341afe2c411b21b3f96263c6584b69baeb", //Not my address
"ETH": {
"balance": 762.13611095505,
"totalIn": 1040.0907032491,
"totalOut": 277.954592294
},
"countTxs": 22,
"tokens": [
{
"tokenInfo": {
"address": "0x355a458d555151d3b27f94227960ade1504e526a",
"name": "StockChain Coin",
"decimals": "18",
"symbol": "SCC",
"totalSupply": "10000000000000000000000000000",
"owner": "0x",
"lastUpdated": 1524401998,
"issuancesCount": 0,
"holdersCount": 86520,
"price": {
"rate": "0.0531126",
"diff": 4.8,
"diff7d": 19.82,
"ts": "1524400762",
"marketCapUsd": null,
"availableSupply": null,
"volume24h": "622004.0",
"currency": "USD"
}
},
"balance": 5000000000000000000,
"totalIn": 0,
"totalOut": 0
},
{
"tokenInfo": {
"address": "0xb679afd97bcbc7448c1b327795c3ef226b39f0e9",
"name": "Win Last Mile",
"decimals": "6",
"symbol": "WLM",
"totalSupply": "2000000000000000",
"owner": "0x8e7a75d5e7efe2981ac06a2c6d4ca8a987a44492",
"lastUpdated": 1524362946,
"issuancesCount": 0,
"holdersCount": 10945,
"price": false
},
"balance": 66000000,
"totalIn": 0,
"totalOut": 0
},
{
"tokenInfo": {
"address": "0xae66d00496aaa25418f829140bb259163c06986e",
"name": "Super Wallet Token",
"decimals": "8",
"symbol": "SW",
"totalSupply": "8400000000000000",
"owner": "0xba051682e9dbc40730fcef4a374e3a57a0ce3eff",
"lastUpdated": 1524401948,
"issuancesCount": 0,
"holdersCount": 30276,
"price": false
},
"balance": 11567900,
"totalIn": 0,
"totalOut": 0
},
{
"tokenInfo": {
"address": "0x8e4fbe2673e154fe9399166e03e18f87a5754420",
"name": "Universal Bonus Token | t.me/bubbletonebot",
"decimals": "18",
"symbol": "UBT",
"totalSupply": "1150000000000000000000000",
"owner": "0xc2db6e5b96dd22d9870a5ca0909cceac6604e21d",
"lastUpdated": 1524393745,
"issuancesCount": 0,
"holdersCount": 99896,
"price": false
},
"balance": 10000000000000000000,
"totalIn": 0,
"totalOut": 0
}
]
}
You need a custom deserializer to do what you want. It should be straight forward though. Here is some code which checks if the price is not false and then turns it into a Dictionary<string, string>. This code makes the assumption that your root object is named RootObject:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var root = default(Rootobject);
// Let's go through each tokenInfo and check if price is not false
// so we can turn it into a dictionary.
foreach (var thisToken in root.tokens)
{
if (thisToken.tokenInfo.price.ToString() != "false")
{
thisToken.tokenInfo.price = JsonConvert.DeserializeObject<Dictionary<string, string>>(thisToken.tokenInfo.price.ToString());
}
}
serializer.Populate(jsonObject.CreateReader(), root);
return root;
}
Please see the full example here and search for ProfessionConverter in that link for the full class.
I think it is not very good approach to use false in cases when there is no price. If there is no price it should be something like "price" :{} or price element shouldn't be there at all. In other words, it is not good idea to mixup boolean object and dictionaty imho.
I think you can use provided by VisualStudio class where Price is an object. And you can create a custom serializer which will treat false as an null (or object with empty fields) along with standart deserialization mechanism.
I am trying to deserialize an object in a Web Api from an object posted by angular. I am receiving an error: Error reading object reference '1'. Path 'Developers[0].DeveloperId', line 20, position 21
My Json object is (which has been validated as valid JSON):
{
"Id": 0,
"Name": "Name",
"OwnerId": 1,
"Description": "Description",
"Note": "My Notes",
"Stakeholders": [
{
"$id": "1",
"StakeholderId": 1,
"Name": "Mary",
"DateModified": "2018-02-21T12:28:15.023",
"DateCreated": "2018-02-21T12:28:15.023",
"$$hashKey": "object:3"
}
],
"Developers": [
{
"$id": "1",
"DeveloperId": 1,
"DeveloperName": "Joseph",
"DateModified": "2018-02-21T12:28:26.07",
"DateCreated": "2018-02-21T12:28:26.07",
"$$hashKey": "object:4"
}
]
}
I am trying to deserialize with:
var app = JsonConvert.DeserializeObject<Application>(request.ToString(), new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
The developer class (which is similar to Stakeholder class)
public class Developer : IModificationHistory
{
public int DeveloperId { get; set; }
[Required]
public string DeveloperName { get; set; }
[JsonIgnore]
public virtual List<Application> Applications { get; set; }
public DateTime DateModified { get; set; }
public DateTime DateCreated { get; set; }
}
The application class is simply:
public class Application
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public string Note { get; set; }
public virtual List<Stakeholder> Stakeholders { get; set; }
public int OwnerId { get; set; }
public virtual List<Developer> Developers { get; set; }
}
The javascript I use to call this post is:
var data =
{
Id: vm.appId,
Name: vm.applicationName,
OwnerId: vm.owner.DeveloperId,
Description: vm.applicationDescription,
Note: vm.note,
Stakeholders: vm.selectedStakeholders,
Developers: vm.selectedDevelopers
};
$http.post("/api/Application/Post/", JSON.stringify(data))
The Stakeholders List gets filled properly, but the Developers list does not. If I put developers in the list before stakeholders, then developers list gets filled properly and stakeholders does not. Any suggestions would be greatly appreciated!
The problem is with the same value of $id, both are set to 1, see inner exception:
{"A different value already has the Id '1'."}
I just changed its value to 2 and it is working fine:
{
"Id": 0,
"Name": "Name",
"OwnerId": 1,
"Description": "Description",
"Note": "My Notes",
"Stakeholders": [
{
"$id": "1",
"StakeholderId": 1,
"Name": "Mary",
"DateModified": "2018-02-21T12:28:15.023",
"DateCreated": "2018-02-21T12:28:15.023",
"$$hashKey": "object:3"
}
],
"Developers": [
{
"$id": "2",
"DeveloperId": 1,
"DeveloperName": "Joseph",
"DateModified": "2018-02-21T12:28:26.07",
"DateCreated": "2018-02-21T12:28:26.07",
"$$hashKey": "object:4"
}
]
}
Here is the screenshot of my output:
I am having an issue with deserializing decimal value correctly. A suggestion on the site was to use a constructor but it is not calling that constructor.
Here is the JSON:
{
"errors": false,
"response": {
"entities": [
{
"currency_id": "1",
"balance": 1e-8,
"address": ""
},
{
"currency_id": "2",
"balance": 0,
"address": null
},
{
"currency_id": "3",
"balance": 0.09865566,
"address": null
},
{
"currency_id": "5",
"balance": 0,
"address": null
},
{
"currency_id": "6",
"balance": 0,
"address": null
}]
},
"pagination": {
"items_per_page": 100,
"total_items": 5,
"current_page": 1,
"total_pages": 1
}
}
My classes:
public class ApiResponse<T> where T : class
{
public bool Errors { get; set; }
public T Response { get; set; }
}
public class ApiPagingResponse<T> : ApiResponse<T> where T : class
{
public Pagination Pagination { get; set; }
}
public class GetBalanceListResponse
{
public GetBalanceListResponseEntity Entity { get; set; }
}
[JsonObject]
public class GetBalanceListResponseEntity
{
[JsonConstructor]
public GetBalanceListResponseEntity([JsonProperty("currency_id")]string currencyId, [JsonProperty("balance")]string balance, [JsonProperty("address")]string address)
{
CurrencyId = currencyId;
Balance = decimal.Parse(balance, NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint,
CultureInfo.InvariantCulture);
Address = address;
}
[JsonProperty("currency_id")]
public string CurrencyId { get; set; }
[JsonProperty("balance")]
public decimal Balance { get; set; }
[JsonProperty("address")]
public string Address { get; set; }
}
I call it using this:
var result = JsonConvert.DeserializeObject<ApiPagingResponse<GetBalanceListResponse>>(stringResult);
Where stringResult is the json string I want to deserizalize.
Currently it just returns null for the Entity property of the response. All my other serialization works fine with this method, the problem is with "balance": 1e-8,
Has anyone dealt with a similar issue and can be of assistance with this?
Your use of [JsonConstructor] is correct. Your only problem is that, in GetBalanceListResponse, the method
public GetBalanceListResponseEntity Entity { get; set; }
should be
public List<GetBalanceListResponseEntity> Entities { get; set; }
This is because, in the JSON, the corresponding property response.entities is an array:
{
"errors": false,
"response": {
"entities": [
// Entity values omitted
]
},
// Pagination omitted
}
With this fix, the constructor is called and your code basically works. Sample fiddle.
To avoid this sort of problem in the future, you could use an automatic code-generation tool such as http://json2csharp.com/, Paste JSON as Classes or https://jsonutils.com/ to generate appropriate classes from your JSON, then modify as required, e.g. by making the auto-generated types generic.
I have the following class
public class Airport
{
[MaxLength(75)]
public string Name { get; set; }
public bool NotInUse { get; set; }
[MaxLength(50)]
public string City { get; set; }
[MaxLength(50)]
public string Country { get; set; }
[MaxLength(2)]
public string Iso { get; set; }
[MaxLength(3)]
public string Iata { get; set; }
[MaxLength(4)]
public string Icao { get; set; }
}
I have the following json file - Not all properties are within the json
{
"Airports":{
[
{
"Name": "Belfast International",
"City": "Belfast",
"Country": "United Kingdom",
"Iso": "GB",
"Iata": "BFS"
},
{
"Name": "City of Derry",
"City": "Derry",
"Country": "United Kingdom",
"Iso": "GB",
"Iata": "LDY"
}
]
}
}
I am trying to deserialise the json with this method
public IList<Airport> ReadAirportsFromJson()
{
if (File.Exists(AirportJsonFilename))
{
string fileContents = File.ReadAllText(AirportJsonFilename);
var airports = JsonConvert.DeserializeObject<List<Airport>>(fileContents);
return airports;
}
return null;
}
I get the following exception
I am unsure how to progress this and resolve the issue.
The json is not valid, I'd suggest changing it to something like this
{
"Airports":
[
{
"Name": "Belfast International",
"City": "Belfast",
"Country": "United Kingdom",
"Iso": "GB",
"Iata": "BFS"
},
{
"Name": "City of Derry",
"City": "Derry",
"Country": "United Kingdom",
"Iso": "GB",
"Iata": "LDY"
}
]
}
and create a wrapper class
public class AirportsWrapper
{
public List<Airport> Airports { get; set; }
}
You can deserialize the json into AirportsWrapper and return the Airports property
public IList<Airport> ReadAirportsFromJson()
{
if (File.Exists(AirportJsonFilename))
{
string fileContents = File.ReadAllText(AirportJsonFilename);
var airportsWrapper = JsonConvert.DeserializeObject<AirportsWrapper>(fileContents);
if (airportsWrapper != null)
{
return airportsWrapper.Airports;
}
}
return null;
}
Demo: https://dotnetfiddle.net/NQ8JfQ
When passing it in like you do there, you're trying to deserialize the entire file into a list of airports. If the entire file is something else that also happens to contain a list of airports, this won't work. You either have to get just the parts of the JSON which is actually the list of airports, or you have to make a class/classes to represent the entire JSON file and deserialize into that. From there on, you can just go to yourObject.Airports.