I have an object that is contains strings and further objects that contain strings, what i need to do is ensure that the object and any sub objects have an empty string and not a null value, so far this works fine:
foreach (PropertyInfo prop in contact.GetType().GetProperties())
{
if(prop.GetValue(contact, null) == null)
{
prop.SetValue(contact, string.empty);
}
}
the problem is this only works for the objects strings and not the sub-objects strings. Is there a way to also loop over all sub-objects and set their strings to string.Empty if found to be null?
Here's an example of the 'contact' object:
new contact
{
a = "",
b = "",
c = ""
new contact_sub1
{
1 = "",
2 = "",
3 = ""
},
d = ""
}
Basically I also need to check in contact_sub1 for nulls and replace the value with an empty string.
You can modify your current code to get all sub objects and then perform the same check for null string properties.
public void SetNullPropertiesToEmptyString(object root) {
var queue = new Queue<object>();
queue.Enqueue(root);
while (queue.Count > 0) {
var current = queue.Dequeue();
foreach (var property in current.GetType().GetProperties()) {
var propertyType = property.PropertyType;
var value = property.GetValue(current, null);
if (propertyType == typeof(string) && value == null) {
property.SetValue(current, string.Empty);
} else if (propertyType.IsClass && value != null && value != current && !queue.Contains(value)) {
queue.Enqueue(value);
}
}
}
}
Related
I'm swapping my values in List Object on some conditions and update List Object value.
Currently, what I'm doing is
- Looping on each object through List
- Check If condition is net
- Swap values
public static void SwapMinMaxIfNull<T>(this IEnumerable<T> rows, string reportfor)
{
if (reportfor.Equals("Comparison"))
{
var row = rows as IEnumerable<RiskBoardDataToExport>;
try
{
if (rows.Any())
{
var Tests = row.Where(min => min.MinGaitSpeed == null && min.MaxGaitSpeed != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinGaitSpeed = test.MaxGaitSpeed;
test.MaxGaitSpeed = null;
}
}
// again check for next object
Tests = row.Where(min => min.MinTUGTime == null && min.MaxTUGTime != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinTUGTime = test.MaxTUGTime;
test.MaxTUGTime = null;
}
}
// again check for next object
Tests = row.Where(min => min.MinBergScoreSpeed == null && min.MaxBergScoreSpeed != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinBergScoreSpeed = test.MaxBergScoreSpeed;
test.MaxBergScoreSpeed = null;
}
}
//.. for brevity
}
}
}
Can I do it in better way? I know about PropertyInfo i.e. Can check property name and get value etc, but, not having any hint to get this done.
Thanks
It's not exactly what you're asking for, but you can combine the clauses in your Where statements and then have a few if statements in the body:
public static void SwapMinMaxIfNull(this IEnumerable<RiskBoardDataToExport> rows,
string reportfor)
{
if (rows = null) return;
if (reportfor.Equals("Comparison", StringComparison.OrdinalIgnoreCase))
{
foreach (var row in rows.Where(r =>
(r.MinGaitSpeed == null && r.MaxGaitSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null)))
{
if (row.MinGaitSpeed == null)
{
row.MinGaitSpeed = row.MaxGaitSpeed;
row.MaxGaitSpeed = null;
}
if (row.MinTUGTime == null)
{
row.MinTUGTime = row.MaxTUGTime;
row.MaxTUGTime = null;
}
if (row.MinBergScoreSpeed == null)
{
row.MinBergScoreSpeed = row.MaxBergScoreSpeed;
row.MaxBergScoreSpeed = null;
}
}
}
}
As this is an operation where order of the items in the list does not matter, you can easily speed this up by parallelization (you can read up on that here).
So, what you should do, is handle this foreach loop in a parallel way and combine it with Rufus L's optimized code for the fastest result.
var rows = rows.Where(r =>
(r.MinGaitSpeed == null && r.MaxGaitSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null))
Parallel.ForEach(rows, (row) => {
{
if (row.MinGaitSpeed == null)
{
row.MinGaitSpeed = row.MaxGaitSpeed;
row.MaxGaitSpeed = null;
}
if (row.MinTUGTime == null)
{
row.MinTUGTime = row.MaxTUGTime;
row.MaxTUGTime = null;
}
if (row.MinBergScoreSpeed == null)
{
row.MinBergScoreSpeed = row.MaxBergScoreSpeed;
row.MaxBergScoreSpeed = null;
}
}
Note that this requires the System.Threading.Tasks namespace, that's where the Parallel class is.
I working hard on a JSON-Deserializer on my own. Just for training. I am nearly finished, but I have a copying issue.
I already have this:
public void CopyValues<T>(T target, T source)
{
Type t = typeof(T);
var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);
foreach (var prop in properties)
{
var value = prop.GetValue(source, null);
if (value != null)
prop.SetValue(target, value, null);
}
}
The Main Problem is here. I have a Property containing 2 Properties. Like content.link and content.value.
If I use the Copy Function it copys right. No discussion. But if I put the copy function into a loop, and the data is filled up, the source having also
"content", but without link and value.
If I copy again, the already correctly filled properties are getting overridden, and the result is that I have only null at conent.link and content.value.
Is there a way, to check if link and value is set to null ?
In order to copy the nested properties, you will need use a recursive function:
public static void DeepCopy<T>(T target, T source)
{
DeepCloneImpl(typeof(T), source, target);
}
public static T DeepClone<T>(T template)
where T : new()
{
return (T)DeepCloneImpl(typeof(T), template);
}
private static object DeepCloneImpl(Type type, object template, object stump = null)
{
if (template == null)
{
return null;
}
var clone = stump ?? Activator.CreateInstance(type);
var clonableProperties = type.GetProperties()
.Where(x => x.GetMethod != null && x.SetMethod != null);
foreach (var property in clonableProperties)
{
var propertyType = property.PropertyType;
if (propertyType.GetTypeInfo().IsValueType || propertyType == typeof(string))
{
var value = property.GetValue(template);
property.SetValue(clone, value);
}
else if (propertyType.GetTypeInfo().IsClass && propertyType.GetConstructor(Type.EmptyTypes) != null)
{
var value = DeepCloneImpl(propertyType, property.GetValue(template));
property.SetValue(clone, value);
}
else if (propertyType.IsArray)
{
var source = property.GetValue(template) as Array;
if (source == null)
{
continue;
}
var elementType = propertyType.GetElementType();
if (elementType.GetTypeInfo().IsValueType || elementType == typeof(string))
{
var copies = Array.CreateInstance(elementType, source.Length);
Array.Copy(source, copies, source.Length);
property.SetValue(clone, copies);
}
else if (elementType.GetTypeInfo().IsClass)
{
var copies = Array.CreateInstance(elementType, source.Length);
for (int i = 0; i < source.Length; i++)
{
var copy = DeepCloneImpl(elementType, source.GetValue(i));
copies.SetValue(copy, i);
}
property.SetValue(clone, copies);
}
}
}
return clone;
}
This should cover most of the use case, however you will have to handle self/circular references.
From what I can gather, the problem is values are being overridden. And from looking at your method, it appears to be doing what you'd expect, it's a soft clone of property values. Depending on what a correctly filled property is in your situation, you'll have to make a comparison that prevents the property from being set again. Perhaps value != null should actually be value == null so that you're only setting the value when there wasn't already one. Because of the generic typing of this method you don't have to worry about property mismatches if that's why you're using != null. Both arguments, source and target, will be of the same type.
Although many questions have been posted, none seem to help me on my issue.
I've started a new Generics / Reflection adventure and I'm just trying to get my head around the syntax and concepts.
I have a Generic class with X amount of properties and one being a collection, all is working fine but I'm having problems extracting the values from the collection props by property name.
foreach (var property in typeof(T).GetProperties())
{
if (property.Name == "Props")
{
foreach (var item in (IEnumerable)property.GetValue(type, null))
{
var propertyName = "";
var newValue = "";
var oldValue = "";
sbDescription.AppendLine(strDescriptionVals
.Replace("{0}", (item.ToString() == "PropertyName") ? item.ToString() : "" + ", "));
sbAllNotes.AppendLine(strAllNotes
.Replace("{0}", (item.ToString() == "PropertyName") ? item.ToString() : "")
.Replace("{1}", (item.ToString() == "NewValue") ? item.ToString() : "")
.Replace("{2}", (item.ToString() == "OldValue") ? item.ToString() : ""));
}
}
}
As you can see I've pinpointed the property Props and now I want to loop through that and pull values by property name.
item.ToString() just prints the namespace of the class property and not the value
Hoping you kind folk can point me in the right direction?
You probably want to recursively "dive" into the items properties.
Note, only conceptual code fragments, no guarantee that it works as I write it here:
void Print(object value, string propertyName)
{
if (property.PropertyType == typeof(string) || property.PropertyType.IsPrimitive)
{
sbAllNotes.AppnedLine(.. propertyName .. value ..);
}
else if ((typeof(IEnumerable).IsAssignableFrom(property.PropertyType)
{
PrintCollectioType((IEnumerable)value);
}
else
{
PrintComplexType(value);
}
}
void PrintCollectioType(IEnumerable collection)
{
foreach (var item in collection)
{
Print(item, "Collection Item");
}
}
void PrintComplexType(IEnumerable collection)
{
foreach(var property in owner.GetType().GetProperties())
{
var propertyName = property.Name;
var propertyValue = property.GetValue(owner);
Print(item, propertyName);
}
}
To print the "Props", do:
var props = typeof(T).GetProperty("Props");
var propsValue = props.GetValue(rootObject);
Print(propsValue, "Root");
After playing with the code and understanding the logic a little more, it occurred to me that is was as simple as "Getting the type and Property" on the Props iteration:
//Get the collection property
foreach (var property in typeof(T).GetProperties())
{
if (property.Name == "Props")
{
foreach (var item in (IEnumerable)property.GetValue(type, null))
{
//Because props is a collection, Getting the type and property on each pass was essential
var propertyName = item.GetType().GetProperty("PropertyName").GetValue(item, null);
var newValue = item.GetType().GetProperty("NewValue").GetValue(item, null);
var oldValue = item.GetType().GetProperty("OldValue").GetValue(item, null);
sbDescription.AppendLine(strDescriptionVals
.Replace("{0}", (propertyName != null) ? propertyName.ToString() : "" + ", "));
sbAllNotes.AppendLine(strAllNotes
.Replace("{0}", (propertyName != null) ? propertyName.ToString() : "")
.Replace("{1}", (newValue != null) ? newValue.ToString() : "")
.Replace("{2}", (oldValue != null) ? oldValue.ToString() : ""));
}
}
}
The code below contains a foreach loop that loops through a list of string collection which contains XML. While enumerating through the collection, it reads the question and answer elements and adds them to the list collection. I need to ensure that no repeated question is added to the list collection.
The code below questionnaire.QuestionAnswers.Add(fataQuestionsAnswers) adds the elements to the list collection. The problem that I am facing is that the repeated questions are getting added to the list. I tried to put the following condition that is:
if (questionnaire.QuestionAnswers.Find(a => a.Question != fataQuestionsAnswers.Question) == null)
but that doesn't seem to work.
var fataQuestionnaireData = DbAccess.GetFatcaQuestionnaire(contactId);
if (fataQuestionnaireData != null)
{
var questionnaire = new FatcaQuestionnaire();
foreach (var fatcaQuestionnaire in fataQuestionnaireData)
{
//var QData = GetObjectFromStream<FatcaQuestionnaire>fataQuestionnaireData);
//FatcaQuestionnaire.Deserialize(fataQuestionnaireData);
XDocument doc = XDocument.Parse(fatcaQuestionnaire.ToString(CultureInfo.InvariantCulture));
// w.WriteLine("The value of doc" + doc);
doc.Descendants("QuestionAnswer").ToList().ForEach(questionAnswer =>
{
var fataQuestionsAnswers = new QuestionAnswers();
{
//var questionAnswer = qa.Element("QuestionAnswer");
var questionElement = questionAnswer.Element("Question");
if (questionElement != null )
fataQuestionsAnswers.Question = questionElement.Value;
//if (questionElement != null)
// w.WriteLine("The value of questionElement" + questionElement.Value);
var answerElement = questionAnswer.Element("Answer");
if (answerElement != null)
fataQuestionsAnswers.Answer = answerElement.Value;
//if (answerElement != null)
// w.WriteLine("The value of answerElement" + answerElement.Value);
var sqa = questionAnswer.Element("SubQuestionAnswer");
if (sqa != null)
{
var subQuestionElement = sqa.Element("Question");
if (subQuestionElement != null)
fataQuestionsAnswers.SubQuestionAnswer.Question = subQuestionElement.Value;
//if (subQuestionElement != null)
// w.WriteLine("The value of answerElement" + subQuestionElement.Value);
var subAnswerElement = sqa.Element("Answer");
if (subAnswerElement != null)
fataQuestionsAnswers.SubQuestionAnswer.Answer = subAnswerElement.Value;
//if (subQuestionElement != null)
// w.WriteLine("The value of answerElement" + subQuestionElement.Value);
}
if (questionnaire.QuestionAnswers.Find(a => a.Question != fataQuestionsAnswers.Question) == null)
questionnaire.QuestionAnswers.Add(fataQuestionsAnswers);
//fatcaQuestionsList.Add(fataQuestionsAnswers);
}
fatca.Questionnaire.Add(fataQuestionsAnswers);
});
}
}
You have your condition wrong, you are looking for questionanswers where the question does not match, you should be looking where they do match and checking that the result is null. (switch the != with ==)
It should be
if (questionnaire.QuestionAnswers.Find(a => a.Question == fataQuestionsAnswers.Question) == null)
However I would change it to Any() as it is a bit nearer and easier to read, it returns a true if one of the items in your list matches the condition that you specify.
if(!questionnaire.QuestionAnswers.Any(a => a.Question == fataQuestionAnswers.Question)) {
questionnaire.QuestionAnswers.Add(fataQuestionsAnswers);
}
Use Any instead
if(!questionnaire.QuestionAnswers.Any(a => a.Question == fataQuestionsAnswers.Question))
{
questionnaire.QuestionAnswers.Add(fataQuestionsAnswers);
}
Is it possible there's trailing spaces / casing differences?
if(!questionnaire.QuestionAnswers.Any(a => a.Question.Trim().Equals(fataQuestionsAnswers.Question.Trim(), StringComparison.OrdinalIgnoreCase)))
{
questionnaire.QuestionAnswers.Add(fataQuestionsAnswers);
}
I have a SelectedListItem like so:
private List<SelectListItem> GetStatus()
{
List<SelectListItem> msStatuses = new List<SelectListItem>() {
new SelectListItem() { Text = "New", Value = "50342"},
new SelectListItem() { Text = "In Process", Value = "50343"},
new SelectListItem() { Text = "Approved", Value = "50345"},
new SelectListItem() { Text = "Rejected", Value = "50344"}};
return msStatuses;
}
This is checked with the incoming value and set msStatus to the same when a match is found:
msStatus.Find(m => m.Value == UserRequest.Status.ToString()).Selected = true;
This works fine as long as the 'Status' property on UserRequest is one of the four.
When it's something other than the four it will throw a null exception.
In which case msStatus should be set to 'New', the first option, when the value to be checked
for is not one among the four.
How do I handle this?
Thanks in advance.
Just store the result into a variable and check for null
var item = msStatus.Find(m => m.Value == UserRequest.Status.ToString());
if(item == null)
{
// set selected item to New
msStatus.Find(m => m.Value == "New").Selected = true;
}
else
{
item.Selected = true;
}
Try using FirstOrDefault which will return null if it does not exists.
var statusItem = msStatus.FirstOrDefault(m => m.Value == UserRequest.Status.ToString())
if (statusItem != null)
{
// here you canaccess statusItem.Selected safety
}
You can check for null or empty values of Value like so:
(m => String.IsNullOrEmpty(Value) || m.Value == UserRequest.Status.ToString());
If Value is empty or null then it'll be ignored.
You can just make a flag wether its matching or not & then you can set it ::
bool IsstatusPresent= msStatus.Any(x=>m.Value == UserRequest.Status.ToString());
if(!IsstatusPresent)
{
return msStatus.Find(m => m.Value == "New").Selected;
}
else
{
return msStatus.Find(x=>m.Value == UserRequest.Status.ToString());
}