I have a class that has this code,
pCollection pub = RSSXmlDeserializer.GetPub(path, fReload);
get pub is the method that returns a collections of pub's...
How can i iterate them. I tried,
for (var n = 0; n < pub.Count; n++ ){
}
this is the getPub method
public static PCollection GetPub(string path, bool fReload)
{
HttpApplicationState session = HttpContext.Current.Application;
PCollection pub = session["PUB"] as PCollection;
if pub == null || fReload)
{
StreamReader reader = null;
try
{
XmlSerializer serializer = new XmlSerializer(typeof(PCollection));
reader = new StreamReader(path);
pub = (PCollection)serializer.Deserialize(reader);
session["PUB"] = pub;
}
catch (Exception ex)
{
//throw ex;
}
finally
{
reader.Close();
}
}
return pub;
}
}
[Serializable()]
public class Pub
{
[System.Xml.Serialization.XmlElement("title")]
public string Title { get; set; }
[System.Xml.Serialization.XmlElement("description")]
public string Description { get; set; }
[System.Xml.Serialization.XmlElement("imageUrl")]
public string ImageUrl { get; set; }
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("RPublications")]
public class PCollection
{
[XmlArray("Pub")]
[XmlArrayItem("Pub", typeof(Pub))]
public Pub[] Pub { get; set; }
}
but 'Count' is not recognised. I get this message , pCollection does not have a definition for 'Count'...
How do i iterate the collection n get the collection elements? pls help.
You could use:
foreach(var p in pub.Pub)
{
// Do work on p
}
Note that your PCollection class is not following good .NET practices, as it's named "Collection" but not implementing any of the standard interfaces for collections. You may want to consider reworking this to be more "standardized".
PCollection is not really a collection. It is a class that contains a collection (more precisely an array). So to iterate you need to iterate the array:
for (Int32 i = 0; i < pub.Pub.Length; ++i) {
Pub p = pub.Pub[i];
...
}
Or if you don't care about the index and just want to go through the collection from start to finish:
foreach (Pub p in pub.Pub) {
...
}
(A more consistent naming of types and members would probably help.)
Related
When I make updates inside a property of SortedList type of a document (haven't checked other Dictionary / List types) Those updates are ignored by the serialization process (even by the ToString method of the object).
Example:
using Microsoft.Azure.Documents;
public class BusinessStats : Document
{
public BusinessStats()
{
}
public BusinessStats(int businessId)
{
this.Id = businessId.ToString();
Counts = new SortedList<int, int>();
}
/// <summary>
/// Business id
/// </summary>
public override string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
[JsonProperty()]
public SortedList<int, int> Counts { get; set; }
}
public static class Test
{
BusinessStats bizStats = DocumentDB.Client.CreateDocumentQuery<BusinessStats>(DocumentDB.BusinessStats.SelfLink).Where(s => s.Id == p.BusinessId.ToString()).ToArray().FirstOrDefault();
if (bizStats == null)
{
bizStats = new BusinessStats(p.BusinessId);
}
}
if ( ! bizStats.Counts.ContainsKey(1))
{
bizStats.Counts.Add(1, 10);
}
else
{
bizStats.Counts[1] = bizStats.Counts[1] + 1;
}
var updateOperation = await DocumentDB.Client.UpsertDocumentAsync(DocumentDB.BusinessStats.DocumentsLink, bizStats);
First time this runs and inserts a proper Json, second time it reads the proper json, and deserialization goes well.
then, after it updates Counts[1] to be 11, it doesn't effect the ToString() and serialization of bizStats.
DocumentDB's .NET SDK makes me sad
I have a custom object with varying datatypes for each property.
I would like to be able to do something like:
public void evalCI(configurationItem CI)
{
foreach (PropertyInformation n in CI)
{
Response.Write(n.Name.ToString() + ": " + n.Value.ToString() + "</br>");
}
}
My custom object is:
public class configurationItem : IEnumerable
{
private string serial;
private string model;
private DateTime? wstart;
private DateTime? wend;
private Int32 daysLeft;
private string platform;
private string productVersion;
private string manufacturer;
private bool verificationFlag;
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
public string Serial
{
set { serial = value; }
get { return serial; }
}
public string Model
{
set { model = value; }
get { return model; }
}
public DateTime? Wstart
{
set { wstart = value; }
get { return wstart; }
}
public DateTime? Wend
{
set { wend = value; }
get { return wend; }
}
public Int32 DaysLeft
{
set { daysLeft = value; }
get { return daysLeft; }
}
public string Platform
{
set { platform = value; }
get { return platform; }
}
public string ProductVersion
{
set { productVersion = value; }
get { return productVersion; }
}
public string Manufacturer
{
set { manufacturer = value; }
get { return manufacturer; }
}
public bool VerificationFlag
{
set { verificationFlag = value; }
get { return verificationFlag; }
}
My expected output would be:
-Serial: 1234567
-Model: Mustang
-Wstart: 12/12/2005
-Wend: 12/11/2006
-DaysLeft: 0
-Platform: Car
-ProductVersion: GT
-Manufacturer: Ford
-VerificationFlag: true
At first I was getting an error that GetEnumerator() had to be implemented to use a foreach loop. The problem I keep running into is that all of the examples of Indexed Properties are of a single property with an indexable list, instead of an index for each property in the object. I was able to get intellisense to give me methods for PropertyInfo by adding:
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
However, the 2nd GetEnumerator() throws:
Compiler Error Message: CS0103: The name 'GetEnumerator' does not exist in the current context.
What am I missing here? How do I modify my object to give me the results I expect from evalCI()?
You don't need to implement IEnumerable. What you do need to do is use Reflection.
This is from memory, but I believe it would look like this:
foreach (PropertyInfo n in typeof(configurationItem).GetProperties())
{
Response.Write(string.Format("{0}: {1}<br/>", n.Name, n.GetValue(CI, null)));
}
This - the code as written - will also only give you public properties, and non-indexed properties (but it doesn't look like you have any indexed properties).
I have a Sharp Architecture based app using Fluent NHibernate with Automapping. I have the following Enum:
public enum Topics
{
AdditionSubtraction = 1,
MultiplicationDivision = 2,
DecimalsFractions = 3
}
and the following Class:
public class Strategy : BaseEntity
{
public virtual string Name { get; set; }
public virtual Topics Topic { get; set; }
public virtual IList Items { get; set; }
}
If I create an instance of the class thusly:
Strategy s = new Strategy { Name = "Test", Topic = Topics.AdditionSubtraction };
it Saves correctly (thanks to this mapping convention:
public class EnumConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance)
{
instance.CustomType(instance.Property.PropertyType);
}
public void Accept(FluentNHibernate.Conventions.AcceptanceCriteria.IAcceptanceCriteria criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum);
}
}
However, upon retrieval (when SQLite is my db) I get an error regarding an attempt to convert Int64 to Topics.
This works fine in SQL Server.
Any ideas for a workaround?
Thanks.
Actually, it is possible to map enums to INT, but in SQLite, INT will come back as an Int64, and, since you can't specify that your enum is castable to long, you will get this error. I am using NHibernate, so my workaround was to create a custom AliasToBean class that handles converting the enum fields to Int32:
public class AliasToBeanWithEnums<T> : IResultTransformer where T : new()
{
#region IResultTransformer Members
public IList TransformList(IList collection)
{
return collection;
}
public object TransformTuple(object[] tuple, string[] aliases)
{
var t = new T();
Type type = typeof (T);
for (int i = 0; i < aliases.Length; i++)
{
string alias = aliases[i];
PropertyInfo prop = type.GetProperty(alias);
if (prop.PropertyType.IsEnum && tuple[i] is Int64)
{
prop.SetValue(t, Convert.ToInt32(tuple[i]), null);
continue;
}
prop.SetValue(t, tuple[i], null);
}
return t;
}
#endregion
}
Usage:
public IList<ItemDto> GetItemSummaries()
{
ISession session = NHibernateSession.Current;
IQuery query = session.GetNamedQuery("GetItemSummaries")
.SetResultTransformer(new AliasToBeanWithEnums<ItemDto>());
return query.List<ItemDto>();
}
By default, emums are automapped to strings for SQLite (don't know what happens with SQL Server).
Less efficient storage wise, obviously, but that might be a non-issue unless you have really huge data sets.
Before .NET 3.5 was released, I use
Dictionary<TKey, List<TValue>>
for containing data. But I just found that .NET 3.5 provides new collection type that is ILookup class that can represent my old complex data type.
I always create ILookup object by using LINQ extension method (ToLookup method). But I do not know how to modify ILookup object.
Is it possible? Or I need to create by using union method and call ToLookup method again.
Thanks,
You don't, it's immutable. You have listed both of the reasonable options; either to use a dictionary of sub-collections or to keep creating new lookups.
Here is an example of an implementation of ILookup that can be manipulated. It wraps around a Dictionary of List's of elements. It is completely generic. I couldn't think of a better name. :)
public class LookupDictionary<TKey, TElement> : ILookup<TKey, TElement>
{
private Dictionary<TKey, List<TElement>> _dicLookup = new Dictionary<TKey, List<TElement>>();
public LookupDictionary()
{
}
public LookupDictionary(ILookup<TKey, TElement> a_lookup)
{
foreach (var grouping in a_lookup)
{
foreach (var element in grouping)
AddElement(grouping.Key, element);
}
}
public IEnumerable<TElement> AllElements
{
get
{
return (from key in _dicLookup.Keys
select _dicLookup[key])
.SelectMany(list => list);
}
}
public int Count
{
get
{
return AllElements.Count();
}
}
public IEnumerable<TElement> this[TKey a_key]
{
get
{
List<TElement> list;
if (_dicLookup.TryGetValue(a_key, out list))
return list;
return new TElement[0];
}
}
public bool Contains(TKey a_key)
{
return _dicLookup.ContainsKey(a_key);
}
public void Add(TKey a_key, TElement a_element)
{
AddElement(a_key, a_element);
}
public void RemoveKey(TKey a_key)
{
_dicLookup.Remove(a_key);
}
public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator()
{
return GetGroupings().GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return (GetGroupings() as System.Collections.IEnumerable).GetEnumerator();
}
private void AddElement(TKey a_key, TElement a_element)
{
List<TElement> list;
if (!_dicLookup.TryGetValue(a_key, out list))
{
list = new List<TElement>();
_dicLookup.Add(a_key, list);
}
list.Add(a_element);
}
private IEnumerable<IGrouping<TKey, TElement>> GetGroupings()
{
return from key in _dicLookup.Keys
select new LookupDictionaryGrouping<TKey, TElement>
{
Key = key,
Elements = _dicLookup[key]
} as IGrouping<TKey, TElement>;
}
}
public class LookupDictionaryGrouping<TKey, TElement> : IGrouping<TKey, TElement>
{
public TKey Key
{
get;
set;
}
public IEnumerable<TElement> Elements
{
get;
set;
}
public IEnumerator<TElement> GetEnumerator()
{
return Elements.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return (Elements as System.Collections.IEnumerable).GetEnumerator();
}
}
As mquander mentioned, the lookup is immutable. However, you can build a new lookup with additional or removed values.
// Add a new value
myLookup = myLookup
.SelectMany(l => l.Select(v => new {l.Key, Value = v}))
.Union(new[] {new {Key = myNewKey, Value = myNewValue}})
.ToLookup(a => a.Key, a => a.Value);
// Remove an old value
myLookup = myLookup
.SelectMany(l => l.Select(v => new {l.Key, Value = v}))
.Where(a => a.Value != myOldValue)
.ToLookup(a => a.Key, a => a.Value);
I want to deserialize xml like this:
<Product>
<Classification>
<NewClassification>1</NewClassification>
<NewClassification>2</NewClassification>
<NewClassification>3</NewClassification>
<OldClassification>A</OldClassification>
<OldClassification>B</OldClassification>
</Classification>
</Product>
My classes are:
public class Product
{
public Classification Classification { get; set; }
}
public class Classification
{
[XmlArrayItem(typeof(int), ElementName = "NewClassification")]
public List<int> NewClassificationList { get; set; }
[XmlArrayItem(typeof(int), ElementName = "OldClassification")]
public List<int> OldClassificationList { get; set; }
}
Why this code is wrong?
Apart from the "int literal" issue commented on by "Bears will eat you" above, you can't serialize your class in the way you want with what you've got (at least I can't think of a way to do it)
without resorting to custom serialisation (e.g. implementing IXmlSerializable or some other method).
Your basic problem is that you have two seperate collections in Classification, which you want to appear as a single collection.
The IXmlSerialization route is pretty simple though. Just implement it as follows on your Classification class, and you'll get the desired result.
public class Classification : IXmlSerializable
{
public List<int> NewClassificationList { get; set; }
public List<string> OldClassificationList { get; set; }
public void WriteXml(System.Xml.XmlWriter writer)
{
foreach (var item in NewClassificationList)
{
writer.WriteElementString("NewClassification", item.ToString());
}
foreach (var item in OldClassificationList)
{
writer.WriteElementString("OldClassification", item.ToString());
}
}
public System.Xml.Schema.XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(System.Xml.XmlReader reader)
{
throw new NotImplementedException();
}
}
Added ReadXml implementation
ReadXml can be implemented as follows:
public void ReadXml(System.Xml.XmlReader reader)
{
var product = new Product();
product.Classification = new Classification { NewClassificationList = new List<int>(), ldClassificationList = new List<string>()};
reader.ReadStartElement("Classification");
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
switch (reader.Name)
{
case "NewClassification":
product.Classification.NewClassificationList.Add(reader.ReadElementContentAsInt());
break;
case "OldClassification":
product.Classification.OldClassificationList.Add(reader.ReadElementContentAsString());
break;
default:
throw new NotSupportedException("Unsupported node: " + reader.Name);
}
}
reader.ReadEndElement();
}