How to return IEnumerable<T> as null - c#

I am having the below ActivitiesDB.cs which is a DBEntity class.
public class ActivitiesDB
{
public IEnumerable<Activity> Activities { get; set; }
}
public class Activity
{
public Guid ActivityId { get ;set; }
public DateTime ActivityDateTime { get; set; }
public DateTime UpdatedDateTime { get; set; }
public IEnumerable<MetaData> MetaData { get; set; }
}
public class MetaData
{
public string Name { get; set; }
public string Value { get; set; }
}
sample data in DB(I am using CosmosDb in backend):
"Activities": [
{
"ActivityId": "4f436d11-7a61-4e7f-b0fb-0f0d5bb6be76",
"ActivityDateTime": "2021-04-30T09:35:35.32Z",
"UpdatedDateTime": "2021-05-03T07:45:21.5115588Z",
"Metadata": [
{
"Name": "name",
"Value": "value"
}
]
},
{
"ActivityId": "659e62c5-4408-42b4-8719-b8c577221d22",
"ActivityDateTime": "2021-04-30T09:35:35.32Z",
"UpdatedDateTime": "2021-05-03T07:45:21.5115588Z",
"Metadata": null
}
]
To display the data, I am having class DisplayActivtiesResponse.cs below:
public class DisplayActivtiesResponse
{
public IEnumerable<DisplayActivity> DisplayActivties{ get; set; }
}
public class DisplayActivity
{
public Guid ActivityId { get ;set; }
public DateTime ActivityDateTime { get; set; }
public DateTime UpdatedDateTime { get; set; }
public IEnumerable<MetaData> MetaData { get; set; }
}
public class MetaData
{
public string Name { get; set; }
public string Value { get; set; }
}
Below is the code to fetch the data from DB:
public async Task<DisplayActivtiesResponse> DisplayActivityAsync(Guid activityId)
{
var feedIterator = this.cosmosReviewRequestContainer.GetItemLinqQueryable<ActivitiesDB>()
.Where(q => q.Activities.Where(x => x.ActivityId == activityId)
.ToFeedIterator();
var requestModel = new ActivitiesDB();
while (feedIterator.HasMoreResults)
{
var items = await feedIterator.ReadNextAsync().ConfigureAwait(false);
requestModel = items.FirstOrDefault();
}
return new DisplayActivtiesResponse
{
DisplayActivties = mapper.Map<IEnumerable<DisplayActivity>>(requestModel);
};
}
MappingProfile.cs:
public class MappingProfile : Profile
{
public MappingProfile()
{
this.CreateMap<Activity, DisplayActivity>().ReverseMap();
}
}
Now, When I am fetching the data from db, I observed that if the metadata is null, I am getting empty array [] instead of null. I still dont know what i am missing from my end. Kindly help me on this.

Set AllowNullCollections to true when initializing:
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.AllowNullCollections = true;
});
or directly in the mapper configuration:
new MapperConfiguration(cfg =>
{
cfg.AllowNullCollections = true;
}

Related

Deserialize a json object with multiple nested objects /lists using JsonConvert

I a beginner in C# application development and have the following json data which I want to de-serialize:
{
"Parameters": [
{
"Info": {
"Id": 0,
"No": "0001"
},
"IntOptions": [
{
"Value": 0,
"ValInfo": "FIFO"
},
{
"Value": 1,
"ValInfo": "FIFO2"
}
],
"BubbleList": [
{
"Position": 0,
"SubBubbleList": [
{
"Value": 0,
"Message": "ListObj1"
},
{
"Value": 1,
"Message": "ListObj2"
}
]
}
]
}
]
}
I have the class structure defined as follows:
public class ParamList
{
private List<Param> _param = new List<Param>();
[JsonProperty("Parameters")]
public IReadOnlyCollection<Param> Param { get => _param.AsReadOnly(); }
}
public class Param
{
private List<IntOptions> _intOptions;
private List<BubbleList> _bubbleList;
[JsonProperty("Info")]
public Info? Info { get; }
[JsonProperty("IntOptions")]
public IReadOnlyCollection<IntOptions> IntOptionsVar { get => _intOptions.AsReadOnly(); }
[JsonProperty("BubbleList")]
public IReadOnlyCollection<BubbleList> BubbleListVar { get => _bubbleList.AsReadOnly(); }
}
public class Info
{
public Info(int id, string number)
{
Id = id;
Number = number;
}
[JsonProperty("Id")]
public int Id { get; private set; }
[JsonProperty("No")]
public string Number { get; private set; }
}
public class IntOptions
{
public IntOptions(int value, string valInfo)
{
Value = value;
ValInfo = valInfo;
}
[JsonProperty("Value")]
public int Value { get; private set; }
[JsonProperty("ValInfo")]
public string ValInfo { get; private set; }
}
public class BubbleList
{
private List<SubBubbleList>? _subBubbleList;
public BubbleList(int position)
{
Position = position;
}
[JsonProperty("Position")]
public int Position { get; private set; }
[JsonProperty("SubBubbleList")]
public IReadOnlyCollection<SubBubbleList> SubBubbleListVar { get => _subBubbleList.AsReadOnly(); }
}
public class SubBubbleList
{
public SubBubbleList(int value, string message)
{
Value = value;
Message = message;
}
[JsonProperty("Value")]
public int Value { get; private set; }
[JsonProperty("Message")]
public string Message { get; private set; }
}
I came up with the following de-serializing code which results in an empty list of Param:
try
{
ParamList paramList = JsonConvert.DeserializeObject<ParamList>(readJsonContent);
Console.WriteLine(paramList);
}
This gives me an empty list of Param.
I read a lot of articles explaining about de-serializing a json object, however, could not find one which would solve my use case. I need to understand what am I doing wrong here. I need to have a List<Param> which would then have Info, List<IntOptions>, & List<BubbleList> -> List<SubBubbleList>.
Is this achievable by just de-serializing the json data, or will I have to iterate through individual objects?
you have only getters in your classes. you need setters to assign values from json to c# objects
List<Parameter> Parameters = JsonConvert.DeserializeObject<ParamList>(json).Parameters;
classes
public class ParamList
{
public List<Parameter> Parameters { get; set; }
}
public class Parameter
{
public Info Info { get; set; }
public List<IntOption> IntOptions { get; set; }
public List<BubbleList> BubbleList { get; set; }
}
public class BubbleList
{
public int Position { get; set; }
public List<SubBubbleList> SubBubbleList { get; set; }
}
public class Info
{
public int Id { get; set; }
public string No { get; set; }
}
public class IntOption
{
public int Value { get; set; }
public string ValInfo { get; set; }
}
public class SubBubbleList
{
public int Value { get; set; }
public string Message { get; set; }
}
but if for some reasons you still need readonly you can change ALL your read only classes by moving json property name using this template
public class ParamList
{
[JsonProperty("Parameters")]
private List<Param> _param = new List<Param>();
public IReadOnlyCollection<Param> Param
{
get => _param.AsReadOnly();
}
}

Passing variables into a string variable that contains json

I am trying to replace some parts of my query with a variable that holds a certain value.
For instance, inside of my variable query I am wanting to replace
\"level\": \"Information\"
and \"match\": { \"level\": \"Error\"
with variable fieldName and variable fieldValue respectively
The fieldName holds a value of "_source.level"
and fieldValue holds a value of "information" and "error".
(Not sure if I have to make another variable to hold the
second value of "error" since I am wanting both information and
error.)
The issue I am running into is normally I know you would add a $ before the string and add a {} where you want to call the variable. However, when I add a $ at the beginning of my string it does not like it and says
Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
So I am facing issues trying to figure out how I can pass those variables into my query variable.
string jsonFromFile;
using (var reader = new StreamReader(path))
{
jsonFromFile = reader.ReadToEnd();
}
var json = JsonConvert.DeserializeObject<VirtualSupport>(jsonFromFile);
foreach (var item in json.Systems.Applications)
{
foreach (var x in item.Application)
{
foreach (var y in x.BusinessProcessSteps)
{
foreach (var z in y.BusinessProcessStep.LogDataSources)
{
var fieldName = z.LogDataSource.LogFieldsMapping.LevelField.FieldName;
var fieldValue = z.LogDataSource.LogFieldsMapping.LevelField.FieldValue;
}
}
}
}
var query = "{\"size\": 1000,\"query\": {\"bool\": {\"should\":[ {\"match\": { \"level\": \"Information\" } }, {\"match\": { \"level\": \"Error\" } } ], " +
"\"filter\": [ { \"range\": { \"#timestamp\": { \"gte\": \"2021-07-26T07:58:45.304-05:00\", \"lt\": \"2021-07-26T08:58:45.305-05:00\" } } } ]," +
"\"minimum_should_match\": 1 } } }";
Snippet of the json file that I am reading from and assigning fieldname and fieldvalue to.
"LogFieldsMapping": {
"IDField": { "FieldName": "_id" },
"LevelField": {
"FieldName": "_source.level",
"FieldValue": [
{ "value": "Information" },
{ "value": "Error" }
]
}
}
Rather then using the $ (string interpolation), you could consider converting the query-string into a class:
{
"size": 1000,
"query": {
"bool": {
"should": [
{
"match": {
"level": "Information"
}
},
{
"match": {
"level": "Error"
}
}
],
"filter": [
{
"range": {
"#timestamp": {
"gte": "2021-07-26T07:58:45.304-05:00",
"lt": "2021-07-26T08:58:45.305-05:00"
}
}
}
],
"minimum_should_match": 1
}
}
}
Which could be converted to something like this:
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
public class Match
{
public string level { get; set; }
}
public class Should
{
public Match match { get; set; }
}
public class Timestamp
{
public DateTime gte { get; set; }
public DateTime lt { get; set; }
}
public class Range
{
[JsonProperty("#timestamp")]
public Timestamp Timestamp { get; set; }
}
public class Filter
{
public Range range { get; set; }
}
public class Bool
{
public List<Should> should { get; set; }
public List<Filter> filter { get; set; }
public int minimum_should_match { get; set; }
}
public class Query
{
public Bool #bool { get; set; }
}
public class Root
{
public int size { get; set; }
public Query query { get; set; }
}
You can instanciate a new object from Root, and then add as many Matches to Should as you need, something similar like this:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
Root queryObject = new();
queryObject.query.#bool.should.Add(
new Should() {
match = new() {
level = "information"
}
});
queryObject.query.#bool.should.Add(
new Should() {
match = new() {
level = "error"
}
}
);
Console.WriteLine(JsonConvert.SerializeObject(queryObject));
// Outputs: {"size":1000,"query":{"bool":{"should":[{"match":{"level":"information"}},{"match":{"level":"error"}}],"filter":null,"minimum_should_match":0}}}
}
}
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
public class Match
{
public string level { get; set; }
}
public class Should
{
public Match match { get; set; }
}
public class Timestamp
{
public DateTime gte { get; set; }
public DateTime lt { get; set; }
}
public class Range
{
[JsonProperty("#timestamp")]
public Timestamp Timestamp { get; set; }
}
public class Filter
{
public Range range { get; set; }
}
public class Bool
{
public List<Should> should { get; set; }
public List<Filter> filter { get; set; }
public int minimum_should_match { get; set; }
}
public class Query
{
public Bool #bool { get; set; }
}
public class Root
{
public Root()
{
size = 1000;
query = new();
query.#bool = new();
query.#bool.should = new();
// skipping the rest ...
}
public int size { get; set; }
public Query query { get; set; }
}
https://dotnetfiddle.net/YhFM1I

Parsing [FromBody] JSON: model null

I'm trying to parse some json in my action which will then do things with it. However I keep getting null as my model instead of the filled out model.
This is the json I'm trying to parse:
{
"sameLanguages":true,
"sameDeadlines":true,
"sameDeliverables":false,
"quotations":[
{
"name":"zasd",
"deliverable":"538184e1-9a62-4ce9-baa7-ed746f267a9a",
"subtitleAssignments":{
"languageCombinations":[
{
"from":"d177b276-8f10-472f-84c6-f2ef59052a09",
"to":"d177b276-8f10-472f-84c6-f2ef59052a09",
"startDate":"19-09-2017",
"endDate":"19-09-2017"
}
],
"amount":12
},
"translationAssignments":{
"languageCombinations":[
]
}
}
]
}
This is my action:
[HttpPost]
public IActionResult Add([FromBody] SubmitQuotationsModel model)
{
//Do things...
return View();
}
These are my models:
public class SubmitQuotationsModel
{
public bool SameLanguages { get; set; }
public bool SameDeadlines { get; set; }
public bool SameDeliverables { get; set; }
public List<SubmitQuotationModel> Quotations { get; set; } = new List<SubmitQuotationModel>();
}
public class SubmitQuotationModel
{
public string Name { get; set; }
public string Deliverable { get; set; }
public List<AssignmentModel> SubtitleAssignments { get; set; }
public List<AssignmentModel> TranslationAssignments { get; set; }
}
public class AssignmentModel
{
public List<LanguageCombinationModel> LanguageCombinations { get; set; }
public int Amount { get; set; }
}
public class LanguageCombinationModel
{
public string From { get; set; }
public string To { get; set; }
public DateTimeOffset StartDate { get; set; }
public DateTimeOffset EndDate { get; set; }
}
I am sending the json from my knockout/typescript script as such:
fetch('/Quotation/Add', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: this.toJSON()
});
public toJSON(): string {
let model = {
sameLanguages: this.step1().sameLanguages(),
sameDeadlines: this.step1().sameDeadlines(),
sameDeliverables: this.step1().sameDeliverables(),
quotations: this.step2().quotations().filter((q) => q.isFilledIn()).map((q) => {
return {
name: q.name(),
deliverable: q.selectedDeliverable().id,
subtitleAssignments: this.getAssignmentModel(q.subtitleAssignmentGroup()),
translationAssignments: this.getAssignmentModel(q.translationAssignmentGroup())
}
})
};
return ko.toJSON(model);
}
private getAssignmentModel(model: AssignmentGroupModel) {
return {
languageCombinations: model.assignments().map((a) => {
return {
from: a.fromLanguage().value,
to: a.toLanguage().value,
startDate: a.startDate().format('DD-MM-YYYY'),
endDate: a.endDate().format('DD-MM-YYYY')
}
}),
amount: model.amount()
}
}
I'm not getting any exceptions, the model parameter just remains null. I have found that if I comment out the SubtitleAssignments and TranslationAssignments in SubmitQuotationModel, it deserializes the other parts of the json just fine. But I can't figure out why it won't deserialize with those two ...Assignments declarations not commented out.
SubtitleAssignments and TranslationAssignments aren't lists in the json but they are lists in the models. They just need to be AssignmentModel and not List<AssignmentModel>

MongoDb 3.4 C# Driver - How to create equivalent aggregate query with C# driver

I need some help with converting a MongoDB query into a C# query.
This is my Schema:
public class Planner
{
public ObjectId Id { get; set; }
public string PlannerName { get; set; }
public string EditorName { get; set; }
public DateTime DateModified { get; set; }
public List<PlannerDayModel> PlannerDays { get; set; }
}
public class PlannerDayModel
{
public int WeekDay { get; set; }
public string WeekDayStr { get; set; }
public int Month { get; set; }
public int Year { get; set; }
public int MonthDay { get; set; }
public DateTime TimeStamp { get; set; }
public bool IsWeekend { get; set; }
public bool IsHoliday { get; set; }
public PlannerFreeTextModel FreeTextBox1 { get; set; }
public PlannerFreeTextModel FreeTextBox2 { get; set; }
public List<PlannerEventModel> Events { get; set; }
}
public class PlannerEventModel
{
public int EventRowId { get; set; }
public string EventName { get; set; }
public bool IsSeries { get; set; }
public int? Episode { get; set; }
public int? Season { get; set; }
public bool? IsCustom{ get; set; }
}
public class PlannerFreeTextModel
{
public int EventRowId { get; set; }
public string Text { get; set; }
}
I am trying to get all Planner docs where a nested collection called Events has an event with the event name of x.
This MongoDB query works just fine:
db.Planner.aggregate([
{ "$match": { "planners.events.eventName": "X" } },
{ "$project":
{"dateModified":1, "editorName":1,"plannerName":1,
"planners": {
"$filter": {
"input": {
"$map": {
"input": "$planners",
"as": "planner",
"in": {
"weekDay": "$$planner.weekDay",
"weekDayStr":"$$planner.weekDay",
"events": {
"$filter": {
"input": "$$planner.events",
"as": "event",
"cond": {
"$eq": ["$$event.eventName", "X" ]
}
}
}
}
}
},
"as": "planner",
"cond": { "$ne": [ "$$planner.events", []]}
},
}
}}
])
But I just cant find the equivalent C# driver query to work.
I tried this one, and it threw the error "Unable to determine the serialization information for the expression"
var projection = Builders<Planner>.Projection
.Include(x => x.PlannerDays
.Find(p => p.Events.FirstOrDefault(e => e.EventName == eventName) != null))
.Include(x => x.DateModified)
.Include(x => x.EditorName)
.Include(x => x.Id)
.Include(x => x.PlannerName);
var res = collection
.Aggregate()
.Match(Builders<Planner>.Filter.Eq("planners.events.eventName", eventName))
.Project<Planner>(projection)
.ToList();
I think you are making it complicated.
First of all you can check your query by doing writing ToString() at the end of your c# query.
Eg:
collection.Aggregate()
.Match(Builders<Planner>.Filter.Eq("planners.events.eventName", eventName))
.Project<Planner>(projection)
.ToString();
Second thing lets get back to your question, so you want all the Planners with a condition.
You can do this.
First your filter would be like:
var filter = Builders<BsonDocument>.Filter.Eq("Planner.PlannerDayModel.PlannerEventModel.eventName", "X");
Now comes your aggregate query:
var aggregate = collection.Aggregate(new AggregateOptions { AllowDiskUse = true })
.Unwind("Planner")
.Unwind("Planner.PlannerDayModel")
.Unwind("Planner.PlannerDayModel.PlannerEventModel")
.Unwind("Planner.PlannerDayModel.PlannerEventModel.eventName")
.Match(filter)
;
Now this will be your c# query. You can check your schema by doing:
aggregate.ToString();
Now lets execute the query.
List<JObject> objList = new List<JObject>();
foreach (var agg in aggregate.ToList())
{
JObject obj = new JObject();
var str = agg.ToString();
objList.Add(obj);
}
You'll get all the data of planner in objList.
Apart from this you can use project according to your requirement.
Please feel free to correct me.
Edit: Adding project
var pro = new BsonDocument {
{"PlannerName", true },
{"EditorName", true },
{"DateModified", true },
{"PlannerDays", true }
};
Now aggregate query would look like this:
var aggregate = collection.Aggregate(new AggregateOptions { AllowDiskUse = true })
.Unwind("Planner")
.Unwind("Planner.PlannerDayModel")
.Unwind("Planner.PlannerDayModel.PlannerEventModel")
.Unwind("Planner.PlannerDayModel.PlannerEventModel.eventName")
.Match(filter)
.Project(pro)
;
Hope it helps. :)

Deserialization Json to C# using Newtonsoft

I have the following JSON that I need to parse :
{
"code": 200,
"status": "OK",
"response": {
"course_accessJSON": null,
"in_progress": 1,
"completed": 0,
"passed": 0,
"location": "http://*************************************",
"subscription": {
"started": 1465834293,
"expired": 1473869493,
"payment": "account"
},
"is_expired": false,
"course_progress": {
"CMP1044": {
"course_name_en": "Java Programming",
"no_of_lessons": 30,
"viewed": 1,
"viewed_start": 1465834789,
"viewed_end": null,
"cert_attemptsCSV": null,
"cert_resetsCSV": null,
"cert_serial": null,
"high_score": null,
"location": "http://***************************"
}
}
}
}
I have managed to get all the elements out except the value in the course_progress item using the following:
SampleResourse obj = JsonConvert.DeserializeObject<SampleResourse>(s);
Response.Write(obj.Response.CourseProgress.CMP1044.CourseNameEn.ToString());
class SampleResourse
{
[JsonProperty("code")]
public int respcode { get; set; }
[JsonProperty("status")]
public string respStatus { get; set; }
[JsonProperty("response")]
public Response2 Response { get; set; }
}
class Response2
{
[JsonProperty("in_progress")]
public int InProgress { get; set; }
[JsonProperty("completed")]
public int Completed { get; set; }
[JsonProperty("passed")]
public int Passed { get; set; }
[JsonProperty("course_progress")]
public CourseProgress CourseProgress { get; set; }
}
class CourseProgress
{
[JsonProperty("CMP1044")]
public CMP10442 CMP1044 { get; set; }
}
class CMP10442
{
[JsonProperty("course_name_en")]
public string CourseNameEn { get; set; }
}
I need the elements course_name_en, no_of_lessons,viewed however there will be multiple properties similar to "CMP1044". Each "CMP" property will have a unique number. I want to list progress on each.
Simply change the following :
class Response2
{
[JsonProperty("in_progress")]
public int InProgress { get; set; }
[JsonProperty("completed")]
public int Completed { get; set; }
[JsonProperty("passed")]
public int Passed { get; set; }
[JsonProperty("course_progress")]
public Dictionary<string, CourseProgress> CourseProgress { get; set; }
}
class CourseProgress
{
[JsonProperty("course_name_en")]
public string CourseNameEn { get; set; }
}
To list the course name along with the id, you can do this:
var resource = Newtonsoft.Json.JsonConvert.DeserializeObject<SampleResourse>(json);
foreach (var kvp in resource.Response.CourseProgress)
{
Response.Write(kvp.Key + ":" + kvp.Value.CourseNameEn + Environment.NewLine);
}

Categories

Resources