I'm trying to deserialize some JSON response. With simple response I've no problem but with a complex response I get this error:
Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type
'System.Collections.Generic.List`1[APIEffilogics.Usuari+Node]' 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.
It seems that I have a problem with the type of I put when deserialize the string. The JSON string is like this:
{
nodes: [
{
id: 5,
global_id: 5,
description: "Oven",
room_id: 2,
floor_id: 1,
building_id: 1,
client_id: 2,
nodemodel_id: 2,
nodetype_id: 1
},
{
id: 39,
global_id: 39,
description: "Fridge",
room_id: 2,
floor_id: 1,
building_id: 1,
client_id: 2,
nodemodel_id: 8,
nodetype_id: 1
}, ...
],
limit: 10,
offset: 0
}
And those are the classes:
public class Node : Usuari //Estructura nodes
{
[JsonProperty("limit")]
public int limit { get; set; }
[JsonProperty("offset")]
public int offset { get; set; }
[JsonProperty("nodes")]
public List<Node_sub> nodes_sub { get; set; }
}
public class Node_sub : Node
{
[JsonProperty("id")]
public string nid { get; set; }
[JsonProperty("global_id")]
public string gid { get; set; }
[JsonProperty("description")]
public string descrip { get; set; }
[JsonProperty("room_id")]
public string rid { get; set; }
[JsonProperty("floor_id")]
public string fid { get; set; }
[JsonProperty("client_id")]
public string cid { get; set; }
[JsonProperty("building_id")]
public string bid { get; set; }
[JsonProperty("nodemodel_id")]
public string model { get; set; }
[JsonProperty("nodetype_id")]
public string type { get; set; }
}
The code is:
public void Request(string url, string metode, string value)
{
try
{
//Enviem la petició a la URL especificada i configurem el tipus de connexió
HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url);
myReq.KeepAlive = true;
myReq.Headers.Set("Cache-Control", "no-store");
myReq.Headers.Set("Pragma", "no-cache");
myReq.Headers.Set("Authorization", usuari.token_type + " " + usuari.access_token);
myReq.ContentType = "application/json";
if (metode.Equals("GET") || metode.Equals("POST"))
{
myReq.Method = metode; // Set the Method property of the request to POST or GET.
if (body == true)
{
// add request body with chat search filters
List<paramet> p = new List<paramet>();
paramet p1 = new paramet();
p1.value = "1";
string jsonBody = JsonConvert.SerializeObject(value);
var requestBody = Encoding.UTF8.GetBytes(jsonBody);
myReq.ContentLength = requestBody.Length;
//myReq.ContentType = "application/json";
using (var stream = myReq.GetRequestStream())
{
stream.Write(requestBody, 0, requestBody.Length); //Enviem el cos de la petició
}
body = false;
}
}
else throw new Exception("Invalid Method Type");
//Obtenim la resposta del servidor
HttpWebResponse myResponse = (HttpWebResponse)myReq.GetResponse();
Stream rebut = myResponse.GetResponseStream();
StreamReader readStream = new StreamReader(rebut, Encoding.UTF8); // Pipes the stream to a higher level stream reader with the required encoding format.
string info = readStream.ReadToEnd();
if (tipus == 0) jsonclient = JsonConvert.DeserializeObject<List<Usuari.Client>>(info);
else if (tipus == 1) jsonedif = JsonConvert.DeserializeObject<List<Usuari.Building>>(info);
else if (tipus == 2) jsonplanta = JsonConvert.DeserializeObject<List<Usuari.Floor>>(info);
else if (tipus == 3) jsonhab = JsonConvert.DeserializeObject<List<Usuari.Room>>(info);
else if (tipus == 4) jsonnode = JsonConvert.DeserializeObject<List<Usuari.Node>>(info);
}
catch (WebException ex)
{
// same as normal response, get error response
var errorResponse = (HttpWebResponse)ex.Response;
string errorResponseJson;
var statusCode = errorResponse.StatusCode;
var errorIdFromHeader = errorResponse.GetResponseHeader("Error-Id");
using (var responseStream = new StreamReader(errorResponse.GetResponseStream()))
{
errorResponseJson = responseStream.ReadToEnd();
}
//var errorCode = JsonObject.Parse(errorResponseJson).Object("responseStatus")["errorCode"];
//var errorMessage = JsonObject.Parse(errorResponseJson).Object("responseStatus")["message"];
}
}
Why I'm having this error? List<Usuari.Node> is an array that contains all the items of JSON message. I try to fix the error but I'm not able to find and answer. How can I fix it?
Thanks
The service returns just one Node, instead of an array of Nodes with one element. To solve this problem, you can use one of these approaches:
Change the service so it always returns an array (by wrapping the result before returning it).
If 1. is not an option: Differentiate the different response types in the client (check if the response is an array and if not, tell the serializer to parse a single Node instead of a list) and either handle the single object differently or just wrap it in a list and then proceed as if the server returned it like that.
Related
Making a POST request for the API, passing incoming json body as hardcoded string. Fine with all hardcoded values, only need to pass ID based on incoming parameter incomingID (int)
Checked couple of articles/questions, but didn't any clear answer. Please suggest/guide how to replace this hardcoded value 123456 with incoming parameter value (incomingID)
Replace field in Json file with some other value
How to replace placeholders inside json string?
private void CallAPI(int incomingID)
{
const string apiURL = "API URL"; // some test API
string getInfoResult = string.Empty;
try
{
HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(apiURL);
webrequest.Method = "POST";
webrequest.ContentType = "application/json";
using (var streamWriter = new StreamWriter(webrequest.GetRequestStream()))
{
string json = "{\"Information\":{\"messageHeader\":{\"message\":\"getInfo\",\"transactionDate\":\"2021-05-11T12:05:54.000\", \"transactionID\":\"2021-05-15T12:05:54.000-12345\",\"payLoadFormat\":\"V1\",\"ID\":123456}}}"; //pass this incomingID based on parameter value
streamWriter.Write(json);
}
HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse();
Encoding enc = System.Text.Encoding.GetEncoding("utf-8");
StreamReader responseStream = new StreamReader(webresponse.GetResponseStream(), enc);
getInfoResult = responseStream.ReadToEnd();
webresponse.Close();
}
catch (Exception ex)
{
throw ex;
}
}
To make this, you can use simple interpolation:
string json = $"{{\"Information\":{{\"messageHeader\":{{\"message\":\"getInfo\",\"transactionDate\":\"2021-05-11T12:05:54.000\", \"transactionID\":\"2021-05-15T12:05:54.000-12345\",\"payLoadFormat\":\"V1\",\"ID\":{incomingID}}}}}}}"
Note that open and close brackets should be repeated twice to escape them. But, this is weird and not so common way to make JSON strings. Better use serialization to achieve your goal. For an example, use Newtonsoft.Json nuget to (de)serialize your objects. What you need to do:
Create your models as separate classes:
PayloadFormat.cs
[Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))] // this string will force your enum serialize as "V1" instead of "0"
public enum PayloadFormat
{
V1,
V2,
V3
}
MessageHeader.cs
public class MessageHeader
{
public MessageHeader(string message, DateTime transactionDate, string transactionId, PayloadFormat payloadFormat, int id)
{
Message = message;
TransactionDate = transactionDate;
TransactionId = transactionId;
PayloadFormat = payloadFormat;
Id = id;
}
public string Message { get; set; }
public DateTime TransactionDate { get; set; } // change type if you need to
public string TransactionId { get; set; } // change type if you need to
public PayloadFormat PayloadFormat { get; set; } // change type if you need to
public int Id { get; set; }
}
Information.cs
public class Information
{
public Information(MessageHeader messageHeader)
{
MessageHeader = messageHeader;
}
public MessageHeader MessageHeader { get; set; }
}
Create an instance of your Information class:
var information = new Information(new MessageHeader("getInfo", DateTime.Now, $"{DateTime.Now}-12345", PayloadFormat.V1, incomingID));
Serialize your string (make sure you are using Newtonsoft.Json;):
var json = JsonConvert.SerializeObject(information);
Then use your json as you need to. The result will be:
{
"MessageHeader": {
"Message": "getInfo",
"TransactionDate": "2022-05-17T19:45:33.2161326+05:00",
"TransactionId": "17.05.2022 19:45:33-12345",
"PayloadFormat": "V1",
"Id": 5
}
}
here is my JSON response:
[
{
"Id": 25,
"CreateDate": "2020-09-26T12:25:27.917",
"Title": "Second TV Repair",
"categoryTitle": "Tv Repair",
"FirstName": "Sample FirsName",
"LastName": "Sample LastName"
} ]
here is my deserilizer class:
public class ServicemanHistory
{
public int Id { get; set; }
public DateTime CreateDate { get; set; }
public string Title { get; set; }
public string categoryTitle { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Here is my restsharp request method:
public static async Task<List<ServicemanHistory>> getServiceManHistory()
{
string url = "https://myapi.net";
string token = Settings.token;
var client = new RestClient(url);
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer " + token);
var response = await client.ExecuteAsync<List<ServicemanHistory>>(request);
Console.WriteLine("**** response is:" + response.Content);
if(response.IsSuccessful)
{
Console.WriteLine("SUCCESS!!!");
return response.Data;
} else
{
return null;
}
}
I get success printed in my console and I get the json returned from response.Content, but response.Data is always null. I did the same using NewtonSoft and that didn't fix it. And I tried using a wrapper for JSON array inside my deserializer class and didn't work either.
My attempt using NewtonSoft:
public static async Task<List<ServicemanHistory>> getServiceManHistory()
{
string url = "https://myapi.net";
string token = Settings.token;
var client = new RestClient(url);
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer " + token);
var response = await client.ExecuteAsync(request);
Console.WriteLine("**** response is:" + response.Content);
var result = JsonConvert.DeserializeObject<List<ServicemanHistory>>(response.Content);
if(response.IsSuccessful)
{
Console.WriteLine("SUCCESS!!!");
return result;
} else
{
return null;
}
}
I get the following error when trying to deserialize using NewtonSoft:
Newtonsoft.Json.JsonSerializationException
Message=Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List
o 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.
My Web API (code that generates JSON) is generating the following JSON string. It seems, if I am not wrong, that it has been encoded twice:
"\"[{\\\"SportID\\\":1,\\\"SportName\\\":\"Tennis\\\"},{\"SportID\\\":2,\\\"SportName\\\":\\\"Footbal\\\"},{\"SportID\\\":3,\"SportName\":\\\"Swimming\\\"}]\""
Web API code:
public string JSONTest()
{
List<Sport> sports = new List<Sport>();
sports.Add(new Sport() { SportID = 1, SportName = "Tennis" });
sports.Add(new Sport() { SportID = 2, SportName = "Footbal" });
sports.Add(new Sport() { SportID = 3, SportName = "Swimming" });
try
{
return JsonConvert.SerializeObject(sports);
}
catch (Exception ex) { }
}
Sport class:
public class Sport { public int SportID { get; set; } public string SportName { get; set; } }
Screenshot of getting JSON:
The following line gives me an error, I think because of twice encoding:
var JavaScriptSerializerResult = (new JavaScriptSerializer()).Deserialize< List<Sport>>(jsonResponse);
I get the same error if try with this:
var jsonConvertResult = JsonConvert.DeserializeObject<List<Sport>>(jsonResponse);
How can I fix my Web API to not encode twice, or if that is not the problem, how can I decode this JSON?
I think you should try JsonConvert.DeserializeObject to deserialize the JSON:
public class Sport
{
// Dummy "Sport" class as it was not mentioned by OP.
public int SportID { get; set; }
public string SportName { get; set; }
}
I get serialized JSON as:
Deserialized it:
string json = JSONTest();
var obj = JsonConvert.DeserializeObject<List<Sport>>(json);
Output:
UPDATE:
As per OP's shared JSON (which is being received from server), encoding can be removed by using:
private string RemoveEncoding(string encodedJson)
{
var sb = new StringBuilder(encodedJson);
sb.Replace("\\", string.Empty);
sb.Replace("\"[", "[");
sb.Replace("]\"", "]");
return sb.ToString();
}
Deserialize it by:
string js = "\"[{\\\"SportID\\\":1,\\\"SportName\\\":\"Tennis\\\"},{\"SportID\\\":2,\\\"SportName\\\":\\\"Footbal\\\"},{\"SportID\\\":3,\"SportName\":\\\"Swimming\\\"}]\"";
string res = RemoveEncoding(js);
var obj = JsonConvert.DeserializeObject<List<Sport>>(res);
Shorter sample for json.net library.
Here, entity is my serializable C# object. I use JsonConvert along with some formatting and specify to ignore reference looping to prevent circular referencing.
using Newtonsoft.Json;
var json = JsonConvert.SerializeObject (entity, Formatting.Indented,
new JsonSerializerSettings {ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
});
Use Linq Query Concept:
public string JSONTest()
{ List<Sport> sports = new List<Sport>();
sports.Add(new Sport() { SportID = 1, SportName = "Tennis" });
sports.Add(new Sport() { SportID = 2, SportName = "Footbal" });
sports.Add(new Sport() { SportID = 3, SportName = "Swimming" });
var result_sports = (from row in sports
group row by new { row.SportID , row.SportName} into hdr
select new Sport()
{
SportID = hdr.Key.SportID ,
SportName = hdr.Key.SportName ,
}).ToList();
string jsondata = new JavaScriptSerializer().Serialize(result_sports);
}
Summary: Your return object is being serialized twice. Remove your hand-rolled serialization code and return objects from your web services - not JSON strings.
Your JSON is indeed being serialized 'twice'. Look closely at this screenshot and we see escaped quote marks:
Your list properly serialized should produce a JSON string something like this:
[{"SportID":1,"SportName":"Tennis"},{"SportID":2,"SportName":"Footbal"},{"SportID":3,"SportName":"Swimming"}]
But we have something more like this:
"[{\"SportID\":1,\"SportName\":\"Tennis\"},{\"SportID\":2,\"SportName\":\"Footbal\"},{\"SportID\":3,\"SportName\":\"Swimming\"}]"
I can replicate this (code below) and it means that your JSON string has itself been fed through a serializer.
This is almost certainly due to you manually serializing your List<Sport>. You don't need to do that. Web API serializes for you - hence the second run through a serializer.
Change the return type of your Web API function if necessary, and then instead of writing:
return JsonConvert.SerializeObject(sports);
just do
return sports;
Web API will take care of the serialization and you will no longer have the bothersome quotes and escape characters.
Code dump I tested with:
void Main()
{
string json = JSONTest();
Console.WriteLine(json);
var obj = JsonConvert.DeserializeObject<List<Sport>>(json);
string jsonX2 = JsonConvert.SerializeObject(json);
Console.WriteLine(jsonX2);
obj = JsonConvert.DeserializeObject<List<Sport>>(jsonX2); // exception
}
public string JSONTest()
{
List<Sport> sports = new List<Sport>();
sports.Add(new Sport() { SportID = 1, SportName = "Tennis" });
sports.Add(new Sport() { SportID = 2, SportName = "Footbal" });
sports.Add(new Sport() { SportID = 3, SportName = "Swimming" });
return JsonConvert.SerializeObject(sports);
}
public class Sport
{
public int SportID { get; set; }
public string SportName { get; set; }
}
I am getting an invalid cast exception that the specified cast is not valid. On this line:
RootObject mountain = JsonConvert.DeserializeObject<RootObject>(json1);
From the documentation this should be fine? I can see the console output is fine?
Response: [{"Height_ft": 2999.0, "Height_m": 914.0, "ID": "c1",
"Latitude": 57.588007, "Longitude": -5.5233564, "Name": "Beinn Dearg",
"humidity": 0.81, "snowCover": 4.99, "temperature": 63.0}]
Spinner spinner = (Spinner)sender;
string urlmountain = "http://removed.azurewebsites.net/api/Mountains?name=";
JsonValue json1 = FetchMountain(urlmountain+string.Format("{0}", spinner.GetItemAtPosition(e.Position)));
//below.................................
RootObject mountain = JsonConvert.DeserializeObject<RootObject>(json1); //this line
string toast = mountain.Name;
Toast.MakeText(this, toast, ToastLength.Long).Show();
private JsonValue FetchMountain(string urlmountain)
{
// Create an HTTP web request using the URL:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(urlmountain));
request.ContentType = "application/json";
request.Method = "GET";
// Send the request to the server and wait for the response:
using (WebResponse response = request.GetResponse())
{
// Get a stream representation of the HTTP web response:
using (Stream stream = response.GetResponseStream())
{
// Use this stream to build a JSON document object:
JsonValue jsonDoc1 = JsonObject.Load(stream);
Console.Out.WriteLine("Response: {0}", jsonDoc1.ToString());
// Return the JSON document:
return jsonDoc1;
}
}
}
public class RootObject
{
public string ID { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public string Name { get; set; }
public double? Height_m { get; set; }
public double? Height_ft { get; set; }
public double? temperature { get; set; }
public double? humidity { get; set; }
public double? snowCover { get; set; }
public override string ToString()
{
return Name;
}
}
The json data being returned is an array of objects, not a single object, as denoted by the opening and closing brackets []. You need to deserialize to an array or a list:
var mountains = JsonConvert.DeserializeObject<List<RootObject>>(json);
To access the first mountain from the deserialized payload, use .FirstOrDefault().
var mountain = mountains.FirstOrDefault();
if (mountain != null)
{
string toast = mountain.Name;
Toast.MakeText(this, toast, ToastLength.Long).Show();
}
It looks like your JSON is an Array of objects. You should be able to deserialize the array and get the first one like so:
RootObject mountain = JsonConvert.DeserializeObject<RootObject[]>(json1)[0];
One thing to note is that you are sort of mixing technologies here. JsonValue is from the System.Json namespace, whereas JsonConvert is from the Newtonsoft.Json (i.e. JSON.Net) namespace. If you wanted to go strictly with JSON.Net, you could do something like this:
private RootObject FetchMountain(string urlmountain)
{
// Create an HTTP web request using the URL:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(urlmountain));
request.ContentType = "application/json";
request.Method = "GET";
using (WebResponse response = request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader streamReader = new StreamReader(stream))
{
JsonSerializer serializer = new JsonSerializer();
RootObject[] mountains = (RootObject[])serializer.Deserialize(streamReader, typeof(RootObject[]));
return (mountains.Length > 0) ? mountains[0] : null;
}
}
Preface: I know of JSON.NET but I cannot use it (client machine).
I need to parse the JSON returned by http://api.fixer.io/latest?base=USD into 3 columns, date, currencyCode, and rate. The issue is with the nested "rates" portion. The currency code is the name of the first element which means I can't use "key" and "value" properties. The only way I know of is to hardcode each possible currency code, which is what I have currently in the code below. I want to be able to use key/value pairs to pull the code/rate simultaneously.
The JSON:
{"base":"USD",
"date":"2016-07-12",
"rates": {
"AUD":1.3101,
"BGN":1.7633,
"BRL":3.2829,
"CAD":1.3029,
etc....}
}
My code so far:
static void Main(string[] args)
{
var curDate = "2001-01-01";
var URL = #"http://api.fixer.io/" + curDate + "?base=USD";
Console.WriteLine(URL);
//WebRequest wrGetURL = WebRequest.Create(URL);
var text = "";
//wrGetURL.ContentType = "application/json; charset=utf-8";
HttpWebRequest httpWebRequest = System.Net.WebRequest.Create(URL) as HttpWebRequest;
using (HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse)
{
if (httpWebResponse.StatusCode != HttpStatusCode.OK)
{
throw new Exception(string.Format("Server error (HTTP {0}: {1}).",
httpWebResponse.StatusCode, httpWebResponse.StatusDescription));
}
Stream stream = httpWebResponse.GetResponseStream();
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(JSONRead));
JSONRead objResponse = (JSONRead)dataContractJsonSerializer.ReadObject(stream);
Console.WriteLine(objResponse.rates.AUD);
}
Console.ReadLine();
}
[DataContract]
public class JSONRead
{
[DataMember(Name = "date")]
public string date { get; set; }
[DataMember(Name = "rates")]
public Rates rates { get; set; }
[DataMember(Name = "base")]
public string bases { get; set; }
}
[DataContract]
public class Rates
{
[DataMember(Name = "AUD")]
public string AUD { get; set; }
//[DataMember(Name = "key")]
//public string key { get; set; }
//[DataMember(Name = "value")]
//public string value { get; set; }
}
What I am trying to return:
Date Code Rate
2016-07-12 AUD 1.3101
2016-07-12 GBN 1.7633
etc...
I had to use the DataContractJsonSerializerSettings and set UseSimpleDictionaryFormat to true. It then reads the nested object into a Dictionary object properly. Thanks for the help #Plutonix.