Serialize list of objects - c#

I need to serialize/deserialize some XML code and part of it looks like next example:
<CoordGeom>
<Curve rot="cw" chord="830.754618036885" crvType="arc" delta="72.796763873948" dirEnd="283.177582669379" dirStart="355.974346543327" external="169.661846548051" length="889.38025007632" midOrd="136.562611151675" radius="699.999999998612" tangent="516.053996536113">
<Start>4897794.2800513292 6491234.9390137056</Start>
<Center>4897096.0071489429 6491185.7968343571</Center>
<End>4897255.5861026254 6491867.3645547926</End>
<PI>4897758.0514541129 6491749.7197593488</PI>
</Curve>
<Spiral length="109.418078418008" radiusEnd="INF" radiusStart="699.999999999025" rot="cw" spiType="clothoid" theta="4.477995782709" totalY="2.849307921907" totalX="109.351261203955" tanLong="72.968738862921" tanShort="36.493923980983">
<Start>4897255.5861026254 6491867.3645547936</Start>
<PI>4897220.0531303799 6491875.6840722272</PI>
<End>4897147.9238984985 6491886.7208634559</End>
</Spiral>
<Spiral length="153.185309785019" radiusEnd="499.99999999993" radiusStart="INF" rot="ccw" spiType="clothoid" theta="8.776871734087" totalY="7.808812331497" totalX="152.826239431476" tanLong="102.249348442205" tanShort="51.176160975293">
<Start>4897147.9238985004 6491886.7208634559</Start>
<PI>4897046.8509311257 6491902.186455016</PI>
<End>4896998.0370401107 6491917.5553683294</End>
</Spiral>
<Curve rot="ccw" chord="936.510896488672" crvType="arc" delta="138.94725576785" dirEnd="66.423714388543" dirStart="287.476458620693" external="925.970149937768" length="1212.543549877849" midOrd="324.680762068264" radius="499.999999999181" tangent="1335.436583485725">
<Start>4896998.0370401107 6491917.5553683294</Start>
<Center>4897148.1939981515 6492394.4755796343</Center>
<End>4896948.2091376046 6492852.7397562303</End>
<PI>4895724.243644949 6492318.6055583945</PI>
</Curve>
</CoordGeom>
I've generated automatically classes using xsd.exe. Part of generated code looks like this:
public partial class CoordGeom
{
private List<object> _items;
private List<Feature> _feature;
private string _desc;
private string _name;
private stateType _state;
private string _oID;
public CoordGeom()
{
_feature = new List<Feature>();
_items = new List<object>();
}
[XmlElementAttribute("Chain", typeof(Chain))]
[XmlElementAttribute("Curve", typeof(Curve))]
[XmlElementAttribute("IrregularLine", typeof(IrregularLine))]
[XmlElementAttribute("Line", typeof(Line))]
[XmlElementAttribute("Spiral", typeof(Spiral))]
public List<object> Items
{
get { return this._items; }
set { this._items = value; }
}
[XmlElement("Feature")]
public List<Feature> Feature { get; set; }
[XmlAttribute()]
public string desc { get; set; }
[XmlAttribute()]
public string name { get; set; }
[XmlAttribute()]
public stateType state { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string oID
{
get{ return this._oID; }
set{ this._oID = value; }
}
}
And my code for deserialization look like this:
XmlSerializer mySerializer = new XmlSerializer(typeof(LandXML), new XmlRootAttribute(""));
TextReader myFileStream = new StreamReader("myFile.xml");
LandXML myObject = (LandXML)mySerializer.Deserialize(myFileStream);
var coordGeomItems = myObject.Alignments.Alignment[0].CoordGeom;
My problem is that, when I deserialize file, it is deserialized as list of items of type {LandXML.Curve}, {LandXML.Spiral} etc. and I don't know how to access their properties. It would be great if I can do this directly. Here is a screenshot:
EDIT 1
Here is inital screen
then I have items:
When I unfold this
And this is at the top layer of object - it has some InnerXml, InnerText... If I want to achieve CoordGeom, there is a lot object.Item(i).ChildNodes.Item(j).ChildNodes...
And all of that is because in some lines, lists of objects are made like List as for CoordGeom

Because there are multiple allowed types, the Items collection is typed as object. The simplest approach is to enumerate and cast each item:
foreach(var item in coordGeomItems.Items)
{
var curve = item as Curve;
if (curve != null)
{
// access curve properties here
}
var spiral = item as Spiral
if (spiral != null)
{
// access spiral properties here
}
// ...
}

You could build up a list of Curves and Spirals and access them using properties with custom getters:
class CoordGeom
{
public List<object> Items;
List<Curve> _curves;
public List<Curve> Curves
{
get
{
return _curves ?? (_curves = Items
.Where(item => item is Curve).Select(curve => (Curve)curve).ToList());
}
}
}
The null coalescing operator (??) will cause the Curves property to set and return the value of _curves as a list of curves if _curves is null. This basically causes it to initialize the list on the first get and on all subsequent gets it will return the already initialized list.

As you cannot change the generated class nor the XML.The best possible approach would be to write an extension method.
public static List<Curve> GetCurves(this CoordGeom cg)
{
return cg.Items.OfType<Curve>().ToList();
}
public static List<Spiral> GetSpirals(this CoordGeom cg)
{
return cg.Items.OfType<Spiral>().ToList();
}
Once you do this, you can get items like this
var coordGeomItems = myObject.Alignments.Alignment[0].CoordGeom;
var curves = coordGeomItems.GetCurves();
var spirals = coordGeomItems.GetSpirals();

Related

Create Intersect of HashSet and remove Items from class structure with extracted ID's

lets assume I have the following classes:
public class ServiceStatistics
{
public string LocalId { get; set; }
public string OrganizationId { get; set; }
public List<StatisticElements> Elements { get; } = new List<StatisticElements>();
}
public class StatisticElements
{
public string StatisticId { get; set; }
public string Type { get; set; }
public string ServiceName { get; set; }
}
I retrieve such ServiceStatistics by a soap service and I use serialization/deserialization.
Each ServiceStatistics contains a set of StatisticElements. I also have a static list of StatisticElements-ID's which are relevant for calculation. All other incoming StatisticElements-ID's can be dropped. I need to do this on my side
because the SOAP Service does not support selecting specific StatisticElements-ID's
So I have generated a static Class with a HashSet:
public static class RelevantDutyPlans
{
private static HashSet<int> relevantDutyPlans;
static RelevantDutyPlans()
{
// only a subset of the original ID's
relevantDutyPlans = new HashSet<int>()
{
530,
1150,
1095,
};
}
public static HashSet<int> GetRelevantDutyPlans()
{
return relevantDutyPlans;
}
public static bool Contains(int planId)
{
return relevantDutyPlans.Contains(planId);
}
// Extracts all DutyPlans which are relevant (HashSet) for validation from
// the incoming data
public static List<int> ExtractRelevantDutyPlans(List<int> planIds)
{
var relevantPlans = new HashSet<int>(planIds);
relevantPlans.IntersectWith(relevantDutyPlans);
return relevantDutyPlans.ToList();
}
}
So my thought was, to create an Intersect like this:
List<ServiceStatistics> statistics = SoapService.GetStatistics(Now);
List<int> incomingIds = new List<int>();
foreach(var item in statistics)
{
foreach(var element in item.Statistic)
{
incomingIds.Add(int.Parse(element.StatisticId));
}
}
List<int> extract = RelevantDutyPlans.ExtractRelevantDutyPlans(incomingIds);
So now I have a List of ID's which are relevant for further processing. What I want to achieve is to remove all class elements "StatisticElements" with "StatisticId" not contained in the the extract list generated above.
Any ideas?
Any help is very appreciated
How about a little bit different approach. Simply remove irrelevant plans right away!
List<ServiceStatistics> statistics = SoapService.GetStatistics(Now);
foreach(var item in statistics)
{
item.Elements.RemoveAll(x => !RelevantDutyPlans.Contains(int.Parse(x.StatisticId)));
}
Now you are only left with the relevant once.
Hope you can use selectMany to flatten the collection and proceed the filter.
var filteredItems = statistics.SelectMany(s => s.Elements)
.Where(s => extract.Contains(Convert.ToInt32(s.StatisticId)))
.ToList();
You could also use LINQ to create a new List<> if you need to keep the original statistcs intact - e.g. if you might run multiple plans against it.
var relevantStatistics = statistics.Select(s => new {
LocalId = s.LocalId,
OrganizationId = s.OrganizationId,
Elements = s.Elements.Where(e => !RelevantDutyPlans.Contains(Convert.ToInt32(e.StatisticId))).ToList()
});
Since ServiceStatistics doesn't provide for construction, I return an anonymous object instead, but you could create an appropriate DTO class.

Sorting CollectionViewSource grouping by external dictionary

I've got some component objects in a CollectionViewSource i need to sort, these objects all have a custom type. The grouping is done on the type and the components are sorted by their name. What I now need to do is sort the grouping on the component type But i need to sort these component types depending on an external source, So the objects looks a bit like this:
public class ComponentType
{
public Guid Identification
{
get;
}
}
public class Component
{
public string Name
{
get;
}
public ComponentType Type
{
get;
}
}
The collection view is created like so:
this.ComponentCollection = new CollectionViewSource();
this.ComponentCollection.Source = this.Components;
this.ComponentCollection.GroupDescriptions.Clear();
this.ComponentCollection.GroupDescriptions.Add(new PropertyGroupDescription("ComponentType"));
this.ComponentCollection.SortDescriptions.Clear();
this.ComponentCollection.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
this.ComponentCollection.Filter += this.FilterComponent;
this.ComponentCollection.View.Refresh();
RaisePropertyChanged(() => this.ComponentCollection);
I also have the following dictionary in the same calss i'm creating the CollectionViewSource which looks like this:
public Dictionary<Guid, int> ComponentTypePositions
Where the key is the identificaiton of the component type and int is the position of which type should come first.
It is not possible to put the position as a property in the ComponentType or Component class, it needs to be a separate list.
How do i sort the grouping according to the corresponding number in the ComponentTypePositions dictionary?
You need to create a new type based on Component class which includes your position numbers and use this for the elements of your source list. This can be done inline with an anonymous type:
ComponentCollection = new CollectionViewSource();
ComponentCollection.Source = (from c in Components
select new
{
Name = c.Name,
Type = c.Type,
Pos = ComponentTypePositions[c.Type.Identification]
}).ToList();
ComponentCollection.GroupDescriptions.Clear();
ComponentCollection.GroupDescriptions.Add(new PropertyGroupDescription("Type"));
ComponentCollection.SortDescriptions.Clear();
ComponentCollection.SortDescriptions.Add(new SortDescription("Pos", ListSortDirection.Ascending));
ComponentCollection.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
ComponentCollection.Filter += FilterComponent;
ComponentCollection.View.Refresh();
If your source list need to be editable, you can use your own custom list type for it:
public class ComponentListElement
{
private Component comp;
public ComponentListElement(Component comp, Dictionary<Guid, int> positionMap)
{
this.comp = comp;
this.Pos = positionMap[comp.Type.Identification];
}
public string Name { get { return comp.Name; } }
public ComponentType Type { get { return comp.Type; } }
public int Pos { get; private set; }
}
public class ComponentList : Collection<ComponentListElement>
{
private Dictionary<Guid, int> positionMap;
public ComponentList(Dictionary<Guid, int> positionMap)
{
this.positionMap = positionMap;
}
public void Add(Component item)
{
base.Add(new ComponentListElement(item, positionMap));
}
}
And use it like this:
ComponentList componentList = new ComponentList(ComponentTypePositions);
foreach (var item in Components)
{
componentList.Add(item);
}
ComponentCollection.Source = componentList;

multi return type in c# methods

I have a (string, object) dictionary, object (class) has some values including data type which is defined by enum. I need a GetItemValue method that should return dictionary item's value. So return type must be the type which is defined in item object.
Class Item
{
String Name;
DataValueType DataType;
Object DataValue;
}
private Dictionary<string, Item> ItemList = new Dictionary<string, Item>();
void Main()
{
int value;
ItemList.Add("IntItem", new Item("IntItem", DataValueType.TInt, 123));
value = GetItemValue("IntItem"); // value = 123
}
What kind of solution can overcome this problem?
Best Regards,
You can use Generic Classes
Class Item<T>
{
String Name;
T DataTypeObject;
Object DataValue;
public T GetItemValue()
{
//Your code
return DataTypeObject;
}
}
A better solution would be to introduce an interface that you make all the classes implement. Note that the interface doesn't necessarily have to specify any behavior:
public interface ICanBePutInTheSpecialDictionary {
}
public class ItemTypeA : ICanBePutInTheSpecialDictionary {
// code for the first type
}
public class ItemTypeB : ICanBePutInTheSpecialDictionary {
// code for the second type
}
// etc for all the types you want to put in the dictionary
To put stuff in the dictionary:
var dict = new Dictionary<string, ICanBePutInTheSpecialDictionary>();
dict.add("typeA", new ItemTypeA());
dict.add("typeB", new ItemTypeB());
When you need to cast the objects to their specific types, you can either use an if-elseif-block, something like
var obj = dict["typeA"];
if (obj is ItemTypeA) {
var a = obj as ItemTypeA;
// Do stuff with an ItemTypeA.
// You probably want to call a separate method for this.
} elseif (obj is ItemTypeB) {
// do stuff with an ItemTypeB
}
or use reflection. Depending on how many choices you have, either might be preferrable.
If you have a 'mixed bag' you could do something like this...
class Item<T>
{
public String Name { get; set; }
public DataValueType DataType { get; set; }
public T DataValue { get; set; }
}
class ItemRepository
{
private Dictionary<string, object> ItemList = new Dictionary<string, object>();
public void Add<T>(Item<T> item) { ItemList[item.Name] = item; }
public T GetItemValue<T>(string key)
{
var item = ItemList[key] as Item<T>;
return item != null ? item.DataValue : default(T);
}
}
and use it like...
var repository = new ItemRepository();
int value;
repository.Add(new Item<int> { Name = "IntItem", DataType = DataValueType.TInt, DataValue = 123 });
value = repository.GetItemValue<int>("IntItem");
If you have just a couple types - you're better off with Repository<T>.
I found a solution exactly what I want. Thanks to uncle Google.
Thanks all of you for your kind interest.
public dynamic GetValue(string name)
{
if (OpcDataList[name].IsChanged)
{
OpcReflectItem tmpItem = OpcDataList[name];
tmpItem.IsChanged = false;
OpcDataList[name] = tmpItem;
}
return Convert.ChangeType(OpcDataList[name].ItemValue.Value, OpcDataList[name].DataType);
}

Reflection - object comparison & default values

I'm trying to compare two complex objects in C#, and produce a Dictionary containing the differences between the two.
If I have a class like so:
public class Product
{
public int Id {get; set;}
public bool IsWhatever {get; set;}
public string Something {get; set;}
public int SomeOtherId {get; set;}
}
And one instance, thus:
var p = new Product
{
Id = 1,
IsWhatever = false,
Something = "Pony",
SomeOtherId = 5
};
and another:
var newP = new Product
{
Id = 1,
IsWhatever = true
};
To get the differences between these, i'm doing stuff that includes this:
var oldProps = p.GetType().GetProperties();
var newProps = newP.GetType().GetProperties();
// snip
foreach(var newInfo in newProps)
{
var oldVal = oldInfo.GetValue(oldVersion, null);
var newVal = newInfo.GetValue(newVersion,null);
}
// snip - some ifs & thens & other stuff
and it's this line that's of interest
var newVal = newInfo.GetValue(newVersion,null);
Using the example objects above, this line would give me a default value of 0 for SomeOtherId (same story for bools & DateTimes & whathaveyou).
What i'm looking for is a way to have newProps include only the properties that are explicitly specified in the object, so in the above example, Id and IsWhatever. I've played about with BindingFlags to no avail.
Is this possible? Is there a cleaner/better way to do it, or a tool that's out there to save me the trouble?
Thanks.
There is no flag to tell if you a property was explicitly set. What you could do is declare your properties as nullable types and compare value to null.
If i understand you correctly, this is what microsoft did with the xml wrapping classes, generated with the xsd utility, where you had a XIsSpecified, or something like that, for each property X.
So this is what You can do as well - instead of public int ID{get;set;}, add a private member _id , or whatever you choose to call it, and a boolean property IDSpecified which will be set to true whenever Id's setter is called
I ended up fixing the issue without using reflection (or, not using it in this way at least).
It goes, more or less, like this:
public class Comparable
{
private IDictionary<string, object> _cache;
public Comparable()
{
_cache = new Dictionary<string, object>();
}
public IDictionary<string, object> Cache { get { return _cache; } }
protected void Add(string name, object val)
{
_cache.Add(name, val);
}
}
And the product implementation goes to this:
public class Product : Comparable
{
private int _id;
private bool _isWhatever;
private string _something;
private int _someOtherId;
public int Id {get { return _id; } set{ _id = value; Add("Id", value); } }
public bool IsWhatever { get { return _isWhatever; } set{ _isWhatever = value; Add("IsWhatever ", value); } }
public string Something {get { return _something; } set{ _something = value; Add("Something ", value); } }
public int SomeOtherId {get { return _someOtherId; } set{ _someOtherId = value; Add("SomeOtherId", value); } }
}
And the comparison is then pretty straightforward
var dic = new Dictionary<string, object>();
foreach(var obj in version1.Cache)
{
foreach(var newObj in version2.Cache)
{
//snip -- do stuff to check equality
dic.Add(....);
}
}
Doesn't hugely dirty the model, and works nicely.

Replace a collection item using Linq

How do I find and replace a property using Linq in this specific scenario below:
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
//TODO: Just copying values... Find out how to find the index and replace the value
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
Thanks for helping out in advance.
Do not use LINQ because it will not improve the code because LINQ is designed to query collection and not to modify them. I suggest the following.
// Just realized that Array.IndexOf() is a static method unlike
// List.IndexOf() that is an instance method.
Int32 index = Array.IndexOf(this.Properties, name);
if (index != -1)
{
this.Properties[index] = value;
}
else
{
throw new ArgumentOutOfRangeException();
}
Why are Array.Sort() and Array.IndexOf() methods static?
Further I suggest not to use an array. Consider using IDictionary<String, Property>. This simplifies the code to the following.
this.Properties[name] = value;
Note that neither solution is thread safe.
An ad hoc LINQ solution - you see, you should not use it because the whole array will be replaced with a new one.
this.Properties = Enumerable.Union(
this.Properties.Where(p => p.Name != name),
Enumerable.Repeat(value, 1)).
ToArray();
[note: this answer was due to a misunderstanding of the question - see the comments on this answer. Apparently, I'm a little dense :(]
Is your 'Property' a class or a struct?
This test passes for me:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
[TestMethod]
public void TestMethod1()
{
var pb = new PropertyBag() { Properties = new Property[] { new Property { Name = "X", Value = "Y" } } };
Assert.AreEqual("Y", pb["X"].Value);
pb["X"] = new Property { Name = "X", Value = "Z" };
Assert.AreEqual("Z", pb["X"].Value);
}
I have to wonder why the getter returns a 'Property' instead of whatever datatype .Value, but I'm still curious why you're seeing a different result than what I am.

Categories