Can't fetch json object data - c#

I have this json object that looks like this below but I can't figure out how to acccess the objects data
I want to access the 5th index like this
jsonContent[5]
error CS0021: Cannot apply indexing with [] to an expression of type 'object'

It looks like an object rather than array. So try jsonContent["5"] or jsonContent["[5]"] instead ?

Here is what is needed, using the JObject library.
var currency = (jsonContent["quotes"] as JObject)["USD"].Value<decimal>();

Related

Picking Out Simple Properties from Hierarchical JSON Part II

I thought this question might be ill-formed, so I created a "sister" question that's far more to-the-point, which specific output cited. Please see to:
Querying JSON Nested Arrays with Linq, JSON.NET, C#
If that question gets answered before this one, I'll try to answer this one myself using the information from the other question... :) Thanks!
In a previous question (Picking Out Simple Properties from Hierarchical JSON), I asked how to pick up simple properties from hierarchical JSON. The answer there [pasted as a Linq query at the end of this post] is extremently useful (and, since posting that, I've studied quite a bit about Linq and JSON.NET). So I'm not using this forum because I'm lazy--I use it when I'm really stuck and can't seem to find the answers in the books I have access to.
I'm stumped on how to continue with the example provided by my previous question. This question builds on the previous one, so here (as succinctly as I could express) is what I'd love to learn how to do in a single Linq query.
To recap: I'm working with dynamic JSON like this (it is more complex than the JSON presented in my earlier Part I question because it contains arrays):
{
"Branch1": {
"Prop1A": "1A",
"Prop1B": "1B",
"Prop1C": "1C",
"Branch2": {
"Prop2A": "2A",
"Prop2B": "2B",
"Prop2C": "2C",
"Branch3": [{
"Prop3A": "3A",
"Prop3B": "3B",
"Prop3C": "3C"
}, {
"Prop3D": "3D",
"Prop3E": "3E",
"Prop3F": "3F"
}, {
"Prop3G": "3G",
"Prop3H": "3H",
"Prop3I": "3I"
}]
},
"Branch4": [{
"Prop4A": "4A",
"Prop4B": "4B",
"Prop4C": "4C"
}, {
"Prop4E": "4E",
"Prop4F": "4F",
"Prop4G": "4G"
}, {
"Prop4H": "4H",
"Prop4I": "4I",
"Prop4I": "4I"
}]
}
}
As you can see, the dynamic JSON is composed of hierarchical objects, and these objects are JSON objects, JSON arrays, and JSON properties.
Ultimately, I want to convert this JSON to a List object I can work with in C#. I plan to use that List object to effectively process each JSON branch in document order from the top, down.
Each item in the List collection would be a JObject; each of these objects would have a synthetic "parent" string property that would point back to the branch under which that JObject appears in the original JSON (my examples below explain what I mean by "parent"). [The previous question correctly came up with a solution for this "parent" value, so that's not too relevant to this question... What's new/relevant here is dealing with JArray objects in the JSON...]
The key is that I want each List item object to contain only the string-value properties for the object. For example, Branch1 has string properties Prop1A, 1B, and 1C. Thus, I would want query[0] to contain:
{"Prop1A":"1A","Prop1B":"1B","Prop1C":"1C", Parent:""}
Next, I would want query[2] to contain the string-value properties for Branch2:
{"Prop2A":"2A","Prop2B":"2B","Prop2C":"2C", Parent:"Branch1"}
Next, I would want query[2] to contain the string properties for only Branch3, but because Branch3 is an array of objects, I would want that array to end up together in query[2]:
[
{"Prop3A": "3A","Prop3B": "3B","Prop3C": "3C"},
{"Prop3D": "3D","Prop3E": "3E","Prop3F": "3F"},
{"Prop3G": "3G","Prop3H": "3H","Prop3I": "3I"}
]
Notice that this Branch doesn't yet have a reference to its "Parent"... I'd be happy getting an array in query[2] that looks like the above. (I plan to use dbc's logic to add a "Parent" property to each array element or figure out a way to create a new JObject that contains the array and cites the Parent only once):
[{"Prop3A": "3A","Prop3B": "3B","Prop3C": "3C","Parent":"Branch2"},
{"Prop3D": "3D","Prop3E": "3E","Prop3F": "3F","Parent":"Branch2"},
{"Prop3G": "3G","Prop3H": "3H","Prop3I": "3I","Parent":"Branch2"}
]
So, as you can see:
I would want any JSON branch that is not an array to be inserted as a new JObject in the query result along with only its string properties and a reference to its parent branch.
I would want any JSON branch that is an array to be inserted as a new JObject array in the query result along with only its string properties and a reference to its parent branch.
The trouble I had solving this on my own has to do with figuring out how to create the "if myJsonObject is JArray" condition in Linq and assigning just the string-property properties when the branch is not an array and iterating the array's elements when it is a JArray. I suspect I need to somehow leverage the ? : ternary expression, but I don't exactly know how to do that.
The query from the previous question is here:
var query3 = from o in root.DescendantsAndSelf().OfType<JObject>() // Find objects
let l = o.Properties().Where(p => p.Value is JValue) // Select their primitive properties
where l.Any() // Skip objects with no properties
// Add synthetic "Parent" property
let l2 = l.Concat(new[] { new JProperty("Parent", o.Ancestors().OfType<JProperty>().Skip(1).Select(a => a.Name).FirstOrDefault() ?? "") })
select new JObject(l2); // And return a JObject.
var list3 = query3.ToList();
That code doesn't handle arrays in the way described above.
A suitable and more general answer to this question effectively appears in my related post:
Picking Out Simple Properties from Hierarchical JSON

How to Pass Raw JSON to RegisterPercolator in ElasticSearch NEST?

I am trying to create an object-agnostic Percolator microservice in C#. I can create and map an index passed into my method using a JSON object for the mapping, I can even then register a percolator against the index using the standard NEST query format, such as this:
var percolateResponse = client.RegisterPercolator<dynamic>(query
.Name, p=>p
.Index(index.ActualName)
.Query(q=>q
.Term(t=>t
.OnField("banana")
.Value("green"))));
The problem is, I need to be able to pass in a JSON of the query and I've been trying to use the following code:
var percolateResponse = client.RegisterPercolator<dynamic>(query
.Name, p=>p
.Index(index.ActualName)
.Query(q=>q.Raw(query.Context)));
The JSON that I am passing in is:
{"query":
{"term":
{"banana":
{"value": "green"}
}
}
}
What is happening though, instead of that registering the specified query as a percolator query, it sets the Query value of the RegisterPercolatorRequest to null and basically cancels out the query if it exists.
I have tried a number of different formats of the JSON, and have not found something that the .Query(q=>q.Raw(query.Context)) liked.
Does ANYONE know how to solve this? I have moved closer and closer to a solution to this, but just never quite gotten past this one issue. It is my last remaining roadblock to creating a percolator proxy class. Any help is appreciated. Thanks.
Just for reference, the error I am getting (deep inside the Response object) is this:
[<indexName>] failed to parse query [<queryName>]];
nested: QueryParsingException[[<indexName>] [_na] query malformed,
no field after start_object
I think I have found the solution to what I am trying to do... the above query didn't work, but through some trial and error, I have found out what DOES work to take in an object-agnostic query and register a percolator:
var percolateResponse = client.RegisterPercolator<dynamic>(query
.Name, p => p
.Index(index.ActualName)
.Query(q => q
.SimpleQueryString(sqs=>sqs
.Query(query.Context))));
This creates the Query as a Simple Query String and stores it as such in ElasticSearch, and appears to then work when I try to percolate a document that would match that query!

Linq from XML to Object with one Element ToList<>

I am trying to perform a LINQ Query on an xml document. The output should be a list of type Quiz. One of the members of the object is also a list but it is List of type string. The code works for everything but the one element that needs to be put into a List of type string. I have looked everywhere and can't seem to find the right syntax for this scenario.
The Code looks something like this.
public static List<Quiz> QuizBuilder()
{
XDocument data = XDocument.Load("Data.xml");
List<Quiz> newquiz = (from d in data.Descendants("Object")
select new Quiz(
(string)d.Element("Question"),
(string)d.Element("Answer"),
//This next line does not work
(List<string>)d.Elements("Choice")
)).ToList();
return newquiz;
}
the xml looks something like this.
<Root>
<Object>
<Question>Question 1</Question>
<Answer>a</Answer>
<Choice>a</Choice>
<Choice>b</Choice>
<Choice>c</Choice>
<Choice>d</Choice>
</Object>
</Root>
the code doesn't show any errors till run time then I get a casting error when the code is run.
Unable to cast object of type 'd__11' to type 'System.Collections.Generic.List`1[System.String]'.
Any help is appreciated.
You can not cast d.Elements("Choice") to List<string>
Use this
Choices = d.Elements("Choice").Select(x=>(string)x).ToList()
instead of
(List<string>)d.Elements("Choice")

Convert Json string with different property names depending on query

I have a string of Json which looks like this:
{"Linkin Park":{"mbid_id":"f59c5520-5f46-4d2c-b2c4-822eabf53419","artistbackground":
[{"id":"34862","url":
"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-4fcd2d8e4764f.jpg",
"likes":"4"},{"id":"3953","url":
"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-4de522dac2c13.jpg","likes":"1"},{"id":"3954","url":
"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-4de523586c57e.jpg","likes":"1"},{"id":"4251","url":"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-4dea53983f4cf.jpg","likes":"1"},{"id":"30196","url":
"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-4fabb53ba80a3.jpg","likes":"1"},{"id":"52251","url":
"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-5058a071d9782.jpg","likes":"1"},{"id":"52252","url":"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-5058a071d9f41.jpg","likes":"1"},{"id":"52254","url":
"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-5058a0ebd0350.jpg","likes":"1"},{"id":"52255","url":"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-5058a0ebd098b.jpg","likes":"1"},{"id":"63902","url":
"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-50c2f127eeae6.jpg","likes":"1"},{"id":"3951","url":"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-4de521f409aa2.jpg","likes":"0"},{"id":"3952","url":
"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-4de5225cdd595.jpg","likes":"0"},
{"id":"28038","url":"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-4f8ecbd3c004d.jpg","likes":"0"},{"id":"88261","url":"http://assets.fanart.tv/fanart/music/f59c5520-5f46-4d2c-b2c4-822eabf53419/artistbackground/linkin-park-51d1d122f3029.jpg","likes":"0"}]}}
If you did have the time to read all of that, you might see my problem. There are Json nodes which change name depending on what query I send. This time I sent "Linkin Park", and I got nodes named Linkin park. If I then were to send a query of Madonna I would end up with nodes called Madonna.
How would you go about de-serializing this into classes?
You have a few options:
Dictionary
dynamic
JObject, and then deserialize to strongly-typed one node down (assuming the subtree is static in its structure and naming) (this is based on Newtonsoft.JSON)
What library are you using for JSON deserialization?
Example of #1 using Json.NET:
var json = JsonConvert.DeserializeObject<Dictionary<string, StronglyTyped>>(jsonStr);
var data = json.Values.FirstOrDefault();
Example of #3 using Json.NET:
var json = JsonConvert.DeserializeObject<JObject>(jsonStr);
var topProp = json.Properties().FirstOrDefault().Name;
var data = json[topProp].ToObject<StronglyTyped>();
Since you know the query, the simplest solution would be to replace {"%QUERY%":{ with {"MyCommonDeserializableClass":{ before deserializing the string. :-)
But you should make sure that the JSON contains only one object like this. Logically it seems they return the artist name because several artists may match the query.

Unable to write a Where Clause with Linq where data contains an array

I am trying to write a where clause in Linq that matches on a date. The values for date is contained in a nested object. What I mean is that the object that contains date has two elements start and finish. I am getting two error messages:
Cannot implicity convert System.Collections.Generic.IEnumerable to bool
Cannot convert lambda expression to delegate type System.Func because some of the return types in the block are not implicity convertible to the delegate return type
My code is:
var locationName = from relocate in relocations where **relocate.Relocations.
Where(c=>c.TimeIntervals.Select(d=>d.Start==sh.StartTime.Date))**
select relocate.Relocations.Select(a=>a.Path.Items.
Select(b=>b.DisplayString.Skip(4).SingleOrDefault()));
It is the bit which is between the double **.
Please help!!!
I think you simply have to add a First() or FirstOrDefault() at the end of the statement.
Your relocate.Relocations.Where returns IEnumerable. You need to compare (intersect) that with something, such that result evaluates to a boolean.
Perhaps like this:
relocate.Relocations.Where(...).Any()
var locationName = from relocate in relocations where **relocate.Relocations.
Where(c=>c.TimeIntervals.Select(d=>d.Start==sh.StartTime.Date).Any()).Any()**
select relocate.Relocations.Select(a=>a.Path.Items.
Select(b=>b.DisplayString.Skip(4).SingleOrDefault()));
There is a disconnect between date and location - I have managed to solve this by using a dictionary class and then matching on the date!

Categories