Serializing to JSON - c#

this is my sql result
and i want to serialize the result to json
like following
{
"Studentid": 1000,
"ExamType":[
{
"Examtype":"TERM 2",
"ExamName":[{
"ExamName":"PERIODIC TEST 1-Term2",
"SubjectName": [{
"SubjectName":"SL-MALAYALAM",
"ComponentName":[{
"ComponenetName":"Exam",
"SubComponent":[{
"SubCOmponent":"Exam",
"ExamDate":"2017-08-03",
"MaxMark":"50.00",
"MarkObtained":"38.00",
"Grade":"B1"
}]
},
{
"ComponenetName":"NOTEBOOK",
"SubComponent":[{
"SubCOmponent":"Neatness & upkeep",
"ExamDate":"2017-08-03",
"MaxMark":"2.00",
"MarkObtained":"2.00" ,
"Grade":"A1"
}]
}]
}]
}]
}]
}
how can i serialize the sql result to json in mvc api,i'm already using newtonsoft for serializing other results,using LINQ is better way? if yes how?
my code Looks like

I don't know of any libraries that will do it for you automatically, but you can certainly write code that does something like:
var grouped = results.GroupBy(r => r.StudentID).Select(g => new
{
StudentID = g.Key,
ExamTypes = g.GroupBy(r => r.ExamType).Select(g2 => new
{
ExamType = g2.Key,
ExamNames = g2.GroupBy(r => r.ExamName).Select(g3 => new
{
ExamName = g3.Key,
SubjectNames = g3.GroupBy(r => r.SubjectName).Select(g4 => new
{
SubjectName = g4.Key,
SubComponents = g4.Select(r => new { SubjectComponentName = r.SubjectComponentName, ExamDate = r.ExamDate, MaxMark = r.MaxMark, MarkObtained = r.MarkObtained /* others here */ })
})
})
})
});
var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(grouped);
Note that I've pluralized the names of the properties whose values are collections, but if you're required to keep to the exact property names specified, you can change that easily enough.

Related

How to read Json file using Linq in c#

I have below Json file ,I need to fetch the role_to_secrets of "rec" then I should take respective secrete value for "prod" environment.
Ex:
rec: roles are "resdns","dnsmoon",resmoon", I need to fetch the corresponding "prod" secrets
"prod/user/res_dns.pub","prod/user/resdns.pub","prod/user/res_moon.pub"
{
"secrets":{
"resdns":{
"_type":"ssh_key",
"_rotatable":false,
"test":"test/user/res_dns.pub",
"prod":"prod/user/res_dns.pub"
},
"dnsmoon":{
"_type":"secret",
"_rotatable":false,
"test":"test/user/dnsmoon.pub",
"prod":"prod/user/resdns.pub"
},
"resmoon":{
"_type":"secret",
"_rotatable":false,
"test":"test/user/res_moon.pub",
"prod":"prod/user/res_moon.pub"
},
"rrservice":{
"_type":"secret",
"_rotatable":false,
"test":"test/user/rrs1ervice.pub",
"prod":"prod/user/rrservice8.pub"
},
"mds":{
"_type":"ssh_key",
"_rotatable":false,
"test":"test/user/mds.pub",
"prod":"prod/user/mds.pub"
}
},
"role_to_secrets":{
"rec":[
"resdns",
"dnsmoon",
"resmoon"
],
"kka":[
"resdns",
"dnsmoon",
"resmoon"
],
"zoper":[
"rrservice",
"mds"
]
}
}
I have used tradition way to fetch the data, But is there any simple way by linq
List<string> lstRecursiveRoles = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>(jsonContent["role_to_secrets"]["recursive"].ToString());
List<string> lstSecretValue = new List<string>();
foreach (var recursiveRole in lstRecursiveRoles)
{
lstSecretValue.Add(jsonContent["secrets"][recursiveRole]["prod"].ToString());
}
You can try this code
var jObj = JObject.Parse(json);
string[] roles = ((JObject)jObj["role_to_secrets"]).Properties()
.Where(x => x.Name == "rec")
.Select(s => s.Value.ToObject<string[]>())
.FirstOrDefault();
List<string> secrets = ((JObject)jObj["secrets"]).Properties()
.Where(s => roles.Contains(s.Name))
.Select(s => (string)s.Value["prod"])
.ToList();

Dynamic Elastic search query in c# NEST

Started working on NEST api for elastic search recently, got stuck on following query, the data.e would be dynamically populated using the input from client in the HttpGet,
ex: user sends eventA,eventB,eventC then we would add in the should part:
GET events/_search
{
"_source": false,
"query": {
"bool": {
"must": [
{"range": {
"timestamp": {
"gte": 1604684158527,
"lte": 1604684958731
}
}},
{"nested": {
"path": "data",
"query": {
"bool": {
"should": [
{"match": {
"data.e": "eventA"
}},
{"match": {
"data.e": "eventB"
}},
{"match": {
"data.e": "eventC"
}},
]
}
},
"inner_hits": {}
}}
]
}
}
}
Following is what I came up with till now:
var graphDataSearch = _esClient.Search<Events>(s => s
.Source(src => src
.Includes(i => i
.Field("timestamp")
)
)
.Query(q => q
.Bool(b => b
.Must(m => m
.Range(r => r
.Field("timestamp")
.GreaterThanOrEquals(startTime)
.LessThanOrEquals(stopTime)
),
m => m
.Nested(n => n
.Path("data")
.Query(q => q
.Bool(bo => bo
.Should(
// what to add here?
)
)
)
)
)
));
Can someone please help how to build the should part dynamically based on what input the user sends?
Thanks.
You can replace the nested query in the above snippet as shown below
// You may modify the parameters of this method as per your needs to reflect user input
// Field can be hardcoded as shown here or can be fetched from Event type as below
// m.Field(f => f.Data.e)
public static QueryContainer Blah(params string[] param)
{
return new QueryContainerDescriptor<Events>().Bool(
b => b.Should(
s => s.Match(m => m.Field("field1").Query(param[0])),
s => s.Match(m => m.Field("field2").Query(param[1])),
s => s.Match(m => m.Field("field3").Query(param[2]))));
}
What we are essentially doing here is we are returning a QueryContainer object that will be passed to the nested query
.Query(q => Blah(<your parameters>))
The same can be done by adding this inline without a separate method. You may choose which ever route you perfer. However, in general, having a method of its own increases the readability and keeps things cleaner.
You can read more about Match usage here
Edit:
Since you want to dynamically add the match queries inside this, below is a way you can do it.
private static QueryContainer[] InnerBlah(string field, string[] param)
{
QueryContainer orQuery = null;
List<QueryContainer> queryContainerList = new List<QueryContainer>();
foreach (var item in param)
{
orQuery = new MatchQuery() {Field = field, Query = item};
queryContainerList.Add(orQuery);
}
return queryContainerList.ToArray();
}
Now, call this method from inside of the above method as shown below
public static QueryContainer Blah(params string[] param)
{
return new QueryContainerDescriptor<Events>().Bool(
b => b.Should(
InnerBlah("field", param)));
}

Nest Elasticsearch wildcard query works as querystring but not with fluent API

I have about a hundred test documents in my index, built using NBuilder:
[
{
"title" : "Title1",
"text" : "Text1"
},
{
"title" : "Title2",
"text" : "Text2"
},
{
"title" : "Title3",
"text" : "Text3"
}
]
I want to query them with a wildcard to find all items with "text" starts with "Text". But when I use two wildcard methods in Nest I get two different results.
var response = await client.SearchAsync<FakeFile>(s => s.Query(q => q
.QueryString(d => d.Query("text:Text*")))
.From((page - 1) * pageSize)
.Size(pageSize));
This returns 100 results. But I'm trying to use a fluent API rather than querystring.
var response = await client.SearchAsync<FakeFile>(s => s
.Query(q => q
.Wildcard(c => c
.Field(f => f.Text)
.Value("Text*"))));
This returns 0 results. I'm new to Elasticsearch. I've tried to make the example as simple as possible to make sure I understand it piece-by-piece. I don't know why nothing is returning from the second query. Please help.
Assuming your text field is of type text, then during indexing elasticsearch will store Text1 as text1 internally in the inverted index. Exactly the same analysis will happen when using query string query, but not when you are using wildcard query.
.QueryString(d => d.Query("text:Text*"))) looks for text* and .Wildcard(c => c.Field(f => f.Text).Value("Text*"))) looks for Text* but elasticsearch stores internally only first one.
Hope that helps.
Supposed your mapping looks like that:
{
"mappings": {
"doc": {
"properties": {
"title": {
"type": "text"
},
"text":{
"type": "text"
}
}
}
}
}
Try this (Value should be in lowercase):
var response = await client.SearchAsync<FakeFile>(s => s
.Query(q => q
.Wildcard(c => c
.Field(f => f.Text)
.Value("text*"))));
Or this (don't know if f.Text has text property on it):
var response = await client.SearchAsync<FakeFile>(s => s
.Query(q => q
.Wildcard(c => c
.Field("text")
.Value("text*"))));
Kibana syntax:
GET index/_search
{
"query": {
"wildcard": {
"text": {
"value": "text*"
}
}
}
}

Elasticsearch using NEST - No Results

I am trying to do a simple search in Elasticsearch.
in marvel, I am getting the results back using the following query:
GET /gl_search/poc/_search
{
"query": {
"term": {
"Account_No": "056109"
}
}
}
The type is poc and index is gl_search. See below
{
"took": 28,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 23586,
"max_score": 4.7722025,
"hits": [
{
"_index": "gl_search",
"_type": "poc",
"_id": "AU7fza_MU2-26YcvKIeM",
var node = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(
node,
defaultIndex: "gl_search"
);
When using NEST, I am not getting any results back. I tried the following:
var r = client.Search<poc>(s => s
.From(0)
.Size(10)
.Query(q => q
.Term(p => p.Account_No, "056109")
)
and
var r = client.Search<poc>(query => query.Index("gl_search")
.Type("poc")
.From(0)
.Size(100)
.Filter(x => x.Term(p=> p.Journal_ID, "056109")));
Settings are:
var node = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(
node,
defaultIndex: "gl_search"
);
Update
I dont see anything in Fiddler. I assume I would see something there. If so, is it the way I set it up or something kind of virus protection blocking it
As pointed out in the comments, NEST by default will camelcase .NET object property names when serializing the SearchDescriptor<T> into the query DSL. So, in your example above,
void Main()
{
var settings = new ConnectionSettings(new Uri("http://localhost:9200"));
var connection = new InMemoryConnection(settings);
var client = new ElasticClient(connection: connection);
var r = client.Search<poc>(s => s
.Index("gl_search")
.From(0)
.Size(10)
.Query(q => q
.Term(p => p.Account_No, "056109")
)
);
Console.WriteLine(Encoding.UTF8.GetString(r.RequestInformation.Request));
}
produces the following query DSL (notice the property is account_No)
{
"from": 0,
"size": 10,
"query": {
"term": {
"account_No": {
"value": "056109"
}
}
}
}
Now, there are two real options for addressing this
1.Use a string for the property name, bypassing NEST's camelcasing convention but at the expense of not working with lambda expressions and all of the type safety that they provide
var r = client.Search<poc>(s => s
.Index("gl_search")
.From(0)
.Size(10)
.Query(q => q
.Term("Account_No", "056109")
)
);
2.Change the default serialization options for NEST; useful if all of you properties are cased as per your examples
// change the default camelcase property name serialization
// behaviour with .SetDefaultPropertyNameInferrer
var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
.SetDefaultPropertyNameInferrer(s => s);
var client = new ElasticClient(settings);
var r = client.Search<poc>(s => s
.Index("gl_search")
.From(0)
.Size(10)
.Query(q => q
.Term(p => p.Account_No, "056109")
)
);
A Func<string, string> is passed to .SetDefaultPropertyNameInferrer() that will be called for each property that will be serialized to form the json query DSL that is sent to Elasticsearch.

Create JSON arrays using LINQ

I have a list of objects returning from the database that look like this when serialized using JSON.NET:
"[{"Percentage":0.78, "PartCode":"D40", "InspectionCode":"292", "Make":"TOYOTA"}]
{"Percentage":0.18, "PartCode":"6N", "InspectionCode":"292", "Make":"GM"},
{"Percentage":0.57, "PartCode":"6N", "InspectionCode":"F", "Make":"GM"},
{"Percentage":0.49, "PartCode":"D40", "InspectionCode":"F", "Make":"TOYOTA"},
{"Percentage":0.09, "PartCode":"785", "InspectionCode":"KB", "Make":"CHRYSLER"},
{"Percentage":0.09, "PartCode":"705", "InspectionCode":"KB", "Make":"FORD"},
{"Percentage":0.18, "PartCode":"D40", "InspectionCode":"KB", "Make":"TOYOTA"},
{"Percentage":0.61, "PartCode":"D40", "InspectionCode":"KB", "Make":"TOYOTA"},
{"Percentage":0.39, "PartCode":"705", "InspectionCode":"SB", "Make":"FORD"},
{"Percentage":0.31, "PartCode":"6N", "InspectionCode":"SB", "Make":"GM"},
{"Percentage":0.21, "PartCode":"AW7", "InspectionCode":"XE1", "Make":"CHRYSLER"},
{"Percentage":0.27, "PartCode":"705", "InspectionCode":"XE1", "Make":"FORD"},
{"Percentage":0.28, "PartCode":"UX", "InspectionCode":"XE1", "Make":"FORD"},
{"Percentage":0.56, "PartCode":"D40", "InspectionCode":"XE1", "Make":"TOYOTA"}]"
I need to create two JSON arrays in this format to pass to HighCharts:
var categories = [
{name: "Toyota", categories: ['D40']},
{name: "GM", categories: ['6N']},
{name: "FORD", categories: ['705', 'UX']},
{name: "CHRYSLER", categories: ['AW7','785']}];
var series = [
{name: "292", data = [0.78, 0.18]}
{name: "F", data = [0.57, 0.49]},
{name: "KB", data = [0.09, 0.09, 0.18, 0.61]},
{name: "SB", data = [0.39, 0.31]},
{name: "XE1", data = [0.21, 0.27, 0.28, 0.56]}];
So far, I am doing the nested grouping of data, since Make and PartCode are hierarchical data.
var query = from d in sortedData
group d by d.Make into newgroup1
from newgroup2 in
(from e in newgroup1
group e by e.PartCode)
group newgroup2 by newgroup1.Key;
I am able to see data in a hierarchical format using:
foreach (var outergroup in query)
{
System.Diagnostics.Debug.WriteLine(outergroup.Key);
foreach (var innergroup in outergroup)
{
System.Diagnostics.Debug.WriteLine(innergroup.Key);
foreach (var innerGroupElement in innergroup)
{
System.Diagnostics.Debug.WriteLine("\t\t{0} {1}", innerGroupElement.InspectionCode, innerGroupElement.Percentage);
}
}
}
But, I am having a hard time understanding what to do further to get to the desired JSON arrays. What steps do I have to take further inorder to achieve this?
This will get you what you want. Once you group by the Make or InspectionCode, then all the items in that sub-list will contain the data you are looking for.
var categories = sortedData.GroupBy(d => d.Make)
.Select(g => new
{
name = g.Key,
categories = g.Select(x => x.PartCode).ToArray()
});
var series = sortedData.GroupBy(d => d.InspectionCode)
.Select(g => new
{
name = g.Key,
data = g.Select(x => x.Percentage).ToArray()
});
var categoriesAsJson = Newtonsoft.Json.JsonConvert.SerializeObject(categories);
var seriesAsJson = Newtonsoft.Json.JsonConvert.SerializeObject(series);
If you have the data already in memory, converting it to Json is as easy as the last two lines, using Json.NET. If you are sending this out over the wire via a WebAPI endpoint, then you can just have your endpoint return a list which would be the categories or series list objects without having converted them to JSON.
[HttpGet]
public HttpResponseMessage GetCategories()
{
var categories = GetCategoriesUsingAboveCode();
return Request.CreateResponse(HttpStatusCode.OK, categories);
}
Something like that?
var categories = sortedData
.GroupBy(i => i.Make)
.Select(g => new
{
name = g.Key,
categories = sortedData
.Where(i2 => i2.Make == g.Key)
.Select(i2 => i2.InspectionCode)
});

Categories

Resources