C# - Convert List<JToken> to List<string> one-liner - c#

I got a Json response from a API which contains a JArray in which there is a JProperty named "title".
I have a working one-liner which returns a List<JToken> which stores all the "title" properties of the Json:
List<JToken> queryResult = JObject
.Parse(client.DownloadString(url))["results"]
.Children()["title"]
.ToList();
I tried to edit the above one-liner so it returns a List<string> but without success...
Is there a way to keep it as a one-liner?

A bit too late, but if someone requires.
List<string> stringList = queryResult.Values<string>().ToList();
may do the job.

Related

Get multiple values on JSON with Json.NET

Is there a function where you can select multiples "nodes" on JSON string?
Sample:
var myJson = #"{
'channel' : 'nine',
'segment' : 'mobile',
'food' : 'pizza'
}";
var myObjectFromJson = JObject.Parse(myJson);
var channelFoodNodes = myObjectFromJson.SelectTokens("channel, food"); //<- This call not works!
Expected result:
{
"channel" : "nine",
"food" : "pizza"
}
Reference:
Querying JSON with SelectToken
First, your code is invalid with the newlines (your string should be a verbatim string if you want to put newlines in it without concatenating, with # in front of it, and quotes replaced by double quotes).
Second, you are trying to invoke SelectTokens() on a string... you need to parse it to a JObject first:
var myJson = JObject.Parse(#"
{
""channel"" : ""nine"",
""segment"" : ""mobile"",
""food"" : ""pizza""
}");
Then myJson is a JObject (and not a string) where you can call SelectTokens() on
HOWEVER, what you want to achieve can't be achieved with JPath (which is what SelectTokens() uses), so you'd be better off parsing the object directly, something like:
var channelFoodNodes = myJson.Children()
.OfType<JProperty>()
.Where(x => new []{ "channel", "food"}.Contains(x.Name));
Then you can construct a new JObject from the resulting enumerable of JProperties:
var newObject = new JObject(channelFoodNodes);
Which will contain your resulting object.
You can see it all in action in this fiddle
If you want to select the properies this way (as you were trying to do with SelectTokens()), you can also build a simple extension method:
public static IEnumerable<JProperty> SelectRootProperties(this JObject obj, params string[] propertyNames)
{
return obj.Children().OfType<JProperty>().Where(x => propertyNames.Contains(x.Name));
}
And call it like:
myObject.SelectRootProperties("channel", "food");
See it in action in this other fiddle
(or you could also make a simple method which gets the input json string and the property names, construct the JObject, parse the properties and return the string of the resulting object, which seems to be what you are asking, but I'll leave that as an exercise)
Your existing data is a key/value of strings. You can deserialize it to a Dictionary<string, string> and access the pieces you want from the dictionary produced.
var things = JsonConvert.DeserializeObject<Dictionary<string,string>>(json);
Console.WriteLine(things["channel"]);
Console.WriteLine(things["food"]);

Construct a JArray with only strings values without a key in C#

I would highly appreciate if someone helped me out with constructing a JArray in C# by only using values and no key. Here is an example of the what I want:
{[
"ZGVyZWtAcG9zc2tpLmNvbQ==",
"YW5kcmVAbGltYWcubmV0",
"YW5keUBiYW9iYW9tYWlsLmNvbQ==",
"dGVzdEBraW5ub3YuY29t",
"c2hhaG5hd2F6LmFsYW0xM0Bob3RtYWlsLmNvbQ==",
"YnJlYW5uQGVtYWlsLmNvbQ=="
]}
Here is the code I wrote for it but I am getting an exception because when I declare JObject, it requires me to have a key and a value, but i only need the value as I am sending this array as a parameter to an API, and they need it in a specific format.
Here is my code that causes the issue:
var recipients = new JArray();
foreach (var c in retrievedContacts.recipients)
{
var jsonObject = new JObject();
jsonObject.Add(c.id);
recipients.Add(jsonObject);
}
dynamic addToListResponse = await sg.client.contactdb.lists._(listJson.lists[0].id).recipients.post(requestBody: recipients);
The last line sends a post request to SendGrid. Here the list id is valid, everything is ok except for adding json object in the loop. Please Help!
To create a JArray with specified values, you can use JToken.FromObject() to convert your c.id to a JToken, then construct your JArray as follows:
var recipients = new JArray(retrievedContacts.recipients.Select(c => JToken.FromObject(c.id)));
In particular, this works if id is a byte array. In this case Json.Net will convert it to a base64 string. Sample fiddle.
If c.id is already a string (in your question, you don't specify its type), you can skip the call to FromObject() and add it as-is without serialization:
var recipients = new JArray(retrievedContacts.recipients.Select(c => c.id));

Can lambda expressions be used on dynamic list to get results

I have following code:
dynamic jsonData = JObject.Parse(data);
var names= new List<dynamic>();
names= jsonData.Properties().Select(p => p.first_name).ToList();
I am unable to make this work as keep on getting error cannot use lambda. Is there a way to get this result? Or should I not use dynamic here?
Json string:
{"items":[{"id":404,"name":"Ken":{"id":215,"neighbourhood":"Mississauga"}]
,{"id":407,"name":"John":{"id":215,"neighbourhood":"Toronto"}]
,...
You don't need dynamic, I'd advise you not to use it, there's no point.
It appears you have an object with an items property which is an array of objects, and you're trying to grab the name of those objects. Just do this:
var obj = JObject.Parse(data);
var names = obj["items"]
.Select(item => (string)item["name"])
.ToList();
Try it like so
... ((IEnumerable<dynamic>)jsonData.Properties()).Select( ...

JContainer, JObject, JToken and Linq confusion

I am having trouble understanding when to use JContainer, JObject, and JToken. I understand from the "standards" that JObject is composed of JProperties and that JToken is the base abstract class for all of the JToken types, but I don't understand JContainer.
I am using C# and I just bought LinqPad Pro 5.
I have a JSON data source in a file, so I'm deserializing that file's contents successfully using this statement:
string json;
using (StreamReader reader = new StreamReader(#"myjsonfile.json"))
{
json = reader.ReadToEnd();
}
At that point, I take the JSON string object and deserialize it to a JObject (and this might be my mistake--perhaps I need to make jsonWork a JToken or JContainer?):
JObject jsonWork = (JObject)JsonConvert.DeserializeObject(json);
In my JSON data (the string represented by JSON), I have three objects--the top-level object look similar to this:
{
"Object1" : { ... },
"Object2" : { ... },
"Object3" : { ... }
}
Each object is composed of all sorts of tokens (arrays, strings, other objects, etc.), so it is dynamic JSON. (I used ellipses as placeholders rather than muddying up this question wit lots of JSON data.)
I want to process "Object1", "Object2", and "Object3" separately using LINQ, however. So, ideally, I would like something like this:
// these lines DO NOT work
var jsonObject1 = jsonWork.Children()["Object1"]
var jsonObject2 = jsonWork.Children()["Object2"]
var jsonObject3 = jsonWork.Children()["Object3"]
But the above lines fail.
I used var above because I have no idea what object type I should be using: JContainer, JObject, or JToken! Just so you know what I want to do, once the above jsonObject# variables are properly assigned, I would like to use LINQ to query the JSON they contain. Here is a very simple example:
var query = from p in jsonObject1
where p.Name == "Name1"
select p
Of course, my LINQ ultimately will filter for JSON arrays, objects, strings, etc., in the jsonObject variable. I think once I get going, I can use LinqPad to help me filter the JSON using LINQ.
I discovered that if I use:
// this line WORKS
var jsonObject1 = ((JObject)jsonWork).["Object1"];
Then I get an JObject type in jsonObject1. Is this the correct approach?
It is unclear to me when/why one would use JContainer when it seems that JToken and JObject objects work with LINQ quite well. What is the purpose of JContainer?
You don't really need to worry about JContainer in most cases. It is there to help organize and structure LINQ-to-JSON into well-factored code.
The JToken hierarchy looks like this:
JToken - abstract base class
JContainer - abstract base class of JTokens that can contain other JTokens
JArray - represents a JSON array (contains an ordered list of JTokens)
JObject - represents a JSON object (contains a collection of JProperties)
JProperty - represents a JSON property (a name/JToken pair inside a JObject)
JValue - represents a primitive JSON value (string, number, boolean, null)
So you see, a JObject is a JContainer, which is a JToken.
Here's the basic rule of thumb:
If you know you have an object (denoted by curly braces { and } in JSON), use JObject
If you know you have an array or list (denoted by square brackets [ and ]), use JArray
If you know you have a primitive value, use JValue
If you don't know what kind of token you have, or want to be able to handle any of the above in a general way, use JToken. You can then check its Type property to determine what kind of token it is and cast it appropriately.
JContainer is a base class for JSON elements that have child items. JObject, JArray, JProperty and JConstructor all inherit from it.
For example, the following code:
(JObject)JsonConvert.DeserializeObject("[1, 2, 3]")
Would throw an InvalidCastException, but if you cast it to a JContainer, it would be fine.
Regarding your original question, if you know you have a JSON object at the top level, you can just use:
var jsonWork = JObject.Parse(json);
var jsonObject1 = jsonWork["Object1"];
Most examples have simple json and I've googled "C# Newtonsoft parse JSON" more than once.
Here's a bit of a json file I was just asked to parse for a csv. The company name value is nested within many arrays / objects so it is semi-complicated in that regard.
{
"page": {
"page": 1,
"pageSize": 250
},
"dataRows": [
{
"columnValues": {
"companyName": [
{
"name": "My Awesome Company",
}
]
}
}
]
}
var jsonFilePath = #"C:\data.json";
var jsonStr = File.ReadAllText(jsonFilePath);
// JObject implementation for getting dataRows JArray - in this case I find it simpler and more readable to use a dynamic cast (below)
//JObject jsonObj = JsonConvert.DeserializeObject<JObject>(jsonStr);
//var dataRows = (JArray)jsonObj["dataRows"];
var dataRows = ((dynamic)JsonConvert.DeserializeObject(jsonStr)).dataRows;
var csvLines = new List<string>();
for (var i = 0; i < dataRows.Count; i++)
{
var name = dataRows[i]["columnValues"]["companyName"][0]["name"].ToString();
// dynamic casting implemntation to get name - in this case, using JObject indexing (above) seems easier
//var name2 = ((dynamic)((dynamic)((dynamic)dataRows[i]).columnValues).companyName[0]).name.ToString();
csvLines.Add(name);
}
File.WriteAllLines($#"C:\data_{DateTime.Now.Ticks}.csv", csvLines);

How to deserialize JSON to Dictionary using JSON.NET?

I am querying data from http://www.imdbapi.com and would like to parse the result using Json.net library. Could someone please tell me how I can use this library to convert the query response into a Map<string, string>.
With this code I'm able to get all keys, but how can query the values then?
JObject obj = JObject.Parse(response);
IList<string> props = obj.Properties().Select(p => p.Name).ToList();
Try JSON.NET
Just use this:
Dictionary<string, string> movieValues =
JsonConvert.DeserializeObject<Dictionary<string, string>>(responseFromImdbApi);
Just get the values like this:
movieValues["title"]
movieValues["released"]
movieValues["genre"]
Why would you use an external library when it is already available ?
JavaScriptSerializer works great.

Categories