How to print the values not the object IEnumerable<IEnumerable<>()>() - c#

I am trying to display the values of a list which is inside another list, but I am running into issues.
Here is the list containing values that I want to display:
"HotelAvailSideFilterResult": {
"listaServices": [
[
{
"code": "APCO",
"description": "Aptos./Hab. con cocina",
"font": null
},
{
So 2 things:
How do I print the description property using loops or whatever?
How do I query unique values? I tried this but repeat values are returned:
var services = resultado.Hotels.Select(h => h.Features.Distinct());//LISTA DE SERVICIOS
var hotelTypes = resultado.Hotels.Select(h => h.Type.Distinct()).Distinct();//LISTA DE TIPOS DE HOTEL
model resultadoFiltro = new model()
{
listaServices = services.ToList(),
};
Here are my models:
public IEnumerable<IEnumerable<RS.Feature>> listaServices { get; set; }
public partial class Hotel
{
public List<Feature> Features { get; set; }
}
public class Feature
{
[JsonProperty("code")]
public string Code { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("font")]
public string Font { get; set; }
}
Thank you very much

Finally the best way is this:
hotelAvailRS.Hotels.SelectMany(h => h.Features).ToList().GroupBy(f => f.Code).Select(f => f.First()).ToList()
Because you will have what you need and also later you will be able to use the property Code to bind when be required.
Hope it helps!!

Related

How to use POST request to create an object in C# API?

So I'm trying to make this parking ticket system and I'm currently creating these objects manually in the code for testing. Now I've come to the step where my POST request in postman should be able to create an object in the memory of the app. I've got two classes one for a ''Car'' and one for the ''Ticket''.
public class Car
{
public string regNr { get; set; }
public string carBrand { get; set; }
public string carColor { get; set; }
public List<Ticket> ticketlist {get; set;}
public Car()
{
this.ticketlist = new List<Ticket>();
}
public void addNewTicket(Ticket newTicket)
{
ticketlist.Add(newTicket);
}
}
}
public class Ticket
{
public int ticketID { get; set; } = 0;
public DateTime date { get; set; }
public string comment { get; set; }
public int parkingAreaID { get; set; }
public int parkingsOfficerID { get; set; }
}
}
List<Bil> list = new List<Car>();
public void Post([FromBody]Bil val)
{
list.Add(val);
}
The GET requests work and I want to be able to add a new ticket to a registration number by using my post request.
My current output is this;
{
"regNr": "BT66358",
"carBrand": "BMW",
"carColor": "Yellow",
"ticketlist": [
{
"ticketID": 1,
"date": "2020-12-12T17:49:34.4000401+01:00",
"comment": "very bad parking",
"parkingsAreaID": 1,
"parkingsOfficerID": 2
},
{
"ticketID": 2,
"date": "2020-12-12T17:49:34.4000401+01:00",
"comment": "very bad parking",
"parkingsAreaID": 2,
"parkingsOfficerID": 2
}
]
}
---------------UPDATE-----------
Bil = Car
botliste = ticketlist
liste = list
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/q3UaO.png
If I understood you correctly, then most likely you just need to add some attributes, and you get something like this:
List<Car> list = new List<Car>();
[HttpPost]
[HttpPost("SomeRoute")]
public IActionResult Post([FromBody] Car val)
{
if (val == null)
{
return BadRequest("Car data must be filled");
}
if (string.IsNullOrEmpty(val.regNr))
{
return BadRequest("Reg number must be filled");
}
var car = list.FirstOrDefault(c => string.Equals(c.regNr, val.regNr));
if (car != null)
{
car.ticketlist.AddRange(val.ticketlist);
}
else
{
list.Add(val);
}
/// And return IActionResult
return Ok();
}
P.S. Give a description of the Bil type to get a more detailed answer.
upd:
You can also add attributes to the fields of your classes that will validate the values, for example:
...
public class Car
{
[MinLength(6, ErrorMessage = "regNr field must be at least 6 characters")]
public string regNr { get; set; }
...
The "MinLength" attribute indicates that the value for this property is required, and must be at least 6 characters.
More details here: https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=net-5.0
upd2:
This is how I get to invoke this method:

C# parsing multiple json

The JSON data is as follows:
{"Sucess":true,
"Code":0,
"Msg":"Sucess",
"Data":{
"UserDayRanking":
{
"UserID":11452112,
"UserCharm":0,
"UserName":"gay",
"UserGender":1,
"UserLevel":36,
"UserPhoto":"http://res.xxx.com/2020/3/16/63719926625601201487545U11452112.jpeg",
"Ranking":0,
"IsNobility":0,
"NobilityType":0,
"NobilityLevel":0,
"UserShowStyle":null,
"LiveLevelUrl":null,
"IsStealth":false},
"DayRankingList":[
{
"UserID":3974854,
"UserCharm":114858,
"UserName":"jack",
"UserGender":1,
"UserLevel":91,
"UserPhoto":"http://res.xxx.com/2020/2/15/63717400601924412312384U3974854.jpeg",
"Ranking":2,
"IsNobility":1,
"NobilityType":1,
"NobilityLevel":3,
"UserShowStyle":
{
"NameColor":100102,
"BorderColor":100403,
"LiangMedal":0,
"DztCountDown":0,
"Mounts":100204,
"LiveLevelCode":0,
"LiveRights":null
},
"LiveLevelUrl":null,
"IsStealth":false
},
{"UserID":6231512,
"UserCharm":22644,
"UserName":"red.girl",
"UserGender":1,
"UserLevel":57,
"UserPhoto":"http://res.xxx.com/2019/11/20/63709843050801519858823U6231512.jpeg",
"Ranking":3,
"IsNobility":0,
"NobilityType":0,
"NobilityLevel":0,
"UserShowStyle":{
"NameColor":0,
"BorderColor":0,
"LiangMedal":0,
"DztCountDown":0,
"Mounts":0,
"LiveLevelCode":0,
"LiveRights":null
},
"LiveLevelUrl":null,
"IsStealth":false}
],
"LiveCharmSwitch":1,
"IsSelf":false
}
}
I want to use c # extraction
"UserID": 3974854,
"UserCharm": 114858,
"UserName": "jack",
"UserID":6231512,
"UserCharm":22644,
"UserName":"red.girl",
That is to extract UserID, UserCharm, UserName,This json has many layers,
What I want after the extraction is,id is sorted in order
id = 1, UserID = 3974854, UserCharm = 114858, UserName = jack
id = 2, UserID = 6231512, UserCharm = 22644, UserName = red.girl
I use the following code, but only extract the first one
string json = #"{"Sucess":true,"Code":0,"Msg":"Sucess","Data":{"UserDayRanking":{"UserID":11452112,"UserCharm":0,"UserName":"gay","UserGender":1,"UserLevel":36,"UserPhoto":"http://res.xxx.com/2020/3/16/63719926625601201487545U11452112.jpeg","Ranking":0,"IsNobility":0,"NobilityType":0,"NobilityLevel":0,"UserShowStyle":null,"LiveLevelUrl":null,"IsStealth":false},"DayRankingList":[{"UserID":3974854,"UserCharm":114858,"UserName":"jack","UserGender":1,"UserLevel":91,"UserPhoto":"http://res.xxx.com/2020/2/15/63717400601924412312384U3974854.jpeg","Ranking":2,"IsNobility":1,"NobilityType":1,"NobilityLevel":3,"UserShowStyle":{"NameColor":100102,"BorderColor":100403,"LiangMedal":0,"DztCountDown":0,"Mounts":100204,"LiveLevelCode":0,"LiveRights":null},"LiveLevelUrl":null,"IsStealth":false},{"UserID":6231512,"UserCharm":22644,"UserName":"red.girl","UserGender":1,"UserLevel":57,"UserPhoto":"http://res.xxx.com/2019/11/20/63709843050801519858823U6231512.jpeg","Ranking":3,"IsNobility":0,"NobilityType":0,"NobilityLevel":0,"UserShowStyle":{"NameColor":0,"BorderColor":0,"LiangMedal":0,"DztCountDown":0,"Mounts":0,"LiveLevelCode":0,"LiveRights":null},"LiveLevelUrl":null,"IsStealth":false}],"LiveCharmSwitch":1,"IsSelf":false}}";
List<Info> jobInfoList = JsonConvert.DeserializeObject<List<Info>>(z);
foreach (Info jobInfo in jobInfoList)
{
//Console.WriteLine("UserName:" + jobInfo.UserName);
}
public class Info
{
public string UserCharm { get; set; }
public string UserName { get; set; }
public data DayRankingList { get; set; }
}
public class data
{
public int UserID { get; set; }
public string UserCharm { get; set; }
public string UserName { get; set; }
public string UserGender { get; set; }
public string UserLevel { get; set; }
}
The above code only shows username = jack,Never show username = red.girl
As it looks to me then you want some details from your JSON has the which is in DayRankingList. As you only want some data then we can use a tool like http://json2csharp.com/ to create our classes and then remove what we don't need. Then we end up with the following classes.
public class DayRankingList
{
public int UserID { get; set; }
public int UserCharm { get; set; }
public string UserName { get; set; }
}
public class Data
{
public List<DayRankingList> DayRankingList { get; set; }
}
public class RootObject
{
public Data Data { get; set; }
}
Which you can deserialise like this
string json = .....
var root = JsonConvert.DeserializeObject<RootObject>(json);
Then if you wish, you can extract the inner data into a new List<> and then just work on that.
List<DayRankingList> rankingLists = root.Data.DayRankingList;
//Do something with this, such as output it
foreach(DayRankingList drl in rankingLists)
{
Console.WriteLine(String.Format("UserId {0} UserCharm {1} UserName {2}",drl.UserId, drl.UserCharm, drl.UserName));
}
You can use Json.Linq to parse your JSON into JObject and enumerate DayRankingList items (since it's an array). Then convert every item into data class and order the result sequence by UserID
var jObject = JObject.Parse(json);
var rankingList = (jObject["Data"] as JObject)?.Property("DayRankingList");
var list = rankingList.Value
.Select(rank => rank.ToObject<data>())
.OrderBy(item => item?.UserID);
foreach (var user in list)
Console.WriteLine($"{user.UserID} {user.UserName}");
Another way is copy your JSON, go to Edit->Paste Special->Paste JSON as classes menu in Visual Studio and generate a proper class hierarchy (I've got 5 classes, they are quite long to post here), then use them during deserialization
The most type-safe way is to define the class structure that you want, like jason.kaisersmith suggested.
To have the final format you need, though, you might want to do an extra Linq Order and Select, to include the id:
var finalList = rankingLists.OrderBy(rl => rl.UserId).Select((value, index) => new
{
id = index,
value.UserId,
value.UserCharm,
value.UserName
});
foreach (var drl in finalList)
{
Console.WriteLine($"Id = {drl.id}, UserId = {drl.UserId}, UserCharm = {drl.UserCharm}, UserName = {drl.UserName}");
}

NEST Api SearchAfter return null in NEST but works in Kibana

We are using elastic search just for document search in our application so we don't have any one expert in it. I was able to use TermQuery, SimpleQueryStringQuery and MatchPhraseQuery successfully. But I found out in documentation that using From & Size for pagination is not good for production and Search After is recommended.
But my implementation return null. It is confusing for me what should be in <Project> parameter as shown in Nest API Object Initializer Syntax in docs here.
My code looks like this:
var request = new SearchRequest<ElasticSearchJsonObject._Source>
{
//Sort = new List<ISort>
//{
// new SortField { Field = Field<ElasticSearchJsonObject>(p=>)}
//},
SearchAfter = new List<object> {
},
Size = 20,
Query = query
};
Reality is I don't understand this. Over here ElasticSearchJsonObject._Source is the class to map returned results.
My documents are simple text documents and I only want documents sorted according to score so document Id is not relevant.
There was already a question like this on SO but I can't find it somehow.
Update
After looking at answer I updated my code and though query obtained does work. It return result in kibana but not in NEST.
This is the new updated code:
var request = new SearchRequest<ElasticSearchJsonObject.Rootobject>
{
Sort = new List<ISort>
{
new SortField { Field = "_id", Order = SortOrder.Descending}
},
SearchAfter = new List<object> {
"0fc3ccb625f5d95b973ce1462b9f7"
},
Size = 1,
Query = query
};
Over here I am using size=1 just for test as well as hard code _id value in SearchAfter.
The query generated by NEST is:
{
"size": 1,
"sort": [
{
"_id": {
"order": "desc"
}
}
],
"search_after": [
"0fc3ccb625f5d95b973ce1462b9f7"
],
"query": {
"match": {
"content": {
"query": "lahore",
"fuzziness": "AUTO",
"prefix_length": 3,
"max_expansions": 10
}
}
}
}
The response from the ES does say successful but no results are returned.
Results do return in Kibana
Query status is successful
But...
Total returned is 0 in NEST
Sort value is null in kibana I used TrackScores = true to solve this issue
Here is the debug information:
Valid NEST response built from a successful low level call on POST: /extract/_source/_search?typed_keys=true
# Audit trail of this API call:
- [1] HealthyResponse: Node: http://localhost:9200/ Took: 00:00:00.1002662
# Request:
{"size":1,"sort":[{"_id":{"order":"desc"}}],"search_after":["0fc3ccb625f5d95b973ce1462b9f7"],"query":{"match":{"content":{"query":"lahore","fuzziness":"AUTO","prefix_length":3,"max_expansions":10}}}}
# Response:
{"took":3,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}
So please tell me where I am wrong and what can be the problem and how to solve it.
Update 2:
Code in Controller:
Connection String:
var node = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(node);
settings.DisableDirectStreaming();
settings.DefaultIndex("extract");
var client = new ElasticClient(settings);
Query:
var query = (dynamic)null;
query = new MatchQuery
{
Field = "content",
Query = content,
Fuzziness = Fuzziness.Auto,
PrefixLength = 3,
MaxExpansions = 10
};
Query Builder
var request = new SearchRequest<ElasticSearchJsonObject.Rootobject>
{
Sort = new List<ISort>
{
new SortField { Field = "_id", Order = SortOrder.Descending}
},
SearchAfter = new List<object> {
documentid //sent as parameter
},
Size = 1, //for testing 1 other wise 10
TrackScores = true,
Query = query
};
JSON Query
I use this code to get query I posted above. This query is then passed to kibana with GET <my index name>/_Search and there it works
var stream = new System.IO.MemoryStream();
client.SourceSerializer.Serialize(request, stream);
var jsonQuery = System.Text.Encoding.UTF8.GetString(stream.ToArray());
ES Response
string responseJson = "";
ElasticSearchJsonObject.Rootobject response = new ElasticSearchJsonObject.Rootobject();
var res = client.Search<object>(request);
if (res.ApiCall.ResponseBodyInBytes != null)
{
responseJson = System.Text.Encoding.UTF8.GetString(res.ApiCall.ResponseBodyInBytes);
try
{
response = JsonConvert.DeserializeObject<ElasticSearchJsonObject.Rootobject>(responseJson);
}
catch (Exception)
{
var model1 = new LoginSignUpViewModel();
return PartialView("_NoResultPage", model1);
}
}
This is where things go wrong. Above debug information was captured from response
ElasticSearchJsonObject
Some how I think problem might be here somewhere? The class is generated by taking response from NEST in Search request.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace ESAPI
{
public class ElasticSearchJsonObject
{
public class Rootobject
{
public int took { get; set; }
public bool timed_out { get; set; }
public _Shards _shards { get; set; }
public Hits hits { get; set; }
}
public class _Shards
{
public int total { get; set; }
public int successful { get; set; }
public int skipped { get; set; }
public int failed { get; set; }
}
public class Hits
{
public int total { get; set; }
public float max_score { get; set; }
public Hit[] hits { get; set; }
}
public class Hit
{
public string _index { get; set; }
public string _type { get; set; }
public string _id { get; set; }
public float _score { get; set; }
public _Source _source { get; set; }
}
public class _Source
{
public string content { get; set; }
public Meta meta { get; set; }
public File file { get; set; }
public Path path { get; set; }
}
public class Meta
{
public string title { get; set; }
public Raw raw { get; set; }
}
public class Raw
{
public string XParsedBy { get; set; }
public string Originator { get; set; }
public string dctitle { get; set; }
public string ContentEncoding { get; set; }
public string ContentTypeHint { get; set; }
public string resourceName { get; set; }
public string ProgId { get; set; }
public string title { get; set; }
public string ContentType { get; set; }
public string Generator { get; set; }
}
public class File
{
public string extension { get; set; }
public string content_type { get; set; }
public DateTime last_modified { get; set; }
public DateTime indexing_date { get; set; }
public int filesize { get; set; }
public string filename { get; set; }
public string url { get; set; }
}
public class Path
{
public string root { get; set; }
public string _virtual { get; set; }
public string real { get; set; }
}
}
}
I am sure this can be used to get response.
Please note that in case of simple search this code works:
so for this query below my code is working:
var request = new SearchRequest
{
From = 0,
Size = 20,
Query = query
};
Using from/size is not recommended for deep pagination because of the amount of documents that need to be fetched from all shards for a deep page, only to be discarded when finally returning an overall ordered result set. This operation is inherent to the distributed nature of Elasticsearch, and is common to many distributed systems in relation to deep pagination.
With search_after, you can paginate forward through documents in a stateless fashion and it requires
the documents returned from the first search response are sorted (documents are sorted by _score by default)
passing the values for the sort fields of the last document in the hits from one search request as the values for "search_after": [] for the next request.
In the Search After Usage documentation, a search request is made with sort on NumberOfCommits descending, then by Name descending. The values to use for each of these sort fields are passed in SearchAfter(...) and are the values of Project.First.NumberOfCommits and Project.First.Name properties, respectively. This tells Elasticsearch to return documents that have values for the sort fields that correspond to the sort constraints for each field, and relate to the values supplied in the request. For example, sort descending on NumberOfCommits with a supplied value of 775 means that Elasticsearch should only consider documents with a value less than 775 (and to do this for all sort fields and supplied values).
If you ever need to dig further into any NEST documentation, click the "EDIT" link on the page:
which will take you to the github repository of the documentation, with the original asciidoc markdown for the page:
Within that page will be a link back to the original NEST source code from which the asciidoc was generated. In this case, the original file is SearchAfterUsageTests.cs in the 6.x branch

System.Linq.Dynamic - No property or field exists in type ICollection

I'm using System.Linq.Dynamic with EntityFramework. My entities are below:
public class Customer
{
public Customer()
{
CustomerInterests = new List<CustomerInterest>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<CustomerInterest> CustomerInterests { get; set; }
}
public class CustomerInterest
{
public int Id { get; set; }
public int CustomerId { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
}
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
}
Below is my method:
public dynamic Get(long customerId)
{
var query = DbContext.Customers.Include("CustomerInterests").Include("CustomerInterests.Course").AsQueryable();
return query.Where(filter => filter.Id == customerId).Select("new(id,name,customerInterests)");
}
JSON result:
{
"id": 2003,
"name": "name customer",
"customerInterests": [
{
"customerId": 2003,
"courseId": 2,
"course": null,
"id": 2016
},
{
"customerId": 2003,
"courseId": 3,
"course": null,
"id": 2017
}
]
}
I'm trying to load the property Course, but it's always returning null as you can see in JSON result.
How can I create the selector new(....) to load correctly the property Course. I've already tried new (customerInterests.course) as customerInterests.course without success.
Do not forget that I am trying to navigate Customer (object) -> CustomerInterests (Collection) -> for each item load Course (object).
I would appreciate if you could help me on this matter.
I had faced that before, and my conclusion is: in Lambda extension methods you can load only 1st level of related object.
You can get list of customerInterests but you can't get foreign records for this list.
But you may use LINQ query instead.
var query=from c in DbContext.Customers
from ci in c.CustomerInterests
from co in ci.Courses
where /// your conditions
select new {
id=c.id,
name=c.name,
customerInterests= new {
customerId= ci.customerId,
courseId=ci.courseId ,
courses= new {
Name=co.Name
/// Other Courses attributes
}
}
}
EDITED
If you're using EF7 , you're able to load second level of foreign records by using ThenInclude method
db.Customers.Include( customer => customer.Orders). ThenInclude( order=> order.OrderDetails);

How to build a Json tree

Here's the json I want to build:
{
id: 0,
item: [{ id: 1, text: "1111"
}, {
id: 2,
text: "222222",
item: [{ id: "21", text: "child" }]
}, {
id: 3, text: "3333"
}]
}
For now I can not build a tree, I can only build something like this:
[{"id":1,"text":"1111"},{"id":21,"text":"child"]
I'm building it with this code:
var serializer = new JavaScriptSerializer();
var jsonString = serializer.Serialize(listOfPairsOfTextAndId);
But how can I build the given tree in c#?
Først issue is that it is somewhat difficult to use an anonymous object to describe your graph i C#. But you can create a class instead:
class Item {
public Int32 id { get; set; }
public String text { get; set; }
public Item[] item { get; set; }
}
Then you can create your object graph:
var graph = new Item {
id = 0,
item = new[] {
new Item {
id = 1,
text = "1111"
},
new Item {
id = 2,
text = "222222",
item = new[] {
new Item {
id = 21,
text = "child"
}
}
},
new Item {
id = 3,
text = "3333"
}
}
};
You are using the Microsoft JSON serializer which will result in this JSON:
{"id":0,"text":null,"item":[{"id":1,"text":"1111","item":null},{"id":2,"text":"222222","item":[{"id":21,"text":"child","item":null}]},{"id":3,"text":"3333","item":null}]}
This is not exactly what you want because of the extra null values. To work around this you can use JSON.NET instead. You have to add some extra attributes to the class being serialized:
class Item {
public Int32 id { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public String text { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public Item[] item { get; set; }
}
Serialization is then performed using this code:
var jsonString = JsonConvert.SerializeObject(graph);
You get this JSON:
{"id":0,"item":[{"id":1,"text":"1111"},{"id":2,"text":"222222","item":[{"id":21,"text":"child"}]},{"id":3,"text":"3333"}]}
Except for the syntax errors in your question this is what you are asking for.
You need to create some objects that contain the data you want to serialize into that format. I used an online generator to create a couple of quick classes, refactor as necessary for your use.
Here are the classes
public class Item2
{
public string id { get; set; }
public string text { get; set; }
}
public class Item
{
public int id { get; set; }
public string text { get; set; }
public List<Item2> item { get; set; }
}
public class RootObject
{
public int id { get; set; }
public List<Item> item { get; set; }
}
You'll be creating the RootObject first and setting it's properties as necessary (make sure to init the List). After that, simply do the same for the other classes that will make up your output.
After you are done, simple Serialize the RootObject instance and you'll get the output requested.
I used http://json2csharp.com/ to generate those classes based on the desired JSON output.
You can create function like this
public static string GetJSON(object obj)
{
var oSerializer = new JavaScriptSerializer();
var sbJsonResults = new StringBuilder();
oSerializer.Serialize(obj, sbJsonResults);
return sbJsonResults.ToString();
}
And then call this function.
Edit 1
You can create a custom class in which form you want to use.
Create List<class> of that and pass it in the above function
It will work.

Categories