Newtonsoft Putting new property in JObject at the right place - json.net

Let's say I have an object like this:
{
"Node": "Fruit.Color",
"Response": "Green"
}
How can I create an JObject which looks like this:
{
"Fruit": {
"Color": "Green"
}
}
I am messing around with Jobjects and such but I can't get it to work.

This seems to do the trick
JToken jToken = response;
foreach (var str in node.Split('.').Reverse())
{
var obj = new JObject
{
{ str, jToken }
};
jToken = obj;
}
No idea if it is the best option ...

Related

Why does assigning a JArray to a var fail when I call JsonConvert.DeserializeObject()?

On this line of code:
var arr = JsonConvert.DeserializeObject<JArray>(s);
...I am getting, "Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type 'Newtonsoft.Json.Linq.JArray'."
I changed that line to this:
JArray arr = JsonConvert.DeserializeObject<JArray>(s);
...and got the same err msg.
I changed it to this:
var arr = JsonConvert.DeserializeObject<JObject>(s);
...and it wouldn't even compile.
The value of what has been read by the call (in string s) at this point is:
{"id":347745,"results":[{"iso_3166_1":"US","release_dates":[{"certification":"","iso_639_1":"","note":"","release_date":"1936-12-12T00:00:00.000Z","type":3}]}]}
All I want from it is the value for "certification"; In this case, the certification value is an empty string ("certification":"")
In context, the code is:
. . .
try
{
var webRequest = (HttpWebRequest)WebRequest.Create(RESTStringToGetMPAARatingForMovieId);
webRequest.Method = "GET";
var webResponse = (HttpWebResponse)webRequest.GetResponse();
if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 0))
{
StreamReader streamReader = new StreamReader(webResponse.GetResponseStream());
string s = streamReader.ReadToEnd();
var arr = JsonConvert.DeserializeObject<JArray>(s);
//JArray arr = JsonConvert.DeserializeObject<JArray>(s);
//var arr = JsonConvert.DeserializeObject<JObject>(s);
foreach (JObject obj in arr)
{
_currentMPAARating = (string)obj["certification"];
. . .
}
}
else
{
MessageBox.Show(string.Format("Status code == {0}, Content length == {1}",
webResponse.StatusCode, webResponse.ContentLength));
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Your JSON is not an array, it is an object which contains an array (results). But it's actually more complicated than that: the certification string you seek is nested even further down inside a second release_dates array.
If you take your JSON and reformat it using a JSON validator/beautifier, it should become more clear:
{
"id": 347745,
"results": [
{
"iso_3166_1": "US",
"release_dates": [
{
"certification": "",
"iso_639_1": "",
"note": "",
"release_date": "1936-12-12T00:00:00Z",
"type": 3
}
]
}
]
}
So to get the data you are looking for using regular foreach loops, you would need code like this:
var obj = JsonConvert.DeserializeObject<JObject>(s);
var resultArr = (JArray)obj["results"];
foreach (JObject resultObj in resultArr)
{
var releaseDatesArr = (JArray)resultObj["release_dates"];
foreach (JObject releaseDateObj in releaseDatesArr)
{
_currentMPAARating = (string)releaseDateObj["certification"];
// ...
}
}
Fiddle: https://dotnetfiddle.net/SMzQTw
If all you need is the one item, here's a shortcut. Use the SelectToken method with the recursive descent operator (..) like this:
var obj = JsonConvert.DeserializeObject<JObject>(s);
_currentMPAARating = (string)obj.SelectToken("..certification");
Fiddle: https://dotnetfiddle.net/S1ScLO
But note the above will only return the first match. If you are expecting multiple certifications, you can use SelectTokens (plural) instead:
var obj = JsonConvert.DeserializeObject<JObject>(s);
var ratings = obj.SelectTokens("..certification").Select(t => (string)t).ToList();
Fiddle: https://dotnetfiddle.net/zyjNnJ

Web API translate JSON object into simple parameters

If I am sending JSON data (via POST) to a .Net Core Web API like this
{ a: "a", b: "b" }
What do I need to do to have a controller method like this?
[HttpPost]
public async Task SometMethod(string a, string b)
{
return Ok();
}
Normally, all tutorials and docs say that you need to define a class and use [FromBody] attribute. But how can I make do without extra classes that I don't really need?
Firstly,your json should be:
{
"a":"a",
"b":"b"
}
You could receive data as JObject instead of a class like below:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpPost]
public void Post(JObject data)
{
//get the property value like below
var data1 = data["a"].ToString();
var data2 = data["b"].ToString();
}
}
Result (For easily distinguish value and property name,I change a to aaa and b to bbb):
If you want to post the data to the method like this, you will have to serialize your data before you can send it to the server. Assuming you are using JQuery, you can do like the following.
var postData = $.param({ a: "a", b: "b" });
//Then you can send this postData obejct to the server. This should perfectly bound to the parameters.
You can also use the same in an angular app.
After some research I came up with ModelBinder to do just this. It is not performant since it re-parses the whole request body for every parameter. I will improve it in the future.
https://github.com/egorpavlikhin/JsonParametersModelBinder
public class JsonBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
var actionDescriptor = bindingContext.ActionContext.ActionDescriptor as ControllerActionDescriptor;
if (actionDescriptor.MethodInfo.GetCustomAttributes(typeof(JsonParametersAttribute), false).Length > 0)
{
var context = bindingContext.HttpContext;
if (context.Request.ContentType != "application/json")
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
#if (NETSTANDARD2_1 || NETCOREAPP3_0)
context?.Request.EnableBuffering();
#else
context?.Request.EnableRewind();
#endif
using var reader = new StreamReader(context.Request.Body, Encoding.UTF8,
false,
1024,
true); // so body can be re-read next time
var body = await reader.ReadToEndAsync();
var json = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(body);
if (json.TryGetValue(bindingContext.FieldName, out var value))
{
if (bindingContext.ModelType == typeof(string))
{
bindingContext.Result = ModelBindingResult.Success(value.GetString());
}
else if (bindingContext.ModelType == typeof(object))
{
var serializerOptions = new JsonSerializerOptions
{
Converters = {new DynamicJsonConverter()}
};
var val = JsonSerializer.Deserialize<dynamic>(value.ToString(), serializerOptions);
bindingContext.Result = ModelBindingResult.Success(val);
}
}
context.Request.Body.Position = 0; // rewind
}
}
}

Fetching JSON data with .netcore from a web api

I made an api which have some data for example www.example.com/data/select?indent=on&q=title:asthma
gives the data in JSON format like
{
"responseHeader":{
"status":0,
"QTime":2,
"params":
{
"q":"title:asthma",
"indent":"on",
"wt":"json"
},
"response":{"numFound":1,"start":0, docs:[
{
"tstamp": "xxxx"
"id": "xxxxx"
"title": "Asthma is a medical term"
"url": "www.example.com/xxxx"
"content":"xxxxx"
}]}
}}
I want to call the same url from my .netcore application such that I can have title and url from the response and show it to my .netcore application.
As a new to .netcore it is pretty tricky to get used to MVC architecture. My model look like this
namespace searchEngineTesting.Models
{
public class SearchModel
{
public string Title {get; set;}
public string Source {get; set;}
}
}
How can I use controller that whenever triggers take a string as an input for example cancer and put it to the title of the api like www.example.com/data/select?indent=on&q=title:cancer and fetch the title and url from the response.
You could fetch json data like below:
[HttpGet]
public async Task<JsonResult> Get()
{
var model = new SearchModel();
var url = "https://localhost:5001/api/values/test";//it should be the url of your api
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.GetAsync(url))
{
using (var content = response.Content)
{
//get the json result from your api
var result = await content.ReadAsStringAsync();
var root = (JObject)JsonConvert.DeserializeObject(result);
var items = root.SelectToken("responseHeader").Children().OfType<JProperty>().ToDictionary(p => p.Name, p => p.Value);
foreach(var item in items)
{
if(item.Key== "response")
{
var key = item.Value.SelectToken("").OfType<JProperty>().ToDictionary(p => p.Name, p => p.Value);
foreach (var k in key)
{
if(k.Key== "docs")
{
var tests = JsonConvert.DeserializeObject<JArray>(k.Value.ToString());
var data = k.Value.SelectToken("").Children().First();
var test = data.SelectToken("").Children().OfType<JProperty>().ToDictionary(p => p.Name, p => p.Value);
foreach (var t in test)
{
if (t.Key == "url")
{
model.Source = t.Value.ToString();
}
else if (t.Key=="title")
{
model.Title = t.Value.ToString();
}
}
}
}
}
}
return new JsonResult(model);
}
}
}
}
[HttpGet("[Action]")]
public string test()
{
//for easy testing,I just read your json file and return string
var jsonstring = System.IO.File.ReadAllText("C:\\test.json");
return jsonstring;
}

Build JObject from iterative path

I need to dynamically build a object that I can then serialize to a JSON string. Essentially I'm working off of two Dictionaries that I need to use to create the new object.
var myValues= new Dictionary<string, object>
{
{ "Value1", "Foo" },
{ "Value2", "Bar" }
};
var mappedValues = new Dictionary<string, string>
{
{ "Value1", "Some:Path" },
{ "Value2", "Some:OtherPath }
};
As I loop through I need to be able to build out the Json Object so that I end up with something like:
{
"Some": {
"Path": "Foo",
"OtherPath": "Bar"
}
}
From what I've seen there is no way built into Newtonsoft.Json to do this specifically, but I'm hoping that someone may have an idea of how I might be able to most efficiently accomplish the goal.
It doesn't seem that there is any sort of way to create an element built into Newtonsoft.Json at this time... however I eventually came up with the following solution which works
private JObject DoFoo()
{
var jObject = new JObject();
foreach(var mapping in mappedValues)
{
var queue = new Queue<string>(mapping.Value.Split(':');
var value = myValues[mapping.Key];
SetValueWithPath(jObject, queue, value);
}
return jObject;
}
private void SetValueWithPath(JObject parent, Queue<string> path, object content)
{
var currentNode = path?.FirstOrDefault();
if (string.IsNullOrEmpty(currentNode)) return;
if (path.Count == 1)
{
parent[currentNode] = JToken.FromObject(content);
return;
}
else if (!parent.ContainsKey(currentNode))
{
parent[currentNode] = new JObject();
}
path.Dequeue();
SetValueWithPath(parent[currentNode] as JObject, path, content);
}

Can a Json converter help with poor Json convention

Here is an example of they type JSON I get:
"Books": {
"6": {
"Name": "Some book"
},
"7": {
"Name": "Some other book"
}
}
in a perfect world, the api (that I cannot change) would return this instead:
"Books": [
{
"ID": "6",
"Name": "Some book"
},
{
"ID": "7",
"Name": "Some other book"
}]
Is it possible to write a JsonConverter that will push the top example into this:
[DataContract]
public class Book
{
[DataMember(Name = "ID")]
public int ID { get; set; }
[DataMember(Name = "Name")]
public string Name { get; set; }
}
Thanks
}
Unfortunately, the fact that it's just not well-formatted JSON is going to throw any library that handles JSON. The curly brackets will always be interpreted in the same way, so unless someone that understands JSON better than me has a better idea, then I think your best bet is to do some manual processing.
If the mis-formatting is consistent, then you might be able to use regular expressions to replace the mis-placed curly brackets with square brackets, and then parse it is well-formatted JSON.
Good luck!
If your input document's structure is defined fairly rigidly, you could try to just correct the document so it will parse, and then translate the document into your format.
// reference Newtonsoft.Json.dll
// using Newtonsoft.Json
// using Newtonsoft.Json.Linq
string badJson = "\"Books\": { \"6\": { \"Name\": \"Some book\" }, \"7\": { \"Name\": \"Some other book\" } }";
var source = JObject.Parse(string.Format("{{{0}}}", badJson));
var sb = new StringBuilder();
using (var writer = new JsonTextWriter(new StringWriter(sb)))
{
writer.Formatting = Newtonsoft.Json.Formatting.None;
writer.WriteStartObject();
writer.WritePropertyName("Books");
writer.WriteStartArray();
foreach (JToken child in source["Books"])
{
JProperty prop = child as JProperty;
writer.WriteStartObject();
writer.WritePropertyName("ID");
writer.WriteValue(prop.Name);
writer.WritePropertyName("Name");
writer.WriteValue((string)prop.Value["Name"]);
writer.WriteEndObject();
}
writer.WriteEndArray();
writer.WriteEndObject();
}
Console.WriteLine(sb.ToString());
This code produces the following JSON:
{"Books":[{"ID":"6","Name":"Some book"},{"ID":"7","Name":"Some other book"}]}

Resources