Get Distinct List of Values from Nested Object - c#

I am deserializing an XML file to and object model. Although this is not the actual model, below is similar in structure to what I have.
[Serializable()]
[System.Xml.Serialization.XmlRoot("AutoEnvelope")]
public class AutoBody
{
[XmlArray("AutoBody")]
[XmlArrayItem("Vehicles", typeof(Vehicles))]
public Vehicles[] Vehicles { get; set; }
}
[Serializable()]
public class Vehicles
{
[XmlElement("SelectedCar", typeof(SelectedCar))]
public SelectedCar SelectedCar { get; set; }
[XmlElement("OfferedVehicles", typeof(OfferedVehicles))]
public OfferedVehicles OfferedVehicles { get; set; }
}
[Serializable()]
public class SelectedCar
{
[System.Xml.Serialization.XmlElement("Model")]
public string Model { get; set; }
[System.Xml.Serialization.XmlElement("NumTires")]
public int NumTires { get; set; }
[System.Xml.Serialization.XmlElement("Color")]
public string Color { get; set; }
}
I am trying to get a distinct list of SelectedCar.Color values and have been unsuccessful. Let's assume that I am storing the data in a variable called autoBody, I have tried variations of the following:
List<char> uniqueColors = autoBody.SelectMany(auto => auto.SelectedCar.Color).Distinct().ToList();
I am clearly doing something wrong, but am not clear on how to achieve what I am looking for.

The SelectMany() method is meant for projection multiple arrays (actually anything that implements IEnumerable<T>) into a single array.
For example, if you were having a list of AutoBody items and you wanted to accumulate all of their associated Vehicles into a single array, you would do:
IEnumerable<Vehicles> vehicles = autoBodies.SelectMany(x => x.Vehicles);
But, when you're using SelectMany on a string property (Color in your case), you're basically projecting the string into an IEnumerable<char> (since String does implement IEnumerable<char> because it's actually a sequence of characters).
Try using Select() instead:
List<string> uniqueColors = autoBody.Select(auto => auto.SelectedCar.Color)
.Distinct()
.ToList()
See MSDN

Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication70
{
class Program
{
static void Main(string[] args)
{
AutoBody bodies = new AutoBody()
{
vehicles = new List<Vehicles>()
{
new Vehicles() {
SelectedCar = new SelectedCar() { Model = "Ford", NumTires = 1, Color = "red"}
},
new Vehicles() {
SelectedCar = new SelectedCar() { Model = "Chevy", NumTires = 2, Color = "blue"}
},
new Vehicles() {
SelectedCar = new SelectedCar() { Model = "Jeep", NumTires = 3, Color = "green"}
},
new Vehicles() {
SelectedCar = new SelectedCar() { Model = "Merecedes", NumTires = 4, Color = "red"}
},
}
};
List<string> colors = bodies.vehicles.Select(x => x.SelectedCar).Select(y => y.Color).Distinct().ToList();
}
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("AutoEnvelope")]
public class AutoBody
{
[XmlArray("AutoBody")]
[XmlArrayItem("Vehicles", typeof(Vehicles))]
public List<Vehicles> vehicles { get; set; }
}
[Serializable()]
public class Vehicles
{
[XmlElement("SelectedCar", typeof(SelectedCar))]
public SelectedCar SelectedCar { get; set; }
//[XmlElement("OfferedVehicles", typeof(OfferedVehicles))]
//public OfferedVehicles OfferedVehicles { get; set; }
}
[Serializable()]
public class SelectedCar
{
[System.Xml.Serialization.XmlElement("Model")]
public string Model { get; set; }
[System.Xml.Serialization.XmlElement("NumTires")]
public int NumTires { get; set; }
[System.Xml.Serialization.XmlElement("Color")]
public string Color { get; set; }
}
}

Related

How to recursively map one nested object type to another

I would like to map the data from one list of objects and another. I am looping through CompanyAEmployee list and able to map FullName and Title. But not able to map Children property.
public class CompanyAEmployee
{
public string FullName { get; set; }
public string Title { get; set; }
public List<CompanyAEmployee> Children { get; set; }
}
public class CompanyBEmployee
{
public string Name { get; set; }
public string PositionName { get; set; }
public List<CompanyBEmployee> Children { get; set; }
}
companyAEmployeeList; // stores all employees of companyA
var companyBEmployeeList = new List<CompanyBEmployee>();
foreach(var employee in companyAEmployeeList)
{
var companyBEmployee = new CompanyBEmployee();
companyBEmployee.Name = employee.FullName;
companyBEmployee.PositionName = employee.Title;
//how to map the children??
}
Can someone suggest a way to map Children?
You can create a recusive method, like below:
public CompanyBEmployee ComAToComB(CompanyAEmployee a){
CompanyBEmployee b = new(){
Name = a.FullName,
PositionName = a.Title,
Children = new()
};
foreach(var child in a.Children){
b.Children.Add(ComAToComB(child));
}
return b;
}
And then call it like
var comB = ComAToComB(comA);
So, in my case it was best solution to use extension methods.
You can see example here
https://dotnetfiddle.net/SwhGMY
Main idea is to use such extension method that was called recursively.
public static class ClassConverterExtensions
{
public static CompanyBEmployee ToCompanyBEmployee(this CompanyAEmployee that)
{
var result = new CompanyBEmployee();
result.Name = that.FullName;
result.PositionName = that.Title;
if(that.Children == null)
{
return result;
}
result.Children = new List<CompanyBEmployee>();
foreach(var item in that.Children)
{
result.Children.Add(item.ToCompanyBEmployee());
}
return result;
}
}
Good point is that you can write this without changing source code of classes CompanyBEmployee and CompanyAEmployee
So, this classes is not referenced one to another, but you can write converters From A to B and from B to A without cyclic references.
Here is the solution:
public class CompanyAEmployee
{
public string FullName { get; set; }
public string Title { get; set; }
public List<CompanyAEmployee> Children { get; set; }
public static explicit operator CompanyBEmployee(CompanyAEmployee employee)
{
CompanyBEmployee employee1 = new CompanyBEmployee();
employee1.Name = employee.FullName;
employee1.PositionName = employee.Title;
employee1.Children = new List<CompanyBEmployee>(employee.Children.Count);
int count = employee.Children.Count;
for (int i = 0; i < count; i++)
employee1.Children[i] = (CompanyBEmployee)employee.Children[i];
return employee1;
}
}
public class CompanyBEmployee
{
public string Name { get; set; }
public string PositionName { get; set; }
public List<CompanyBEmployee> Children { get; set; }
}
Now you can just use an assignment operator with explicit casting from CompanyAEmployee to CompanyBEmployee.
Like this:
CompanyAEmployee employee = new CompanyAEmployee();
//... assign all the fields
CompanyBEmployee employee2 = (CompanyBEmployee)employee.
Now you are done!

Sort MultiLevel Data with Linq

I have a data structure as follows:
public class BranchLevel_1
{
public string Name { get; set; }
public ObservableCollection<BranchLevel_2> Children { get; set; }
public BranchLevel_1(string name, List<BranchLevel_2> children)
{
this.Name = name;
this.Children = new ObservableCollection<BranchLevel_2>(children);
}
}
public class BranchLevel_2
{
public ObservableCollection<BranchLevel_3> Contents { get; set; }
public BranchLevel_2(List<string> contents)
{
this.Contents = new ObservableCollection<BranchLevel_3>();
for (int i = 0; i < contents.Count; i++)
{
this.Contents.Add(new BranchLevel_3(contents[i]));
}
}
}
public class BranchLevel_3
{
public string Content { get; set; }
public BranchLevel_3(string text)
{
this.Content = text;
}
}
Sorting data on the first level is easy and I can obtain in easily by:
Level1_Data.OrderBy(item => item.Name).ToList()
However, I am stuck with sorting on the second level. BranchLevel_2 class is just a container for items stored in BranchLevel_3 classes. Therefore I would like to sort Level2 with data stored in BranchLevel_2.Contents1.Content value. This syntax for me seems to be correct and I cannot locate the problem...
Level1_Data.Select(item_Level1 => item_Level1.Children.OrderBy(item_Level2 => item_Level2.Contents[1].Content)).ToList();
Any hints?
Here is the rusult (indicated in yellow is supposed to be sorted alphabetically)
Why not just sort the contents before adding them to the ObservableCollection
public class BranchLevel_2
{
public ObservableCollection<BranchLevel_3> Contents { get; set; }
public BranchLevel_2(List<string> contents)
{
this.Contents = new ObservableCollection<BranchLevel_3>();
foreach (var content in contents.OrderBy(c => c))
{
this.Contents.Add(new BranchLevel_3(content));
}
}
}
Here is a solution that solved the problem, thanks to suggestion from #bhmahler
public class BranchLevel_1
{
public string Name { get; set; }
public ObservableCollection<BranchLevel_2> Children { get; set; }
public BranchLevel_1(string name, List<BranchLevel_2> children)
{
this.Name = name;
//this line needs to be added before creating ObservableCollection
children.OrderBy(item_level2 => item_level2.Contents[1].Content).ToList();
this.Children = new ObservableCollection<BranchLevel_2>(children);
}
}

AutoMapper to map a child list object

I'm using an generic method to map two classes using Automapper
My generic methods
public class AutoMapperConfiguration
{
public MapperConfiguration Configure<TSource, TDestination>() where TSource:class where TDestination:class
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<ClientMappingProfile<TSource,TDestination>>();
});
return config;
}
}
ClientMappingProfile.cs
public class ClientMappingProfile<TSource,TDestination>: Profile where TSource : class where TDestination:class
{
public ClientMappingProfile()
{
CreateMap<TSource, TDestination>().ReverseMap();
}
}
StudentDetailsViewModel.cs
public class StudentDetailsViewModel
{
public long ID { get; set; }
public string FirstName { get; set; }
public List<QualificationViewModel> listQualificationViewModel { get; set; }
}
QualificationViewModel.cs
public class QualificationViewModel
{
public long ID { get; set; }
public long StudentID { get; set; }
public string ExaminationPassed { get; set; }
}
StudentValueObject.cs
public class StudentValueObject
{
public long ID { get; set; }
public string FirstName { get; set; }
public List<StudentQualificationValueObject> listStudentQualificationValueObject { get; set; }
}
StudentQualificationValueObject.cs
public class StudentQualificationValueObject
{
public long ID { get; set; }
public long StudentID { get; set; }
public string ExaminationPassed { get; set; }
}
Usage
StudentValueObject studentValueObject = new StudentValueObject();
var config = new AutoMapperConfiguration().Configure<StudentValueObject, StudentDetailsViewModel>();
var iMapper = config.CreateMapper();
studentValueObject = iMapper.Map<StudentDetailsViewModel, StudentValueObject>(objStudentModel);
So, this works fine with Mapping StudentDetailsViewModel.cs with StudentValueObject.cs. But it silently fails to copy my child list objects which is List<QualificationViewModel> to List<StudentQualificationValueObject>. The child list object always seems to be null. I'm pretty newbie to AutoMapper. I need some help as to know where am I going wrong or what need to be added/fixed to my generic method, so that the child list object gets copied to with Parent object.
Update -
Currently I'm doing it using below code and its working properly but I'm confused is this the proper way of doing this.
StudentValueObject studentValueObject = new StudentValueObject();
var config = new AutoMapperConfiguration().Configure<StudentValueObject, StudentDetailsViewModel>();
var iMapper = config.CreateMapper();
studentValueObject = iMapper.Map<StudentDetailsViewModel, StudentValueObject>(objStudentModel);
config = new AutoMapperConfiguration().Configure<StudentQualificationValueObject, QualificationViewModel>();
iMapper = config.CreateMapper();
studentValueObject.listStudentQualificationValueObject = iMapper.Map<List<QualificationViewModel>, List<StudentQualificationValueObject>>(objStudentModel.listQualificationViewModel);
You have to map the list properties, cause they have different names in the given parent types and you have to add a mapping for the types used within both lists. Here is an working example for your code:
public class StudentsMappingProfile : Profile
{
public StudentsMappingProfile()
{
CreateMap<StudentValueObject, StudentDetailsViewModel>()
.ForMember(viewModel => viewModel.listQualificationViewModel, conf => conf.MapFrom(value => value.listStudentQualificationValueObject));
CreateMap<StudentQualificationValueObject, QualificationViewModel>();
}
}
public class Program
{
public static void Main()
{
var config = new MapperConfiguration(cfg => cfg.AddProfile<StudentsMappingProfile>());
var mapper = config.CreateMapper();
var source = new StudentValueObject { ID = 73, FirstName = "Hello", listStudentQualificationValueObject = new List<StudentQualificationValueObject> { new StudentQualificationValueObject { ID = 42, StudentID = 17, ExaminationPassed = "World" } } };
var destination = mapper.Map<StudentDetailsViewModel>(source);
Console.ReadKey();
}
}

List Updating a value after Insert

i have a issue, I want to Update a value to element which is getting added in a list.
With generic example:-
I have a Model Object:-
public class Model
{
public int ModelProperty1 { get; set; }
public int ModelProperty2 { get; set; }
public int ModelPropertyStatus { get; set; }
}
I have a DTO Object:-
public class DTO
{
public int DTOProperty1 { get; set; }
public int DTOProperty2 { get; set; }
public int DTOPropertyStatus { get; set; }
}
Now, in my Controller i have a List which adds Model object:-
List<Model> _listOfModel = new List<Model>();
Secondly, i have created a mapping method which maps my Model & DTO
private Model MapDTOToModel(DTO dto)
{
return new Model
{
ModelProperty1 = dto.DTOProperty1,
ModelProperty2 = dto.DTOProperty2
};
}
Coming to my Issue:-
I want something like this to work:-
//I want a piece of code that Updates my ModelPropertyStatus after it gets inserted to //List
_listOfModel.Add(new Model() { ModelPropertyStatus = 1 });
//Here is the piece of code i want to convert:-
Model model = new Model();
model.ModelPropertyStatus = 1;
_listOfModel.Add(MapDTOToModel(model));
To be specific(Updated)
I want something like this:-
_listOfModel.Add(MapDTOToModel() { ModelPropertyStatus = 1 });
Any Suggestions??
You are looking for an ObservableCollection:
Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
Here's an example:
public class Model
{
public int ModelProperty1 { get; set; }
public int ModelProperty2 { get; set; }
public int ModelPropertyStatus { get; set; }
}
void Main()
{
ObservableCollection<Model> _listOfModel = new ObservableCollection<Model>();
_listOfModel.CollectionChanged += (s, o) =>
{
foreach (var m in o.NewItems)
((Model)m).ModelPropertyStatus = 1;
};
var model = new Model();
Console.WriteLine("Before add: " + model.ModelPropertyStatus.ToString());
_listOfModel.Add(model);
Console.WriteLine("After add: " + model.ModelPropertyStatus.ToString());
}
output:
Before add: 0
After add: 1
As you can see, using the CollectionChanged event, the property gets updatet during the insert.

Protobuf Inheritance and Generics

I am attempting to use ProtoBuf net to serialize an object tree with the classes in the following format:
[ProtoContract]
class MySpecialCollectionList<T> : List<MySpecialCollection<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class MySpecialCollection<T> : List<Special<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
interface IBeast
{
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
using (var fs = File.Create(#"c:\temp\protobuftest.bin"))
{
Serializer.Serialize(fs, collectionList);
fs.Close();
}
}
private MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(cat)},
{new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>() {
specialCollection1, specialCollection2 };
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}
Notice how the class I am serializing (MySpecialCollectionList<T>) is derived from a List<SomeOtherClass<T>>, not just List<T>.
I am struggling to work out where to put "ProtoInclude" attributes to get this to serialize all the items in the MySpecialCollectionList. Any help would be much appreciated.
Inheritance is not an issue here since even if A : B it is not true that Foo<A> : Foo<B>. Note that protobuf-net won't use a non-default constructor, although it is possible to skip the constructor, binding to the field directly (even readonly). While you may have 6 T, I can't see (from the code) that it would ever be in doubt which closed type you intend, and if the closed type is known you should be set.
If you have a Foo<SomeBaseClass> and a number of concrete types inherited from SomeBaseClass then the markers would o on SomeBaseClass.
However, if you have a concrete scenario I can use to reproduce your issue, I'll happily take a look.
Updated re edit:
There are a couple of key points drawn out in the example:
in common with most binding APIs, XmlSerializer and IIRC DataContractSerializer, an item is either a list xor an item with values; if a collection (something implementing IList) has properties itself, they will not be serialized; encapsulation is preferred over inheritance here, i.e. something that has a Name and has a list (rather than has a Name and is a list)
protobuf-net v1 does not support interface-based serialization; v2 does, but as with XmlSerializer and DataContractSerializer you need to explicitly tell it what things it needs to expect; quite nicely, though, we can move the [ProtoMember] onto the interface itself
Here's a fully working version in v2:
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
class MySpecialCollectionList<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<MySpecialCollection<T>> items = new List<MySpecialCollection<T>>();
[ProtoMember(2)]
public List<MySpecialCollection<T>> Items { get { return items; } }
}
[ProtoContract]
class MySpecialCollection<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<Special<T>> items = new List<Special<T>>();
[ProtoMember(2)]
public List<Special<T>> Items { get { return items; } }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
[ProtoContract]
[ProtoInclude(2, typeof(Ant))]
[ProtoInclude(3, typeof(Cat))]
[ProtoInclude(4, typeof(Dog))]
interface IBeast
{
[ProtoMember(1)]
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
public string Name { get; set; }
}
public static class Form1
{
private static void Main()
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
var copy = Serializer.DeepClone(collectionList);
}
private static MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {Items =
{new Special<IBeast>(ant),
new Special<IBeast>(cat),
new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>()
{
Items =
{new Special<IBeast>(ant),
new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>()
{
Items ={
specialCollection1, specialCollection2 }
};
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}

Categories