linq query where some from values might be missing - c#

I'm pulling contacts from JSON, there are contacts inside Data that might contain emails and phone numbers.
If they do the following query works perfectly:
var results = from d in json.Data
from c in d.Contacts
from e in c.Emails
from p in c.Phones
select new IoModel {Email = e.EmailValue, Id = d.Id, FullName = string.IsNullOrEmpty(c.Name) ? c.Name : d.DisplayName, Phone = p.PhoneNumber, DOB = d.CustomDOB};
If the Data doesn't have Emails or Phones then it runs with no error but it doesn't return the Contact Name.
Is there a way to do this without using if and and's to keep the code shorter and cleaner?
Here is an example of the JSON with a missing email:
{
"data": [
{
"addresses": [],
"contacts": [
{
"created_by": "user_ltUL3eXGPRWb5ghDeGTfOe9qjW0LeE2e4ouopLcSSWj",
"date_created": "2017-12-28T17:13:00.392000+00:00",
"date_updated": "2017-12-28T17:13:58.453000+00:00",
"emails": [],
"id": "cont_6hxxhz51ctlTnHfo8gA8cce0rthS1dJy1kguKAj148s",
"integration_links": [
{
"name": "LinkedIn Search",
"url": "https://www.linkedin.com/search/results/index/?keywords=John%20Doe"
}
],
"lead_id": "lead_3UzfxCgQHmw4BlGwElitHhlP6E7q9Tg3sdSkTl1CIXp",
"name": "John Doe",
"organization_id": "orga_PvgGx1opSZDsCHl73P8OoSFZUlJ3qsNGI2kwgoObM17",
"phones": [
{
"phone": "+15555555555",
"phone_formatted": "+1 555-555-5555",
"type": "mobile"
}
],
"title": "Main Service",
"updated_by": "user_ltUL3eXXPRWb5shDeGTfOe9qjP0LeE2e4ouopLcSSWj",
"urls": []
}
],
"created_by": "user_ltUL3eXXPRWb5shDeGTfOe9qjW0LeE2e4ouopLcSSWP",
"created_by_name": "John",
"custom": {
"Date Of Birth": "1901-08-01",
"Department": "Main Service",
"Initial Service": "Main Service"
},
"custom.lcf_ufMH5ZhlR99zcdvJxKMxdgxcIbV4wtgTb3EdWDEkL8g": "1971-08-17",
"date_created": "2017-12-28T17:13:00.388000+00:00",
"date_updated": "2017-12-29T21:41:53.840000+00:00",
"description": "",
"display_name": "John Doe",
"html_url": "https://app.close.io/lead/lead_3UzfxCgQHmw4BlGwElitHhlP6E7q9Tg3sdSkTl1CIXp/",
"id": "lead_3UzfxCgQHmw4BlGwElitHhlP5E7q9Tg3sdSkTl1CIXp",
"integration_links": [
{
"name": "Google Search",
"url": "http://google.com/search?q=John%20Doe"
}
],
"name": "",
"opportunities": [],
"organization_id": "orga_AvcGx4opSZDsCHl73H8OogDDUlJ3qsNGI2kwgoObA17",
"status_id": "stat_LpJu8FO72WgIob4qDDVnS4GEieoU41zmQ8xBquTvusm",
"status_label": "Established Patient",
"tasks": [],
"updated_by": "user_0JFRnl8QRvRhMhAlLz4JJxgmrzPeLs3xboxYyj5Pm80",
"updated_by_name": "BT",
"url": null
}
],
"has_more": false,
"total_results": 1
}

Since I don't know what you are deserializing your JSON into, I can only guess at a solution here, but I think if you use DefaultIfEmpty() on your Emails and Phones collections and then use the null-conditional operator on the EmailValue and PhoneNumber, you should get the result you want:
var results = from d in json.Data
from c in d.Contacts
from e in c.Emails.DefaultIfEmpty()
from p in c.Phones.DefaultIfEmpty()
select new IoModel
{
Email = e?.EmailValue,
Id = d.Id,
FullName = string.IsNullOrEmpty(c.Name) ? c.Name : d.DisplayName,
Phone = p?.PhoneNumber,
DOB = d.CustomDOB
};

Let me explain the issue you are running into. Imagine the following two lists:
Issue
var ids = new List<int> { 1, 2, 3, 4 };
var names = new List<string>();
Let's write a Linq query to get all the ids along with names:
var idsAndNames = from id in ids
from name in names
select new { Id = id, Name = name };
The above query will return no results. Why? This is more obvious if you were to use a foreach instead of Linq. The foreach version will look roughly like below:
foreach (var id in ids)
{
foreach (var name in names)
{
//select new { Id = id, Name = name };
}
}
It is very clear and obvious why no results will be returned in the foreach version above (the code within the inner loop will not be executed because the names list is empty).
Fix
We need to tell our query: If there are no items in names collection, we will accept the default. We can do that using DefaultIfEmpty method.
var idsAndNames = from id in ids
from name in names.DefaultIfEmpty()
select new { Id = id, Name = name };
The above will return 4 results. It is sort of like doing the following:
foreach (var id in ids)
{
if (names.Any())
{
//select new { Id = id, Name = name };
}
else
{
//select new { Id = id, Name = "" }; // <-- Notice empty string
}
}
Answer to your question
#BrianRogers has answered your question but I elaborated on what the issue is.

Related

How to create json string from C# list of object with specific properties?

Consider I have below values in the drop-down (dropDownList variable) and user selected values are in selectedDropDownValues list.
And DB api returns the list of customers (customerDBList variable).
Now the requirement is to build JSON from selected values as mentioned below -
var dropDownList = new[] { "Customer.Id", "Customer.FirstName", "Customer.LastName", "Customer.Address.AddressLine1", "Customer.Address.AddressLine2" };
var selectedDropDownValues = new[] { "Customer.Id", "Customer.FirstName", "Customer.Address.AddressLine1" };
var customerDBList = new List<Customer>(){
new Customer {
Id=1,
FirstName="John",
LastName="Desouza",
Address=new Address{
AddressLine1="1 Street",
AddressLine2="Linking Road"
}},
new Customer {
Id=2,
FirstName="Sam",
LastName="Lewis",
Address=new Address{
AddressLine1="Fedral Highway",
AddressLine2="Louisville"
}
}};
Expected JSON output as -
[
{
"Customer": {
"Id": 1,
"FirstName": "John",
"Address": {
"AddressLine1": "1 Street"
}
}
},
{
"Customer": {
"Id": 2,
"FirstName": "Sam",
"Address": {
"AddressLine1": "Fedral Highway"
}
}
}
]
As it happens, your selectedDropDownValues strings "Customer.Address.AddressLine1" are in JSONPath syntax, so you could convert your Customer objects to an intermediate JObject then prune unwanted values using JsonExtensions.RemoveAllExcept(this JObject obj, IEnumerable<string> paths) from this answer to How to perform partial object serialization providing "paths" using Newtonsoft JSON.NET.
Note that your desired JSON has an extra level of nesting { "Customer": { ... } } not present in your data model, so it will need to be manually inserted before filtering:
var rootName = "Customer";
var query = customerDBList
// Convert to JObject
.Select(c => JObject.FromObject(c))
// Add additional level of object nesting { "Customer": { ... } }
.Select(o => new JObject( new JProperty(rootName, o)))
// Remove all but selected properties.
.Select(o => o.RemoveAllExcept(selectedDropDownValues));
var json = JsonConvert.SerializeObject(query, Formatting.Indented);
Working sample .Net fiddle here which shows the generated JSON to be, as required,
[
{
"Customer": {
"Id": 1,
"FirstName": "John",
"Address": {
"AddressLine1": "1 Street"
}
}
},
{
"Customer": {
"Id": 2,
"FirstName": "Sam",
"Address": {
"AddressLine1": "Fedral Highway"
}
}
}
]

Making a diff of JSON objects with lists return object instead of array

I'm trying to compare two C# objects by converting them to JSON and then making a diff. It works fine for all primitives but I don't understand why when I change items in a list that had items the comparison is retuning an object instead of an array.
I'm going to list 3 cases, the first 2 go by as expected:
Case 1 - previous list was empty and one item was added later
var company = new Company(15)
{
Name = "Sega"
};
var previousCat = new Category(1)
{
Name = "Cat X",
Department = new Department(100)
{
Name = "Department 100",
Company = company
},
Companies = new List<Company>()
};
var currentCat = new Category(1)
{
Name = "Cat XYZ",
Department = new Department(100)
{
Name = "Department 100",
Company = company
},
Companies = new List<Company> { company }
};
Result as expected*
// New Value:
[
{
"Name": "Sega",
"Categories": null,
"Id": 15
}
]
// Old value:
[]
Case 2 - previous list had one item that was later removed
var company = new Company(15)
{
Name = "Sega"
};
var previousCat = new Category(1)
{
Name = "Cat X",
Department = new Department(100)
{
Name = "Department 100",
Company = company
},
Companies = new List<Company>
{
company
}
};
var currentCat = new Category(1)
{
Name = "Cat XYZ",
Department = new Department(100)
{
Name = "Department 100",
Company = company
},
Companies = new List<Company>()
};
Result as expected*
// New value:
[]
// Old Value:
[
{
"Name": "Sega",
"Categories": null,
"Id": 15
}
]
Case 3 - both lists have items
var sega = new Company(15)
{
Name = "Sega"
};
var sony = new Company(30)
{
Name = "Sony"
};
var nintendo = new Company(45)
{
Name = "Nintendo"
};
var microsoft = new Company(60)
{
Name = "Microsoft"
};
var previousCat = new Category(1)
{
Name = "Cat X",
Department = new Department(100)
{
Name = "Department 100",
Company = sega
},
Companies = new List<Company>
{
sega,
nintendo
}
};
var currentCat = new Category(1)
{
Name = "Cat XYZ",
Department = new Department(100)
{
Name = "Department 100",
Company = sega
},
Companies = new List<Company>
{
nintendo,
sony,
microsoft
}
};
Result NOT as expected*
// New value:
{
"0": {
"Name": "Nintendo",
"Id": 45
},
"1": {
"Name": "Sony",
"Id": 30
},
"2": {
"Name": "Microsoft",
"Categories": null,
"Id": 60
},
"## Count": 3
}
// Old value:
{
"0": {
"Name": "Sega",
"Id": 15
},
"1": {
"Name": "Nintendo",
"Id": 45
},
"## Count": 2
}
As you can see the third result returns an object, not an array. Why is that?
I'm using this class:
https://github.com/khalidsalomao/SimpleHelpers.Net/blob/master/SimpleHelpers/ObjectDiffPatch.cs
This way:
var diff = ObjectDiffPatch.GenerateDiff(Previous,Current);
That's probably because of the way the code you linked (ObjectDiffPatch) builds up the array diff object. This is the relevant part, as I recreated it in LINQPad:
I added this as an image because that tooltip is important here. But let's start at the top:
If one of the arrays is empty, it properly keeps the original structure: grabs the two arrays (one of which is empty), and adds them under the field name "Companies".
If, however, neither are empty, then it starts comparing their contents, piece by piece. And when a difference is found, the code adds it to the diff object using AddNewValuesToken, which expects a field name as its third parameter, but here we don't have a field name, just an array index.
Now, the code "solves" this by converting the array index to string, and treating it as a field name. No wonder that the resulting JObject will treat it that way too – this is why you see an object in the output, where what used to be array indices, become fields.

give names to a var object in .net

just wondering I am using a web method to return some json data to a web form. anyway I don't want the entire class in json just two of the columns. so I used linq to get the columns here is an example.
IEnumerable<Person> model = new List<Person>
{
new Person { id = 1, Name = "Bryan", Phone = "218-0211", Email = "bryan#mail.mil" },
new Person { id = 2, Name = "Joe", Phone = "248-0241", Email = "joe#mail.mil" },
new Person { id = 3, Name = "Fred", Phone = "354-0441", Email = "fred#mail.mil" },
new Person { id = 4, Name = "Mary", Phone = "344-3451", Email = "mary#mail.mil" },
new Person { id = 5, Name = "Jill", Phone = "127-3451", Email = "jill#mail.mil" }
};
var mysubset = from a in model select new { a. Name, a.Email };
unfotunately when I then serialize my result and send it back I lose the column names. so data.name doesn't work so I was wondering can I give names to a var type? for example is there a way to do this?
var mysubset = from a in model select new { a. Name, a.Email };
string myname as string;
foreach (var item in mysubset)
{
myname = subset.Name;
}
ok here is the actual code sorry it is in vb but that is the project I inherited
Dim mycollection = From a in cmpnyList select {a.CmpnyName, a.ShipFrom}
return jsSerialize.Serialize(mycollection)
the json returned from that is
[{"Company A","New York"},{"Company B", "Harrisburg"}]
so I'm trying to get back something like
[{"CmpnyName":"Company A", "ShipFrom": "New York"},
{"CmpnyName": "Company B", "ShipFrom": "Harrisburg}]
I believe you're using json.net the answer would be something like this using jobject(newtonsoft.json.linq)
JObject o = new JObject(
new JProperty("PersonssList",
new JArray(
from p in model
select new JObject(
new JProperty("PersonName", p.Name),
new JProperty("Email", p.Email)))));
and the result would be this json
{
"PersonssList": [
{
"PersonName": "Bryan",
"Email": "bryan#mail.mil"
},
{
"PersonName": "Joe",
"Email": "joe#mail.mil"
},
{
"PersonName": "Fred",
"Email": "fred#mail.mil"
},
{
"PersonName": "Mary",
"Email": "mary#mail.mil"
},
{
"PersonName": "Jill",
"Email": "jill#mail.mil"
}
]

C# MongoDb - Output id field properly?

I don't understand how can I output id properly when displaying objects. It's either ObjectId() that I can't parse back to object or some object that has id in it that I can parse but it looks weird.
Note that mapping it to class is not an option, it needs to be fully dynamic because different users have different fields.
Code
public List<object> Get()
{
var client = new MongoClient("mongodb://localhost");
var server = client.GetServer();
var database = server.GetDatabase("api_test");
var collection = database.GetCollection("users");
var json = collection.FindAllAs<BsonDocument>().ToJson(new JsonWriterSettings { OutputMode = JsonOutputMode.Strict });
var obj = JsonConvert.DeserializeObject<List<object>>(json);
return obj;
}
Example
[
{
"_id": {
"$oid": "528e7f9bb1fece903aa9b246"
},
"Name": "Steve",
"Age": 60
},
{
"_id": {
"$oid": "528e7fabb1fece903aa9b247"
},
"Name": "Alice",
"Age": 44
}
]
What I would like
[
{
"_id": "528e7f9bb1fece903aa9b246",
"Name": "Steve",
"Age": 60
},
{
"_id": "528e7fabb1fece903aa9b247",
"Name": "Alice",
"Age": 44
}
]
You can do this by explicitly updating your query result to convert _id to a string before it's serialized to JSON:
var docs = test.FindAll().ToList();
foreach (var doc in docs)
{
doc["_id"] = doc["_id"].ToString();
}
var json = docs.ToJson(
new JsonWriterSettings { OutputMode = JsonOutputMode.Strict });

How to display indirect data in Jqgrid

I am implementing Jqgrid in my ASP.net MVC web application. I have data some thing like this:
SID SNAME CITY
1 ABC 11
2 XYZ 12
3 ACX 13
4 KHG 14
5 ADF 15
6 KKR 16
and another table
CID CNAME
11 Chennai
12 Mumbai
13 Delhi like this
but, in the grid i would like to display like this:
SID SNAME City
1 ABC Chennai
2 XYZ Mumbai
3 ACX Delhi
4 KHG Banglore
5 ADF Hyderabad
6 KKR Kolkatta
I was not able to use join because the class structure is like this:
Class Student
{
long sid,
string sname,
long city
}
So, when i am reading from the data base i am getting the city id not city name.
But, i would like to display city name instead of City ID in the grid data to end user
i need some thing like a lookup function so that before binding data to the jQgrid,the city id will be mapped with city name and displays it instead of displaying ID
I didnt find a way to get this done.
Please help..
The controller method i am using is as follows:
public JsonResult Students()
{
List<Students> liStudents = new List<Students>();
SortedList<long, string> slLocations = new SortedList<long, string>();
slLocations = Students.LoadLocations();
liStudents = Students.GetStudents();
return Json(liStudents,JsonRequestBehavior.AllowGet);
}
How to modify the return statement to throw slLocations too in the json response
I answered already on the closed question before (see here). Nevertheless I decide to answer on your question in detail because the problem which you describe is really very common.
I start with reminding that jqGrid provides formatter: "select" which uses formatoptions.value or editoptions.value to decode ids to texts. The formatter: "select" uses value and optional separator, delimiter and defaultValue properties, but it can't uses editoptions.dataUrl to get required data from the server instead of usage static value. The problem is very easy: processing dataUrl works asynchronous, but during formatting of the column of grid body one don't support delayed filling. So to use formatter: "select" one have to set formatoptions.value or editoptions.value before the server response will be processed by jqGrid.
In the old answer I suggested to extend JSON response returned from the server with additional data for editoptions.value of the columns having formatter: "select". I suggest to set the beforeProcessing. For example one can generate the server response in the following format:
{
"cityMap": {"11": "Chennai", "12": "Mumbai", "13": "Delhi"},
"rows": [
{ "SID": "1", "SNAME": "ABC", "CITY": "11" },
{ "SID": "2", "SNAME": "XYZ", "CITY": "12" },
{ "SID": "3", "SNAME": "ACX", "CITY": "13" },
{ "SID": "4", "SNAME": "KHG", "CITY": "13" },
{ "SID": "5", "SNAME": "ADF", "CITY": "12" },
{ "SID": "6", "SNAME": "KKR", "CITY": "11" }
]
}
and uses the following jqGrid options
colModel: [
{name: "SNAME", width: 250},
{name: "CITY", width: 180, align: "center"}
],
beforeProcessing: function (response) {
var $self = $(this);
$self.jqGrid("setColProp", "CITY", {
formatter: "select",
edittype: "select",
editoptions: {
value: $.isPlainObject(response.cityMap) ? response.cityMap : []
}
});
},
jsonReader: { id: "SID"}
The demo demonstrates the approach. It displays
One can use the same approach to set any column options dynamically. For example one can use
{
"colModelOptions": {
"CITY": {
"formatter": "select",
"edittype": "select",
"editoptions": {
"value": "11:Chennai;13:Delhi;12:Mumbai"
},
"stype": "select",
"searchoptions": {
"sopt": [ "eq", "ne" ],
"value": ":Any;11:Chennai;13:Delhi;12:Mumbai"
}
}
},
"rows": [
{ "SID": "1", "SNAME": "ABC", "CITY": "11" },
{ "SID": "2", "SNAME": "XYZ", "CITY": "12" },
{ "SID": "3", "SNAME": "ACX", "CITY": "13" },
{ "SID": "4", "SNAME": "KHG", "CITY": "13" },
{ "SID": "5", "SNAME": "ADF", "CITY": "12" },
{ "SID": "6", "SNAME": "KKR", "CITY": "11" }
]
}
and the following JavaScript code
var filterToolbarOptions = {defaultSearch: "cn", stringResult: true, searchOperators: true},
removeAnyOption = function ($form) {
var $self = $(this), $selects = $form.find("select.input-elm");
$selects.each(function () {
$(this).find("option[value='']").remove();
});
return true; // for beforeShowSearch only
},
$grid = $("#list");
$.extend($.jgrid.search, {
closeAfterSearch: true,
closeAfterReset: true,
overlay: 0,
recreateForm: true,
closeOnEscape: true,
afterChange: removeAnyOption,
beforeShowSearch: removeAnyOption
});
$grid.jqGrid({
colModel: [
{name: "SNAME", width: 250},
{name: "CITY", width: 180, align: "center"}
],
beforeProcessing: function (response) {
var $self = $(this), options = response.colModelOptions, p,
needRecreateSearchingToolbar = false;
if (options != null) {
for (p in options) {
if (options.hasOwnProperty(p)) {
$self.jqGrid("setColProp", p, options[p]);
if (this.ftoolbar) { // filter toolbar exist
needRecreateSearchingToolbar = true;
}
}
}
if (needRecreateSearchingToolbar) {
$self.jqGrid("destroyFilterToolbar");
$self.jqGrid("filterToolbar", filterToolbarOptions);
}
}
},
jsonReader: { id: "SID"}
});
$grid.jqGrid("navGrid", "#pager", {add: false, edit: false, del: false})
$grid.jqGrid("filterToolbar", filterToolbarOptions);
The demo uses the above code.
We recreate the searching filter if any option are changed dynamically. The way allows implement more flexible solutions. For example the server can detect the language preferences of the client (of the web browser) and return formatting options for numbers, dates and so on based on the options. I'm sure that everyone can suggest other interesting scenarios.
One more remark. If you have too many items in select in (searchoptions.value and editoptions.value) I would recommend you don't use strings instead of objects as the value of searchoptions.value and editoptions.value. It allows you to specify the order of items in the select element.
If you will have too many items in select (for example all cities of your country) then you can consider to use select2 plugin which usage I demonstrate in the answer. It simplify selection of options because it convert select in element which is very close to jQuery UI Autocomplete.
The next demo demonstrate the usage of select2 plugin. If one click on the dropdown arrow of "select" element of the searching toolbar or the searching dialog one get additional input filed which can be used for quick searching. If one starts to type some text in the input box (for example "e" on an example on the picture below) the list of options will be reduced to the options having the typed text as substring:
I personally find such "select-searching" control very practical.
By the way I described in the another answer how to set colNames dynamically. In can be used to manage more information from the server side.
UPDATED: The corresponding controller action Students can be about the following
public class Student {
public long SID { get; set; }
public string SNAME { get; set; }
public long CITY { get; set; }
}
public class City {
public long CID { get; set; }
public string CNAME { get; set; }
}
...
public class HomeController : Controller {
...
public JsonResult Students () {
var students = new List<Student> {
new Student { SID = 1, SNAME = "ABC", CITY = 11 },
new Student { SID = 2, SNAME = "ABC", CITY = 12 },
new Student { SID = 3, SNAME = "ABC", CITY = 13 },
new Student { SID = 4, SNAME = "ABC", CITY = 13 },
new Student { SID = 5, SNAME = "ABC", CITY = 12 },
new Student { SID = 6, SNAME = "ABC", CITY = 11 }
};
var locations = new List<City> {
new City { CID = 11, CNAME = "Chennai"},
new City { CID = 12, CNAME = "Mumbai"},
new City { CID = 13, CNAME = "Delhi"}
};
// sort and concatinate location corresponds to jqGrid editoptions.value format
var sortedLocations = locations.OrderBy(location => location.CNAME);
var sbLocations = new StringBuilder();
foreach (var sortedLocation in sortedLocations) {
sbLocations.Append(sortedLocation.CID);
sbLocations.Append(':');
sbLocations.Append(sortedLocation.CNAME);
sbLocations.Append(';');
}
if (sbLocations.Length > 0)
sbLocations.Length -= 1; // remove last ';'
return Json(new {
colModelOptions = new {
CITY = new {
formatter = "select",
edittype = "select",
editoptions = new {
value = sbLocations.ToString()
},
stype = "select",
searchoptions = new {
sopt = new[] { "eq", "ne" },
value = ":Any;" + sbLocations
}
}
},
rows = students
},
JsonRequestBehavior.AllowGet);
}
}
#Avinash, You can do some trick like this. But still it's not a better solution. It may help you get some idea. What my suggestion is you need to find out better way from your server(ASP.Net) itself. I used grid complete function to modify your data,
gridComplete: function () {
var rowIDs = jQuery("#list5").getDataIDs();
for (var i=0;i<rowIDs.length;i=i+1){
rowData=jQuery("#list5").getRowData(rowIDs[i]);
if (rowData.city == "11") {
$("#list5").find('td').eq('5').html('chennai');
}else if (rowData.city == "12") {
$("#list5").find('td').eq('8').html('mumbai');
}
}
}
Hope this helps.

Categories