This question already has answers here:
What is the best way to dump entire objects to a log in C#?
(16 answers)
Closed 8 years ago.
i have class basically just a row of a table.
this row contains many columns.
for testing purpose, i will need to output the reads i get .
so i need to output all of the columns in the row.
the class is like
public class tableRow
{
public tableRow()
{}
public string id
public string name
public string reg
public string data1
....
....
....
<lot more columns>
}
then i need to write like:
Console.WriteLine("id: " + tableRow.id);
Console.WriteLine("name: " + tableRow.name);
Console.WriteLine("reg: " + tableRow.reg);
Console.WriteLine("data1: " + tableRow.data1);
...
...
...
<lot more Console.WriteLine>
So i want to know , is there an easy way to get all of these output , without so much console.writeLine?
thanks
You can serialize tableRow into JSON and all columns will be printed. E.g. with JSON.NET (available from NuGet):
tableRow tr = new tableRow { id = "42", name = "Bob", reg = "Foo" };
Console.WriteLine(JsonConvert.SerializeObject(tr, Formatting.Indented));
Output:
{
"id": "42",
"name": "Bob",
"reg": "Foo",
"data1": null
}
I use this approach for logging (to show object state) - it's nice to have extension
public static string ToJson<T>(this T obj)
{
return JsonConvert.SerializeObject(tr, Formatting.Indented);
}
Usage is simple:
Console.WriteLine(tr.ToJson());
Here's a short example using reflection:
void Main()
{
var myObj = new SomeClass();
PrintProperties(myObj);
myObj.test = "haha";
PrintProperties(myObj);
}
private void PrintProperties(SomeClass myObj){
foreach(var prop in myObj.GetType().GetProperties()){
Console.WriteLine (prop.Name + ": " + prop.GetValue(myObj, null));
}
foreach(var field in myObj.GetType().GetFields()){
Console.WriteLine (field.Name + ": " + field.GetValue(myObj));
}
}
public class SomeClass {
public string test {get; set; }
public string test2 {get; set; }
public int test3 {get;set;}
public int test4;
}
Output:
test:
test2:
test3: 0
test4: 0
test: haha
test2:
test3: 0
test4: 0
This should work for classes as well as types with custom type descriptors:
private static void Dump(object o)
{
if (o == null)
{
Console.WriteLine("<null>");
return;
}
var type = o.GetType();
var properties = TypeDescriptor.GetProperties(type);
Console.Write('{');
Console.Write(type.Name);
if (properties.Count != 0)
{
Console.Write(' ');
for (int i = 0, n = properties.Count; i < n; i++)
{
if (i != 0)
Console.Write("; ");
var property = properties[i];
Console.Write(property.Name);
Console.Write(" = ");
Console.Write(property.GetValue(o));
}
}
Console.WriteLine('}');
}
If you want to dump fields, and not properties, you can use type.GetFields() and make the necessary modifications to the above code. FieldInfo has a similar GetValue() method.
Note that this will not print "deep" representations of records. For that, you could adapt this into a recursive solution. You may also want to support collections/arrays, quote strings, and identify circular references.
You could use reflection, to do it ... Give me a minute, I'll post some code
Something along:
PropertyInfo[] propertyInfos;
propertyInfos = typeof(MyClass).GetProperties(BindingFlags.Public |
BindingFlags.Static);
// sort properties by name
Array.Sort(propertyInfos,
delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
{ return propertyInfo1.Name.CompareTo(propertyInfo2.Name); });
// write property names
foreach (PropertyInfo propertyInfo in propertyInfos)
{
Console.WriteLine(propertyInfo.Name);
}
You could add a check to see it's a string, and add some output as well, but that's the git of it.
Source from here
if you set up an array of strings
var dataFields = new string[] { "id", "name", ...
you could do a foreach loop to populate the table, and reference the table data using the same array, allowing you to do another foreach loop to do the Console.WriteLine calls
If you wanted to, each table row could just be a dictionary, with the datafield as the key, and the data as a value. Then you can just loop though the dictionary
Related
My application receives Kafka messages that contain a Dictionary<string,string> as one of the properties, and its values could be a nested (however dynamic) json string, and I need to iterate through this unknown json. I am struggling to find a logic and even the best data structure to do this iteration.
Examples of the dictionary (mocked data):
//could have complex nested json string as value
"reward":"{
'xp':'200',
'gp':'150',
'loot':'{
'item':'sword',
'rarity': 'low'
}'
}",
"achievement":"win_match"
// while other messages might be simple
"type":"generic_xp",
"percent":"100",
"status":"complete"
Serialized version of a real message:
"{\"player_stats\":\"{\\\"assist\\\":0,\\\"deaths\\\":0,\\\"kills\\\":0,\\\"team_index\\\":2}\",\"round_attr\":\"{\\\"max_player_count\\\":4,\\\"rdur\\\":0,\\\"round\\\":1,\\\"team_player_count\\\":{\\\"team_1\\\":1,\\\"team_2\\\":0},\\\"team_score\\\":0}\",\"custom\":\"{\\\"armor\\\":\\\"armor_pickup_lv2\\\",\\\"balance\\\":550,\\\"helmet\\\":\\\"helmet_pickup_lv2\\\",\\\"misc\\\":[{\\\"count\\\":48,\\\"item_id\\\":\\\"shotgun\\\"},{\\\"count\\\":120,\\\"item_id\\\":\\\"bullet\\\"},{\\\"count\\\":2,\\\"item_id\\\":\\\"health_pickup_combo_small\\\"},{\\\"count\\\":2,\\\"item_id\\\":\\\"health_pickup_health_small\\\"}],\\\"weapon_1\\\":\\\"mp_weapon_semipistol\\\",\\\"weapon_2\\\":\\\"mp_weapon_shotgun_pistol\\\"}\",\"gdur\":\"0\"}"
To complicate even more
Create a model class is not an option because this json is completely dynamic
Flatting the dictionary is not possible because the json may have duplicated key names, but under different hierarchy
I cant request to change the Kafka message
What I am trying to do
The end user will define rules that I need to check if I find a match. For instance, a rule could be reward.xp == 200 or reward.loot.rarity == high or status == complete. These rules will be defined by the user so it cant be hardcoded, however I can decide with data structure to use to save them. So for each Kafka message, I have to iterate through that dictionary and try to find a match with the rules.
What I have tried
I ve tried JsonConvert.Deserialize to object, dynamic, ExpandoObject and none could handle the nested json hierarchy. They just got the 1st level correct. Same result with JObject.Parse as well.
Parse the JSON using whatever parser you like (I used Newtonsoft.Json).
Then recursively visit the hierarchy and copy each property to a flat list using the full path to each property value as a key. You can then iterate that flat list.
Edit: Comment requested supporting arrays, so this version does.
https://dotnetfiddle.net/6ykHT0
using System;
using Newtonsoft.Json.Linq;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
string json = #"{
'reward': {
'xp': '200',
'gp': '150',
'loot': {
'item': 'sword',
'rarity': 'low',
'blah': {
'socks': 5
}
},
'arrayofint': [1,2,3,4],
'arrayofobj': [
{
'foo': 'bar',
'stuff': ['omg!', 'what?!']
},
{
'foo': 'baz',
'stuff': ['a', 'b']
}
],
'arrayofarray': [
[1,2,3],
[4,5,6]
],
'arrayofheterogenousjunk': [
'a',
2,
{ 'objprop': 1 },
['staahp!']
]
},
'achievement': 'win_match'
}";
JObject data = JObject.Parse(json);
IList<string> nodes = flattenJSON(data);
Console.WriteLine(string.Join(Environment.NewLine, nodes));
}
private static IList<string> flattenJSON(JToken token)
{
return _flattenJSON(token, new List<string>());
}
private static IList<string> _flattenJSON(JToken token, List<string> path)
{
var output = new List<string>();
if (token.Type == JTokenType.Object)
{
// Output the object's child properties
output.AddRange(token.Children().SelectMany(x => _flattenJSON(x, path)));
}
else if (token.Type == JTokenType.Array)
{
// Output each array element
var arrayIndex = 0;
foreach (var child in token.Children())
{
// Append the array index to the end of the last path segment - e.g. someProperty[n]
var newPath = new List<string>(path);
newPath[newPath.Count - 1] += "[" + arrayIndex++ + "]";
output.AddRange(_flattenJSON(child, newPath));
}
}
else if (token.Type == JTokenType.Property)
{
var prop = token as JProperty;
// Insert the property name into the path
output.AddRange(_flattenJSON(prop.Value, new List<string>(path) { prop.Name }));
}
else
{
// Join the path segments delimited with periods, followed by the literal value
output.Add(string.Join(".", path) + " = " + token.ToString());
}
return output;
}
}
Output:
reward.xp = 200
reward.gp = 150
reward.loot.item = sword
reward.loot.rarity = low
reward.loot.blah.socks = 5
reward.arrayofint[0] = 1
reward.arrayofint[1] = 2
reward.arrayofint[2] = 3
reward.arrayofint[3] = 4
reward.arrayofobj[0].foo = bar
reward.arrayofobj[0].stuff[0] = omg!
reward.arrayofobj[0].stuff[1] = what?!
reward.arrayofobj[1].foo = baz
reward.arrayofobj[1].stuff[0] = a
reward.arrayofobj[1].stuff[1] = b
reward.arrayofarray[0][0] = 1
reward.arrayofarray[0][1] = 2
reward.arrayofarray[0][2] = 3
reward.arrayofarray[1][0] = 4
reward.arrayofarray[1][1] = 5
reward.arrayofarray[1][2] = 6
reward.arrayofheterogenousjunk[0] = a
reward.arrayofheterogenousjunk[1] = 2
reward.arrayofheterogenousjunk[2].objprop = 1
reward.arrayofheterogenousjunk[3][0] = staahp!
achievement = win_match
PREVIOUS VERSION (NO ARRAY SUPPORT)
This doesn't properly support arrays - it will output the contents of a property that is an array as the raw JSON - i.e. it won't traverse into the array.
https://dotnetfiddle.net/yZbwul
public static void Main()
{
string json = #"{
'reward': {
'xp': '200',
'gp': '150',
'loot': {
'item': 'sword',
'rarity': 'low',
'blah': {
'socks': 5
}
}
},
'achievement': 'win_match'
}";
JObject data = JObject.Parse(json);
IList<string> nodes = flattenJSON(data, new List<string>());
Console.WriteLine(string.Join(Environment.NewLine, nodes));
}
private static IList<string> flattenJSON(JObject obj, IList<string> path)
{
var output = new List<string>();
foreach (var prop in obj.Properties())
{
if (prop.Value.Type == JTokenType.Object)
{
output.AddRange(flattenJSON(prop.Value as JObject, new List<string>(path){prop.Name}));
}
else
{
var s = string.Join(".", new List<string>(path) { prop.Name }) + " = " + prop.Value.ToString();
output.Add(s);
}
}
return output;
}
Output:
reward.xp = 200
reward.gp = 150
reward.loot.item = sword
reward.loot.rarity = low
reward.loot.blah.socks = 5
achievement = win_match
I'm parsing a CSV file in a c# .net windows form app, taking each line into a class I've created, however I only need access to some of the columns AND the files being taken in are not standardized. That is to say, number of fields present could be different and the columns could appear in any column.
CSV Example 1:
Position, LOCATION, TAG, NAME, STANDARD, EFFICIENCY, IN USE,,
1, AFT-D3, P-D3101A, EQUIPMENT 1, A, 3, TRUE
2, AFT-D3, P-D3103A, EQUIPMENT 2, B, 3, FALSE
3, AFT-D3, P-D2301A, EQUIPMENT 3, A, 3, TRUE
...
CSV Example 2:
Position, TAG, STANDARD, NAME, EFFICIENCY, LOCATION, BACKUP, TESTED,,
1, P-D3101A, A, EQUIPMENT 1, 3, AFT-D3, FALSE, TRUE
2, P-D3103A, A, EQUIPMENT 2, 3, AFT-D3, TRUE, FALSE
3, P-D2301A, A, EQUIPMENT 3, 3, AFT-D3, FALSE, TRUE
...
As you can see, I will never know the format of the file I have to analyse, the only thing I know for sure is that it will always contain the few columns that I need.
My solution to this was to ask the user to enter the columns required and set as strings, the using their entry convert that to a corresponding integer that i could then use as a location.
string standardInpt = "";
string nameInpt = "";
string efficiencyInpt = "";
user would then enter a value from A to ZZ.
int standardLocation = 0;
int nameLocation = 0;
int efficiencyLocation = 0;
when the form is submitted. the ints get their final value by running through an if else... statement:
if(standard == "A")
{
standardLocation = 0;
}
else if(standard == "B")
{
standardLocation = 1;
}
...
etc running all the way to if VAR1 == ZZ and then the code is repeated for VAR2 and for VAR3 etc..
My class would partially look like:
class Equipment
{
public string Standard { get; set;}
public string Name { get; set; }
public int Efficiency { get; set; }
static Equipment FromLine(string line)
{
var data = line.split(',');
return new Equipment()
{
Standard = data[standardLocation],
Name = [nameLocation],
Efficiency = int.Parse(data[efficiencyLocation]),
};
}
}
I've got more code in there but i think this highlights where I would use the variables to set the indexes.
I'm very new to this and I'm hoping there has got to be a significantly better way to achieve this without having to write so much potentially excessive, repetitive If Else logic. I'm thinking some kind of lookup table maybe, but i cant figure out how to implement this, any pointers on where i could look?
You could make it automatic by finding the indexes of the columns in the header, and then use them to read the values from the correct place from the rest of the lines:
class EquipmentParser {
public IList<Equipment> Parse(string[] input) {
var result = new List<Equipment>();
var header = input[0].Split(',').Select(t => t.Trim().ToLower()).ToList();
var standardPosition = GetIndexOf(header, "std", "standard", "st");
var namePosition = GetIndexOf(header, "name", "nm");
var efficiencyPosition = GetIndexOf(header, "efficiency", "eff");
foreach (var s in input.Skip(1)) {
var line = s.Split(',');
result.Add(new Equipment {
Standard = line[standardPosition].Trim(),
Name = line[namePosition].Trim(),
Efficiency = int.Parse(line[efficiencyPosition])
});
}
return result;
}
private int GetIndexOf(IList<string> input, params string[] needles) {
return Array.FindIndex(input.ToArray(), needles.Contains);
}
}
You can use the reflection and attribute.
Write your samples in ,separated into DisplayName Attribute.
First call GetIndexes with the csv header string as parameter to get the mapping dictionary of class properties and csv fields.
Then call FromLine with each line and the mapping dictionary you just got.
class Equipment
{
[DisplayName("STND, STANDARD, ST")]
public string Standard { get; set; }
[DisplayName("NAME")]
public string Name { get; set; }
[DisplayName("EFFICIENCY, EFFI")]
public int Efficiency { get; set; }
// You can add any other property
public static Equipment FromLine(string line, Dictionary<PropertyInfo, int> map)
{
var data = line.Split(',').Select(t => t.Trim()).ToArray();
var ret = new Equipment();
Type type = typeof(Equipment);
foreach (PropertyInfo property in type.GetProperties())
{
int index = map[property];
property.SetValue(ret, Convert.ChangeType(data[index],
property.PropertyType));
}
return ret;
}
public static Dictionary<PropertyInfo, int> GetIndexes(string headers)
{
var headerArray = headers.Split(',').Select(t => t.Trim()).ToArray();
Type type = typeof(Equipment);
var ret = new Dictionary<PropertyInfo, int>();
foreach (PropertyInfo property in type.GetProperties())
{
var fieldNames = property.GetCustomAttribute<DisplayNameAttribute>()
.DisplayName.Split(',').Select(t => t.Trim()).ToArray();
for (int i = 0; i < headerArray.Length; ++i)
{
if (!fieldNames.Contains(headerArray[i])) continue;
ret[property] = i;
break;
}
}
return ret;
}
}
try this if helpful:
public int GetIndex(string input)
{
input = input.ToUpper();
char low = input[input.Length - 1];
char? high = input.Length == 2 ? input[0] : (char?)null;
int indexLow = low - 'A';
int? indexHigh = high.HasValue ? high.Value - 'A' : (int?)null;
return (indexHigh.HasValue ? (indexHigh.Value + 1) * 26 : 0) + indexLow;
}
You can use ASCII code for that , so no need to add if else every time
ex.
byte[] ASCIIValues = Encoding.ASCII.GetBytes(standard);
standardLocation = ASCIIValues[0]-65;
I have used String Builder to generate a RAW SQL QUERY in C#.
List<string> columns = new List<string>();
columns.Add("id");
columns.Add("Temp");
StringBuilder SqlStatement = new StringBuilder();
SqlStatement.Append("Select ");
for (int i = 0; i < columns.Count; i++)
{
if (i == columns.Count - 1)
{
SqlStatement.Append(columns[i]);
}
else
{
SqlStatement.Append(columns[i]);
SqlStatement.Append(",");
}
}
SqlStatement.Append(" FROM graph_update");
var ctx = new graphDBContext();
var result = ctx.Database.SqlQuery<graphDBContext>(SqlStatement.ToString()).ToList();
This translates into SELECT id,Temp FROM graph_update
And the result gives me
id = 1, temp = 20
id = 2 temp = 30
How do I get all these values????
I'm too use to:
foreach(var item in result)
{
item.id = id;
item.temp = temp;
}
But it won't let me.
EDIT:
Sorry but I'm not sure what you mean. Here is my debugger
Try to use foreach like this if theres no error return
foreach(var v in result)
{
String v0 = v[0].ToString();
String v1 = v[1].ToString();
}
Assuming you have EF > 6, then the ctx.Database.SqlQuery, according to the method documentation:
Creates a raw SQL query that will return elements of the given generic type.
The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
With that in mind you can do something like this:
public class GraphUpdateResult
{
public int Id {get; set;}
public decimal Temp {get; set;}
}
Then in your current method:
var result = ctx.Database.SqlQuery<GraphUpdateResult>SqlStatement.ToString()).ToList();
foreach (var graphResult in result)
{
Console.WriteLine(graphResult.Id);
Console.WriteLine(graphResult.Temp);
}
You can add more columns to the GraphUpdateResult class for EF to bind to, even if in some queries you don't specify them in the select statement.
I hope this helps.
foreach(var item in result)
{
var id = item.id;
var temp = item.temp;
}
in your code above, you are trying to assign the values to the item, instead of retrieving.
You can use a ORM-Mapper like
https://stormy.codeplex.com/SourceControl/latest#Stormy.cs
It is a very light Mapper and you can look how it works.
It maps the reader Data to the Object data:
public class CatMapper : ISelectable<Cat>
{
public Cat ApplySelect(IDataReader reader)
{
return new Cat()
{
Name = reader["name"].ToString(),
Weight = (float)reader["weight"]
};
}
}
What I am trying to do is to have an array of references to variables. By which I mean the equivalent to a C array of pointers to ints (for example).
Example: (!!not real code!!)
int a = 4;
int b = 5;
int c = 6;
List<ref int> tmp = new List<ref int>();
tmp.Add(ref a);
tmp.Add(ref b);
tmp.Add(ref c);
tmp[0] = 16;
tmp[1] = 3;
tmp[2] = 1000;
Console.Writeline(tmp[0] + " " + a); // 16 16
Console.Writeline(tmp[1] + " " + b); // 3 3
Console.Writeline(tmp[2] + " " + c); // 1000 1000
The specifics of my case: I have a list of strings that will correspond to the keys in a dictionary. What I think I want to have is a list of Tuples where Type1 is a reference to either an int or string, and Type2 is a reference to an Textbox.
I will be iterating through this list, using the string to get the value from the dictionary (and doing stuff with that data) then storing the results of that into Type1. And eventually I will be taking the data from those Type1 variable references and copying their data to the corresponding Type2 Textbox.
That's the gist of what I think I want to do. If someone thinks that my approach is overly complicated, I will say that I need to keep the Textboxes as they are sadly, so I can't just make an array and iterate through it. And it would be perferable to keep the Type1 variables seperate too, though not quite as necessary.
Now, from reading around, I thought Func<> looked like it was the closest thing to being useful for what I want, so I tried to use the following (with Type1, as an object because it needs to handle both ints and strings)
List<Tuple<string, Func<object>, Func<object>>>
but I was unsure how to use that to get references to the variables.
What you're specifically asking for isn't possible; what would be more appropriate (and has the convenience of actually working!) would be to design a class structure around what you're trying to do.
For instance, something like this:
public class MyObject // Name it appropriately
{
public object Value { get; set; }
public string Key { get; set; }
public TextBox TextBox { get; set; }
}
Then, in your code, you can do something akin to this...
Dictionary<string, object> values = ...
List<MyObject> objects = ...
foreach(var item in objects)
{
item.Value = values[item.Key];
// process your data
item.TextBox = item.Value.ToString();
}
Obviously, this is just a rough design and the class here serves as little more than a data transfer container. You could make the class "smarter" by, for example, using the setter for the Value property to set the value of the TextBox automatically. But this should hopefully give you the general idea of how something like this would be done in an OO fashion.
EDIT Here's how your example would look.
MyObject a = new MyObject() { Value = 4 };
MyObject b = new MyObject() { Value = 5 };
MyObject c = new MyObject() { Value = 6 };
List<MyObject> tmp = new List<MyObject>();
tmp.Add(a);
tmp.Add(b);
tmp.Add(c);
tmp[0].Value = 16;
tmp[1].Value = 3;
tmp[2].Value = 1000;
Console.Writeline(tmp[0].Value.ToString() + " " + a.Value.ToString()); // 16 16
Console.Writeline(tmp[1].Value.ToString() + " " + b.Value.ToString()); // 3 3
Console.Writeline(tmp[2].Value.ToString() + " " + c.Value.ToString()); // 1000 1000
You can't store references using C#. You can only use the ref keyword when calling a method.
You can use pointers, but you can only do that with a fixed expression and within an unsafe context.
It is possible to fake this kind of thing using delegates, but I'm not sure if it's what you're looking for. I'm also fairly sure that you really need to redesign your approach, but nevertheless, here's an example of how you can fake it...
Firstly, write a "value wrapper" class like so:
public class ValueWrapper<T>
{
readonly Func<T> get;
readonly Action<T> set;
public ValueWrapper(Func<T> get, Action<T> set)
{
this.get = get;
this.set = set;
}
public T Value
{
get
{
return get();
}
set
{
set(value);
}
}
}
Then you can use that to change values:
void run()
{
int x = 0;
var intWrapper = new ValueWrapper<int>(() => x, value => x = value);
test(intWrapper);
Console.WriteLine(x); // Prints 42, which shows that x was changed.
TextBox textBox = new TextBox {Text = ""};
var stringWrapper = new ValueWrapper<string>(() => textBox.Text, value => textBox.Text = value);
test(stringWrapper);
Console.WriteLine(textBox.Text); // Prints "Changed".
}
static void test(ValueWrapper<int> wrapper)
{
wrapper.Value = 42;
}
static void test(ValueWrapper<string> wrapper)
{
wrapper.Value = "Changed";
}
You can also create a wrapper in one method and pass it to a different method which uses the wrapper to change a property in the original wrapped object, like so:
void run()
{
TextBox textBox = new TextBox {Text = ""};
var wrapper = test1(textBox);
test2(wrapper);
Console.WriteLine(textBox.Text); // Prints "Changed"
}
void test2(ValueWrapper<string> wrapper)
{
wrapper.Value = "Changed";
}
ValueWrapper<string> test1(TextBox textBox)
{
return new ValueWrapper<string>(() => textBox.Text, value => textBox.Text = value);
}
Warning: This does lead to some fairly head-scratching code, for example:
void run()
{
var intWrapper = test();
intWrapper.Value = 42;
Console.WriteLine(intWrapper.Value); // Works, but where is the value? It can't be the x inside test()!
}
ValueWrapper<int> test()
{
int x = 0;
var intWrapper = new ValueWrapper<int>(() => x, value => x = value);
return intWrapper;
}
So we returned a ValueWrapper from test() which is apparently wrapping a local variable from inside test(). And then we can apparently change the value and print it out...
This isn't really what's happening, of course, but it can be quite confusing!
you can use pointers in this case, use unsafe keyword for method and set project unsafe to allow pointers in c#, also you can encapsulate the value in a class and in C# each class is of reference type
i used this and works perfect:
exp.
public int value1 = 3;
public int value2 = 4;
public int value3 = 5;
public void Method1()
{
int[] values = { value1, value2, value3};
for (int i = 0; i < values.Length; i ++)
{
Console.WriteLine(values[i]);
}
}
I need your help with the following code below. Basically I have a class called "Job" which has some public fields. I'm passing to my method "ApplyFilter" two parameters "job_in" and "job_filters". First parameter contains actual data, and the second one has instructions (if any). I need to iterate through "job_in" object, read it's data, apply any instructions by reading "job_filters", modify data (if needed) and return it in a new "job_out" object. Everything works fine till i need to store my data in "job_out" object:
public class Job
{
public string job_id = "";
public string description = "";
public string address = "";
public string details = "";
}
...
private Job ApplyFilters(Job job_in, Job job_filters)
{
Type type = typeof(Job);
Job job_out = new Job();
FieldInfo[] fields = type.GetFields();
// iterate through all fields of Job class
for (int i = 0; i < fields.Length; i++)
{
List<string> filterslist = new List<string>();
string filters = (string)fields[i].GetValue(job_filters);
// if job_filters contaisn magic word "$" for the field, then do something with a field, otherwise just copy it to job_out object
if (filters.Contains("$"))
{
filters = filters.Substring(filters.IndexOf("$") + 1, filters.Length - filters.IndexOf("$") - 1);
// MessageBox.Show(filters);
// do sothing useful...
}
else
{
// this is my current field value
var str_value = fields[i].GetValue(job_in);
// this is my current filed name
var field_name = fields[i].Name;
// I got stuck here :(
// I need to save (copy) data "str_value" from job_in.field_name to job_out.field_name
// HELP!!!
}
}
return job_out;
}
Please help. I've seen a few samples by using properties, but i'm pretty sure it is possible to do the same with fields as well. Thanks!
Try this
public static void MapAllFields(object source, object dst)
{
System.Reflection.FieldInfo[] ps = source.GetType().GetFields();
foreach (var item in ps)
{
var o = item.GetValue(source);
var p = dst.GetType().GetField(item.Name);
if (p != null)
{
Type t = Nullable.GetUnderlyingType(p.FieldType) ?? p.FieldType;
object safeValue = (o == null) ? null : Convert.ChangeType(o, t);
p.SetValue(dst, safeValue);
}
}
}
fields[i].SetValue(job_out, str_value);