I'm trying to implement the C# aspect of a LightWeight JSON Spec JsonR, but cannot get my head around any kind of recursion :-/ If anyone could help out here it would be more than greatly appreciated.
// Mockup class
public class User {
public string Name { get; set; }
public int Age { get; set; }
public List<string> Photos { get; set; }
public List<Friend> Friends { get; set; }
}
// Mockup class
public class Friend {
public string FirstName { get; set; }
public string LastName { get; set; }
}
// Initialize objects
var userList = new List<User>();
userList.Add(new User() {
Name = "Robert",
Age = 32,
Photos = new List<string> { "1.jpg", "2.jpg" },
Friends = new List<Friend>() {
new Friend() { FirstName = "Bob", LastName = "Hope"},
new Friend() { FirstName = "Mr" , LastName = "T"}
}
});
userList.Add(new User() {
Name = "Jane",
Age = 21,
Photos = new List<string> { "4.jpg", "5.jpg" },
Friends = new List<Friend>() {
new Friend() { FirstName = "Foo" , LastName = "Bar"},
new Friend() { FirstName = "Lady" , LastName = "Gaga"}
}
});
The idea behind it all is to now take the above object and split it into 2 separate collections, one containing the keys, and the other containing the values. Like this we can eventually only send the values over the wire thus saving lots of bandwidth, and then recombine it on the client (a js implementation for recombining exists already)
If all went well we should be able to get this out of the above object
var keys = new object[] {
"Name", "Age", "Photos",
new { Friends = new [] {"FirstName", "LastName"}}};
var values = new [] {
new object[] {"Robert", 32, new [] {"1.jpg", "2.jpg"},
new [] { new [] {"Bob", "Hope"},
new [] {"Mr", "T"}}},
new object[] {"Jane", 21, new [] {"4.jpg", "5.jpg"},
new [] { new [] {"Foo", "Bar"},
new [] {"Lady", "Gaga"}}}};
As a verification we can test the conformity of the result with
Newtonsoft.Json.JsonConvert.SerializeObject(keys).Dump("keys");
// Generates:
// ["Name","Age","Photos",{"Friends":["FirstName","LastName"]}]
Newtonsoft.Json.JsonConvert.SerializeObject(values).Dump("values");
// Generates:
// [["Robert",32,["1.jpg","2.jpg"],[["Bob","Hope"],["Mr","T"]]],["Jane",21,["4.jpg","5.jpg"],[["Foo","Bar"],["Lady","Gaga"]]]]
A shortcut i explored was to take advantage of Newton's JArray/JObject facilities like this
var JResult = Newtonsoft.Json.JsonConvert.DeserializeObject(
Newtonsoft.Json.JsonConvert.SerializeObject(userList));
Like this we end up with a sort of array object that we can already start iterating on
Anyone think they can crack this in a memory/speed efficient way ?
I have a solution that works with your example data. It is not a universal solution and may fail with other examples, but it shows how to use recursions. I did not include any error handling. A real-world solution would have to.
I use this helper method which gets the item type of the generic lists:
private static Type GetListItemType(Type listType)
{
Type itemType = null;
foreach (Type interfaceType in listType.GetInterfaces()) {
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition() == typeof(IList<>)) {
itemType = interfaceType.GetGenericArguments()[0];
break;
}
}
return itemType;
}
Now, the recursion:
public void SplitKeyValues(IList source, List<object> keys, List<object> values)
{
Type itemType = GetListItemType(source.GetType());
PropertyInfo[] properties = itemType.GetProperties();
for (int i = 0; i < source.Count; i++) {
object item = source[i];
var itemValues = new List<object>();
values.Add(itemValues);
foreach (PropertyInfo prop in properties) {
if (typeof(IList).IsAssignableFrom(prop.PropertyType) &&
prop.PropertyType.IsGenericType) {
// We have a List<T> or array
Type genericArgType = GetListItemType(prop.PropertyType);
if (genericArgType.IsValueType || genericArgType == typeof(string)) {
// We have a list or array of a simple type
if (i == 0)
keys.Add(prop.Name);
List<object> subValues = new List<object>();
itemValues.Add(subValues);
subValues.AddRange(
Enumerable.Cast<object>(
(IEnumerable)prop.GetValue(item, null)));
} else {
// We have a list or array of a complex type
List<object> subKeys = new List<object>();
if (i == 0)
keys.Add(subKeys);
List<object> subValues = new List<object>();
itemValues.Add(subValues);
SplitKeyValues(
(IList)prop.GetValue(item, null), subKeys, subValues);
}
} else if (prop.PropertyType.IsValueType ||
prop.PropertyType == typeof(string)) {
// We have a simple type
if (i == 0)
keys.Add(prop.Name);
itemValues.Add(prop.GetValue(item, null));
} else {
// We have a complex type.
// Does not occur in your example
}
}
}
}
I call it like this:
List<User> userList = InitializeObjects();
List<object> keys = new List<object>();
List<object> values = new List<object>();
SplitKeyValues(userList, keys, values);
InitializeObjects initializes the user list as you did above.
UPDATE
The problem is that you are using an anonymous type new { Friends = ... }. You would have to create an anonymous type dynamically by using reflection. And that's pretty nasty. The article "Extend Anonymous Types using Reflection.Emit" seems to do it. (I didn't test it).
Maybe an easier approach would do the job. I suggest creating a helper class for the description of class types.
public class Class
{
public string Name { get; set; }
public List<object> Structure { get; set; }
}
Now let's replace an else case in the code above:
...
} else {
// We have a list or array of a complex type
List<object> subKeys = new List<object>();
var classDescr = new Class { Name = genericArgType.Name, Structure = subKeys };
if (i == 0)
keys.Add(classDescr);
List<object> subValues = new List<object>();
itemValues.Add(subValues);
SplitKeyValues(
(IList)prop.GetValue(item, null), subKeys, subValues);
}
...
The result is:
You may want to try an external tool, like AutoMapper, which is a convention based mapping library.
I suggest to try the flattening feature with your own conventions.
I'm sorry I cannot write out an example because of lack of time, but I think the ideas behind this open source library might help a lot.
Related
I want to get a list of all (unique) Apple (or Oranges) objects:
var theBigFruitsList = new List<Fruits>{
new Fruits(){
Apples = new List<Apple>{
new Apple { Id = 1 },
new Apple { Id = 2 }
}
},
Oranges = new List<Orange>{
new Orange { Id = 4 },
new Orange { Id = 5 }
}
},
FruitBoolean = False,
FruitCount = 4,
},
new Fruits(){
Apples = new List<Apple>{
new Apple { Id = 3 },
new Apple { Id = 1 },
}
},
Oranges = new List<Orange>{
new Orange { Id = 6 },
}
}
FruitBoolean = False,
FruitCount = 3,
}
}
I have written this method for it:
public static List<Apple> GetApplesFromBigFruitsList(List<Fruits> theBigFruitsList )
{
var dc = new Dictionary<long, Apple>();
foreach (var fruits in theBigFruitsList)
{
foreach (var apple in fruits.Apples)
{
if (!dc.ContainsKey(apple.Id))
dc.Add(apple.Id, apple);
}
}
return dc.Values.ToList();
}
But as besides Apples and Oranges there are many other types of 'Fruits' in that object, I have that method more then 10 times where the word Apple is just replaced with Orange.. It would make sense to make it generic.
I wrote this function but it gives an error as Fruits class does not implement an enumerator. Please help!
public static List<T> FilterFruits<T>(T typeToGet, List<Fruits> theBigFruitsList)
{
List<T> fruitResult = new List<T>();
var fruitType = typeToGet.GetType();
foreach (var fruits in theBigFruitsList)
{
foreach (var fruit in fruits) //errors, intention is to loop over all properties in the Fruits entity to find one specific type
if (fruit.GetType() == fruitType) //check if type is Apple
{
fruitResult.AddRange(fruits); //add the Apples to a list
}
}
return fruitResult;
}
The classes:
public class Fruits{
public List<Apple> Apples { get; set; }
public List<Orange> Oranges { get; set; }
public List<Pineapple> Pineapples { get; set; }
public List<Mango> Mangos { get; set; }
public bool FruitBoolean { get; set; }
public long FruitCount { get; set; }
}
public class Apple{
public long Id { get; set; }
}
public class Orange{
public long Id { get; set; }
}
public class Pineapple{
public long Id { get; set; }
}
public class Mango{
public long Id { get; set; }
}
The desired method result:
var Apples = List<Apple>{
new Apple { Id = 1 },
new Apple { Id = 2 },
new Apple { Id = 3 }
}
With One Big List
Storing each type of fruit in its own separate list is...weird. I suggest you combine them in a single list. If you can't change the design, you can combine them at run time like this:
IEnumerable<object> GetAllFruits(Fruits bigFruitlist)
{
return ((IEnumerable<object>)bigFruitlist.Apples)
.Concat((IEnumerable<object>)bigFruitlist.Oranges)
.Concat((IEnumerable<object>)bigFruitlist.Mangoes)
.Concat((IEnumerable<object>)bigFruitlist.Pineapples);
}
Of course it would be way better if all of your fruits had a common interface-- then you wouldn't need IEnumerable<object>-- but this can still work if you can't make that change either.
Once you have the combined list, the rest is easy:
List<T> FilterFruits<T>(Fruits bigFruitlist)
{
return GetAllFruits(bigFruitList).OfType<T>().ToList();
}
With an Array of Lists
If there is some reason you want to avoid enumerating all of the lists (i.e. the lists are massive and performance is a concern), you can also do it with an array of lists.
object[] GetAllFruitLists(Fruits bigFruitlist)
{
return new object[]
{
bigFruitlist.Apples,
bigFruitlist.Oranges,
bigFruitlist.Mangoes,
bigFruitlist.Pineapples
};
}
List<T> FilterFruits<T>(Fruits bigFruitlist)
{
return GetAllFruitLists(bigFruitList).OfType<List<T>>().Single();
}
To interrogate an object's type at runtime use Reflection. Like this:
public static List<T> FilterFruits<T>(List<Fruits> fruitsList) where T : IFruit
{
List<T> fruitResult = new List<T>();
var fruitType = typeof(T);
foreach (var fruits in fruitsList)
{
foreach (var fp in typeof(Fruits).GetProperties())
{
if (fp.PropertyType == typeof(List<T>)) //check if type is Apple
{
fruitResult.AddRange((List<T>)(object)fp.GetValue(fruits)); //add the Apples to a list
}
}
}
return fruitResult;
}
(T)(object)o is the C# idiom for performing a cast that can't by verified by the compiler, and may possibly fail at runtime, which we know that it won't but the compiler can't because it doesn't understand Reflection.
To do this without Reflection (which can be too slow for some scenarios), you can do something like this:
public static List<T> GetDistinct<T>( IEnumerable<Fruits> fruitsList) where T : IFruit
{
var ft = typeof(T);
Func<Fruits, List<T>> picker;
if (ft == typeof(Apple))
{
picker = (fruits) => (List<T>)(object)fruits.Apples;
}
else if (ft == typeof(Mango))
{
picker = (fruits) => (List<T>)(object)fruits.Mangos;
}
else
{
throw new NotImplementedException($"Fruit Type {ft.Name} not supported");
}
var rv = new Dictionary<long, T>();
foreach (var t in fruitsList.SelectMany(picker))
{
if (!rv.ContainsKey(t.Id))
{
rv.Add(t.Id, t);
}
}
return rv.Values.ToList();
}
I have below class structure and a list of CollectionInstance
public class CollectionInstance
{
public string Name { get; set; }
public List<CollectionProperty> CollectionProperties { get; set; }
}
public class CollectionProperty
{
public string Name { get; set; }
public object Value { get; set; }
public string DataType { get; set; }
}
Here is list of CollectionInstance. Currently it has only two data types double and string, but I have more data types
var lstCollectionInstances = new List<CollectionInstance>
{
new CollectionInstance
{
Name = "A",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P1", Value = 10, DataType = "Double"}
}
},
new CollectionInstance
{
Name = "A",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P2", Value = "H1", DataType = "String"}
}
},
new CollectionInstance
{
Name = "B",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P1", Value = 20, DataType = "Double"}
}
},
new CollectionInstance
{
Name = "B",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P2", Value = "H2", DataType = "String"}
}
},
};
Now my goal to fetch all the different data type and filter list of CollectionInstance based on the data type. May be a dictionary or could be other collection as well, where I should store data type as key and filtered CollectionInstance as a value.
I tried below, but what could be the best way?
var dictionary = new Dictionary<string, List<CollectionInstance>>();
var dataTypesGroups = lstCollectionInstances
.SelectMany(x => x.CollectionProperties).GroupBy(x => x.DataType);
foreach (var dataType in dataTypesGroups)
{
dictionary.Add(dataType.Key, GetFilterData(lstCollectionInstances, dataType.Key));
}
private static List<CollectionInstance> GetFilterData(IEnumerable<CollectionInstance> lst, string dataType)
{
return lst.Where(x => x.CollectionProperties.Any(y => y.DataType == dataType)).ToList();
}
You could keep reference to parent CollectionInstance when grouping and reuse that when selecting results:
lstCollectionInstances
.SelectMany(x => x.CollectionProperties, (i, c) => new {CollectionInstance = i, CollectionProperty = c})
.GroupBy(x => x.CollectionProperty.DataType)
.ToDictionary(c => c.Key, c => c.Select(d => d.CollectionInstance) )
UPD
here we leverage this overload of .SelectMany(). So Instead of List<CollectionProperty> you end up having List<Tuple<CollectionInstance,CollectionProperty>> (well, i opted for anonymous type, but this does not matter much). You basically enhance each child object with reference to its parent. And since all these are just references - you don't trade a lot of memory for having it.
And when you group it - you get an option to not select the CollectionProperty, but rather the parent object directly.
I hope this makes sense
Build a dictionary that for each data type stores a list of instances with a property of the key data type.
var result = instances
.SelectMany(x => x.Properties)
.Select(x => x.DataType)
.Distict()
.ToDictionary(x => x, x => GetInstancesWithPropertyOfType(x, instances));
Given the following is defined:
public class Instance
{
public string Name { get; set; }
public List<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public object Value { get; set; }
public string DataType { get; set; }
}
List<Instance> GetInstancesWithPropertyOfType(string dataType, IEnumerable<Instance> instances) =>
instances.Where(x => x.Properties.Any(y => y.DataType == dataType)).ToList();
Personally think that using LINQ on this just makes it more unreadable and harder to understand. This is basically a two loop operation; for each x in instances/foreach y in x.properties/add x to dictionary indexed by y.z and would be most easily understood by keeping it as such. This minimizes the amount of work done by the framework too; here we create no unnecessary extra objects, lists, groupings etc in the quest to enumerate a 2-deep object hierarchy and create a dictionary, and even a coder who never saw LINQ can understand it:
var dictionary = new Dictionary<string, List<CollectionInstance>>();
foreach (var ci in lstCollectionInstances){
foreach(var cp in ci.CollectionProperties){
if(!dictionary.ContainsKey(cp.DataType))
dictionary[cp.Key] = new List<CollectionInstance>();
dictionary[cp.Key].Add(ci);
}
}
LINQ is a hammer; not every problem is a nail
Pretty silly question to ask. but could not figure it out .
In a C# MVC Controller action , I need to model a Json Array for testing purposes.
But this shows me compilation errors instead of being a valid Json:
var result = {
"controllerId": "controller1",
"controllerName": "ControllerOne"
};
But this is perfectly valid :
var scheduleResult = new[]
{
new { scheduleId = "schedule1",scheduleName = "scheduleOne"},
new { scheduleId = "schedule2",scheduleName = "scheduleTwo"}
};
Why so ?
Also how to write a nested Json array :
I tried :
var scheduleResult = new[]
{
new { scheduleId = "schedule1",scheduleName = "scheduleOne",new[]{ new {doorId="Door1",doorName="DoorOne"}, new { doorId = "Door2", doorName = "DoorTwo" } } },
new { scheduleId = "schedule2",scheduleName = "scheduleTwo"}
};
But it shows errors in syntax. What to do ?
I Need to have nested array within each element of that array .
Thanks in advance.
Well, C# does not support the way you wrote. You can't just type in JSON in C# and expect it to work unfortunately. You can try like that with anonymous type:
var result = new
{
controllerId = "controller1",
controllerName = "ControllerOne",
myArray = new []
{
"a",
"b"
}
};
This is converted to JSON no problem if you return it as a result of API call.
The nested arrays you are talking about don't work because you need to give them a name, you can't have array property without a name. See example above.
Why don't you use Dictionary<TKey, TValue> with Newtonsoft.Json?
Simple json:
IDictionary<string, string> obj = new Dictionary<string, string>();
obj.Add("controllerId", "controller1");
obj.Add("controllerName", "ControllerOne");
// {"controllerId":"controller1","controllerName":"ControllerOne"}
string json = JsonConvert.SerializeObject(obj);
Nested json:
IList<string> obj = new List<string>();
IDictionary<string, string> first = new Dictionary<string, string>();
first.Add("scheduleId ", "schedule1");
first.Add("scheduleName", "scheduleOne");
IDictionary<string, string> second = new Dictionary<string, string>();
second.Add("scheduleId ", "schedule2");
second.Add("scheduleName", "scheduleTwo");
string first_json = JsonConvert.SerializeObject(first);
string second_json = JsonConvert.SerializeObject(second);
obj.Add(first_json);
obj.Add(second_json);
// ["{\"scheduleId \":\"schedule1\",\"scheduleName\":\"scheduleOne\"}","{\"scheduleId \":\"schedule2\",\"scheduleName\":\"scheduleTwo\"}"]
string json = JsonConvert.SerializeObject(obj);
Here first of all we should create model class with the same pattern of our return type
public class ScheduleModel
{
public List<Schedule> ScheduleList { get; set; }
}
public class Schedule
{
public int ScheduleId { get; set; }
public string ScheduleName { get; set; }
public List<Door> DoorList { get; set; }
}
public class Door
{
public int DoorId { get; set; }
public string DoorName { get; set; }
}
Now at the controller Action create the test data
List<Door> doorList = new List<Door>();
doorList.Add(new Door{DoorId = "Door1",DoorName = "DoorOne"});
doorList.Add(new Door{DoorId = "Door2",DoorName = "DoorTwo"});
List<Schedule> scheduleList = new List<Schedule>();
scheduleList.Add(new Schedule{
ScheduleId = "schedule1",
ScheduleName = "scheduleOne",
DoorList = doorList
});
scheduleList.Add(new Schedule
{
ScheduleId = "schedule2",
ScheduleName = "scheduleTwo",
});
return Json(scheduleList, JsonRequestBehavior.AllowGet);
If this answer benefits you please mark as an answer.
I have generic object and I need to get item {code, description} from it using LINQ Query
Class
[Serializable]
[XmlRoot("Genders")]
public class Gender
{
[XmlElement("Genders")]
public List<GenderListWrap> GenderListWrap = new List<GenderListWrap>();
}
public class GenderListWrap
{
[XmlAttribute("list")]
public string ListTag { get; set; }
[XmlElement("Item")]
public List<Item> GenderList = new List<Item>();
}
public class Item
{
[XmlElement("CODE")]
public string Code { get; set; }
[XmlElement("DESCRIPTION")]
public string Description { get; set; }
}
here in following screen shot I see my data in GenderObject
* EDIT *
Based on your edits, it appears your issue is related to the datatype you are receiving from your "ObjectToXML" method - this returns something of type Object by the looks of your code, not something of the type you have specified in your question. You could try to cast to the expected type eg. Gender GenderObject = (Gender)SystemCore.XMLPrasing.ObjectToXML(...), which may fail (if the returned type isn't actually Gender). Or you could use the .NET built in XML deserialization, which will be able to return objects of the correct type.
Despite the rather odd data structure you have (whay such effort for a list of genders?), if you want a List<Item>, the following will project just the items into a list:
genders.GenderListWrap.SelectMany(l => l.GenderList).ToList();
(This is assuming you have an instance called genders, constructed something like:
var genders = new Gender
{
GenderListWrap = new List<GenderListWrap>
{
new GenderListWrap
{
GenderList = new List<Item>
{
new Item { Code = "F", Description = "Female" },
new Item { Code = "M", Description = "Male" },
}
},
new GenderListWrap
{
GenderList = new List<Item>
{
new Item { Code = "N", Description = "Neutral" },
}
}
}
};
var genderList = genders.GenderListWrap.SelectMany(l => l.GenderList).ToList();
If, however, you want to get a specific item, you can just use the standard index accessors mentioned in comments, eg. genders.GenderListWrap[0].GenderList[0]
This should work for you, if you cast the object you have to Gender and then do a select on it:
var genderList = ((Gender)GenderObject).GenderListWrap.SelectMany(x => x.GenderList);
This will return a collection of Items on which you will have the {code, description} you are looking for.
I have a simple LINQ query here:
var Staffs = new[]
{
new { id = 1, name = "Jefferson", age = 42},
new { id = 2, name = "Jacobson", age = 54},
new { id = 3, name = "Zhang", age = 34}
};
var payroll = new[]
{
new { pid = 1, wage = 5000},
new { pid = 2, wage = 6500},
new { pid = 3, wage = 6700}
};
var q = from stf in Staffs
from pay in payroll
where stf.id == pay.pid
select new
{
stfObj = stf,
pay.pid,
pay.wage
};
Here, stfObj would be an object containing the id, name and age fields
Here comes the question:
Is it possible to turn the object into the fields themselves without explicitly hard-coding the field names like this:
select new
{
stf.id,
stf.name,
stf.age,
pay.pid,
pay.wage
};
In this way, there will be no need to change the select new block when I add a new field to Staffs, like Gender for example
Is that possible?
(ok, this looks like the question here... anyway, hoping to get better answers here)
Is this what you want!
select new
{
sId=stfObj.id,
sName=stfObj.name,
sAge=stdObj.age,
pId=pay.pid,
pWage=pay.wage
};
Why not simply embed your object ?
select new {
staff = stf,
pay = pay
};
I do not know, what you need this for. But you could try to use Dictionary<> for this. Say, we have a class:
public class Test
{
public string Name { get; set; }
public string Desc { get; set; }
}
So you can do the following:
List<Test> list = new List<Test>
{
new Test
{
Name = "Test 1",
Desc = "Desc 1"
}
};
var temp = list.Select(t =>
{
Dictionary<string, object> values = new Dictionary<string, object>();
foreach (PropertyInfo pi in t.GetType().GetProperties())
values[pi.Name] = pi.GetValue(t, null);
return values;
})
.FirstOrDefault();
temp.ToList().ForEach(p => Console.WriteLine(string.Format("{0}:\t{1}", p.Key, p.Value)));
So if you add a property to the Test class, say, like this:
public bool Extra { get; set; }
You'll get it in the dictionary automatically. Probably you'll have to work with reflection methods overloads to get exactly what you need...