How to appropriately deserialize dynamic JSON? - c#

I have a service that accepts JSON, and as a part of forwarding the requests from the Browser to a web server to the service (also an HTTP Server), I have this code:
if (Request.HttpMethod.ToLower() == "post")
{
var jsonObj = ObjectFromRequestBody<Dictionary<string, string>>();
if (jsonObj != null)
{
// uses Newtonsoft JSON Serializer to re-serialize the object; this is RestSharp
requestToForward.AddJsonBody(jsonObj);
}
}
ObjectFromRequestBody looks like this:
protected T ObjectFromRequestBody<T>()
{
Stream requestStream = Request.InputStream;
requestStream.Seek(0, SeekOrigin.Begin);
string json = new StreamReader(requestStream).ReadToEnd();
var payload = JsonConvert.DeserializeObject<T>(json);
return payload;
}
Now, this works fine, assuming that it -is- a JSON object being posted. (a KVP collection)
However, I imagine in the case that the payload is simply a value (int, string, boolean, or heck, even an array), this will error out.
I've tried simply deserializing to a type of Object results in it being re-serialized into an array of empty arrays; probably because Newtonsoft's JSON Serializer by default deserializes Objects to JObjects, which is essentially an enumerable of KVPs.
Is there a type agnostic way to get from Object to serializing to the appropriate type without essentially writing a series of conditional statements?

In this case a much better solution is to simply pipe the request body directly into the RestSharp request.
if (Request.HttpMethod.ToLower() == "post")
{
var requestBody = new StreamReader(Request.InputStream).ReadToEnd();
if (!string.IsNullOrWhiteSpace(requestBody))
{
request.AddHeader("Content-Type", "application/json");
request.AddParameter("", requestBody, RestSharp.ParameterType.RequestBody);
}
}
Though, would still be interested in hearing how to type-agnostically serialize a JObject properly.

Related

API Controller unable to read Json from POST body

Background
I'm sending JSON in my body to my API controller but keep getting the following error.
{"":["Unexpected character encountered while parsing value: {. Path
'', line 1, position 1."]}
My HTTP request
HttpClient client = new HttpClient();
HttpRequest httpRequest;
HttpResponseMessage httpResponse = null;
httpRequest = new HttpRequest("", HostnameTb.Text, null);
var values = new Dictionary<string, string>
{
{ "APIKey", APIKeyTb.Text }
};
string json = JsonConvert.SerializeObject(values);
StringContent content = new StringContent(json.ToString(), Encoding.UTF8, "application/json");
httpResponse = client.PostAsync(HostnameTb.Text, content).Result;
var responseString = await httpResponse.Content.ReadAsStringAsync();
My Controller looks like this.
[HttpPost]
public void Post([FromBody] string value)
{
//Never gets here.
}
The Json in the body.
{"APIKey":"1283f0f8..."}
Question
I would prefer to use the .Net Core [From Body] functionality, rather than getting the content manually.
I would expect the JSON string to be available in the string Value parameter.
What am I missing?
ASP.NET Core tries to deserialize {"APIKey":"1283f0f8..."} from JSON into a string value, and fails, because it expects the input to be a valid JSON string.
In other words, if your body was "{\"APIKey\":\"1283f0f8...\"}" you would have the JSON string in the input variable as you expect.
In order to get the APIKey value without changing the HTTP request, create an input type:
public class Input
{
public string ApiKey { get; set; }
}
and use that as the input of your controller action:
[HttpPost]
public void Post([FromBody] Input input)
{
var apiKey = input.ApiKey;
// etc
}
Alternatively, change your HTTP request to send a string:
// ...
var content = new StringContent(JsonConvert.SerializeObject(json), Encoding.UTF8, "application/json");
// ...
Note the use of JsonConvert.SerializeObject() instead of ToString(); "foo".ToString() is still just "foo", while you want "\"foo\"".
That's not how this works. [FromBody] invokes a serializer to deserialize the request body. Then, the modelbinder attempts to bind that to the param. Here, it cannot do that because you're binding to a string, and the request body is a dictionary. Essentially what's happening under the hood (pseudo-code) is:
value = JsonConvert.DeserializeObject<string>(dictionaryAsJson);
You're getting a deserialization error from JSON.NET because it can't parse the JSON into a string.
If you want the value as a string, then you should post is as something like text/plain instead of application/json. Otherwise, you'll need to bind to a type that actually represents the JSON object coming in, which would be a Dictionary<string, string> here.
I had the same issue with ASP.NET Core 3.1. I was POSTing a JSON to my API controller that looked like:
public JsonResult Save([FromBody]MainDetails obj){ ... }
The problem in my case was that a property ChildDetails.StartDate of my MainDetails object was of type DateTime, and I was sending a null value in JSON. This was causing the deserialization to fail at the controller. I changed the property type to DateTime? from DateTime to make it work.
Basically, one needs to check and ensure that the JSON that you POST is valid for the target object to which you are deserializing it. If you have non-nullable properties for which the value sent in JSON is null, then your deserialization will fail (without telling you why).
Hope this helps.

Unable to Parse JSON Response

I have a service that sends JSON response. The controller method looks as follows:
string varStr = "{proper JSON here}";
public string GetListofResourcesInSubscription(string subscriptionId)
{
// Uncomment any option below to test. The error persists either way.
//return varStr; --- Option 1
return JsonConvert.SerializeObject(JObject.Parse(varStr)); // Option 2
}
The method that gets the response is like the following:
response = outgoingRequest.GetResponse() as HttpWebResponse;
if (response.StatusCode == HttpStatusCode.OK)
{
responseStream = response.GetResponseStream();
using (var reader = new StreamReader(responseStream))
{
string strResp = reader.ReadToEnd();
JObject joResponse = JObject.Parse(strResp); // throws error
JArray objArray = (JArray)joResponse["value"];
// other processing
}
}
Irrespective of the return statement selected in the controller method above, the response parser is always throwing an error while it is trying to parse the response.
Changing the parsing line to the following resolves the issue but it is not clear to me why it is so.
JObject joResponse = JObject.Parse(JsonConvert.DeserializeObject<string>(strResp));
Also, I want to know what is the correct way of sending a JSON response from an ASP.NET web api2 controller. I don't want to use models for creating the response because I have JSON strings that I want to return directly instead of creating models out of them.
Update 1:
The error is the following:
"Error reading JObject from JsonReader. Current JsonReader item is not an object: String. Path '', line 1, position 6546."} System.Exception {Newtonsoft.Json.JsonReaderException}
You can't deserialize a complex JSON object back to a string. Your example won't work, because you are assuming the JSON evaluates to a string:
JObject joResponse = JObject.Parse(JsonConvert.DeserializeObject<string>(strResp))
You might have more success either using a JObject, or the alternative is to deserialize into a Dictionary, or into a known type.
var dictionary = JsonConvert.DeserializeObject<<Dictionary<string,object>>(strResp);
The problem here was the return type of the controller function. As it was returning string, the serialization to string was required to get correct result. The correct way of returning JSON is to return a JToken as explained here. So the controller needs to be changed to the following:
public JToken GetListofResourcesInSubscription(string subscriptionId)
{
return JObject.Parse(varStr);
}

Using the contents of a JsonObject on aspx web page

I am trying to use a HTTP request to return the contents of a json string from google shopping api. What I have so far seems to be working but I would like to know how I can use the contents of the object to display the data on a page.
public string HttpGet(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
try
{
using (Stream stream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(stream);
return reader.ReadToEnd();
}
}
finally
{
response.Close();
}
}
protected void submit_Click(object sender, EventArgs e)
{
string json = HttpGet("https://www.googleapis.com/shopping/search/v1/public/products?key={KEY}4&country=UK&q=BX80637153570K&rankBy=price:ascending&maxResults=1&alt=json");
dynamic obj = JsonObject.Parse(json);
}
Ok looking at the responses it looks as though I need a C# class for the data returned in json. I have created a classes using json2csharp.com. This is the data I need to return from the Json and display on the page. Maybe it will help explain my problem better.
https://www.googleapis.com/shopping/search/v1/public/products?key=AIzaSyCC0j59RBeGNtf2W2ft6avhfoTdJ1FQ2c4&country=UK&q=BX80637153570K&rankBy=price:ascending&maxResults=1&alt=json
Can anyone advise how I can use this information on my website. I am a little lost now as I'm new to all this and have tried several different methods.I don't need all of the returned data if that makes a difference? Just price and link.
Supposing dynamic obj is rightly filled you can use it's content in web page
For examples, if json is:
{
"error" : {
"code": 400,
"message": "Bad Request"
}
}
You can access object properties with code like this:
Response.Write("Error code is" + obj.error.code);
Of course Response.Write is only a sample on how you can send retrieved data to the page.
Edit 1:
It seems json converter used in question is not working or not working right.
In many cases, it's overkill to create a concrete class only to parse a json, expecially since C# 4 that can use ExpandoObject
This is a sample on how you can deserialize in a dynamic object without the need to create a concrete object
var url = "http://www.google.com/ig/calculator?hl=en&q=100USD=?EUR";
string json = HttpGet(url);
//this is json string:
//{lhs: "100 U.S. dollars",rhs: "78.1799703 Euros",error: "",icc: true}
//now convert in a dynamic object
var jss = new DynamicJsonConverter();
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
dynamic obj = serializer.Deserialize(json, typeof(object));
//now you have access to json content
string text1 = obj.lhs;
string text2 = obj.rhs;
DynamicJsonConverter class can be created using code you can find here:
Deserialize JSON into C# dynamic object?
I think you need to deserialize the response
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
You will need to create a class with the property resulted by the response.
Say test class
class Test
{
//define a similar property here
// which you suppose it will be return in the response
}
Then
Test routes_list = (Test)json_serializer.DeserializeObject(yourjson);
Edit 1
Or you can go through this link
How to Convert JSON object to Custom C# object?

Deserialize Json to Object in a handler

I have a simple class:
public class site
{
public string URL { get; set; }
}
That exists within a http handler. Currently I am posting json to this handler and trying to deserialize it to get the URL out of the string. I am however having a problem with the deserialize part of it.
I have a string "jsonString" that has the json formatted like so:
[{"URL":"http://www.google.com/etc/"}]
Here is my code for the deserialize:
JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
string jsonString = String.Empty;
HttpContext.Current.Request.InputStream.Position = 0;
using (StreamReader inputStream = new StreamReader(HttpContext.Current.Request.InputStream))
{
jsonString = inputStream.ReadToEnd();
}
site currSite = new site();
currSite = jsonSerializer.Deserialize<site>(jsonString);
//set response types
HttpContext.Current.Response.ContentType = "application/json";
HttpContext.Current.Response.ContentEncoding = Encoding.UTF8;
//send response
HttpContext.Current.Response.Write(jsonSerializer.Serialize(currSite.URL));
I am then trying to send a response using currSite.URL however it is failing. What am I missing here? I am reasonably confident it is at the deserialize part because if I send a response of jsonString instead of currSite.URL it will work.
Your json string shows that it's an array, not a single entity. You should deserialize it as so:
var result = jsonSerializer.Deserialize<site[]>(jsonString);
And result[0].Url should contain what you are looking for.
Update
Adding sample code:
string json = #"[{""URL"":""http://www.google.com/etc/""}]";
JavaScriptSerializer js = new JavaScriptSerializer();
var result = js.Deserialize<site[]>(json);
Console.WriteLine(result[0].URL);
Prints:
http://www.google.com/etc/
If you're simply trying to send back the URL as the response (just the string) there's no need to serialize the value. Just pass currSite.URL to the Write method.
Otherwise, I'm guessing you should serialize the whole currSite object for the response rather than just the URL. It all depends on what kind of response the caller is expecting (which you haven't specified).

Deserialize JSON Stream with JSON.net

I recently switched from using SOAP to JSON. I now have a problem with the performance because it takes about 26 seconds to deserialize the JSON stream on my WP7 device.
Therefore I thought about using Json.net instead of DataContractJsonSerializer.
However, I was not able to find much information about this.
I use a webclient and then OpenReadAsync, so I have a Stream as e.Result.
How can I turn that stream into an object using Json.net?
Should I maybe use DownloadStringAsync instead?
This is the Code that currently works:
//string URL = ""; //Actual URL to JSON is here
WebClient client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
client.OpenReadAsync(new Uri(URL, UriKind.Absolute));
And the event handler:
void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(JSONObject), null);
JSONObject data = serializer.ReadObject(e.Result) as JSONObject;
}
JSONObject is the response object of the service's SOAP endpoint, but both endpoints return the same data.
This works fine and I can go on and parse data just like I parsed the SOAP response, but I would like it do deserialize faster.
In order to deserialize you'll need the whole JSON. It may be better to use DownloadStringAsync and once you've received the entire JSON string, you can deserialize it.
Then in your DownloadStringCompleted you can pass in the class you want to deserialize it to. For example, if you are receiving JSON like:
{ name: "smoak", title: "super awesome" }
Then you need to create a class with those properties:
class SomeClassName
{
publc string name { get;set;}
public string title { get;set; }
}
and pass it to the JsonConvert:
var deserializedObj = JsonConvert.DeserializeObject<SomeClassName>(e.Result);
Console.WriteLine(deserializedObj.name);

Categories