Why this code wont work? XML with .StartsWith - c#

Why this work like this:
XDocument dataFeed = XDocument.Parse(e.Result);
var guide = from query in dataFeed.Descendants("MaxPayne3")
select new NewGamesClass
{
GameID = (string)query.Element("ID"),
GameTitle = (string)query.Element("Title"),
GameDescription = (string)query.Element("Description"),
GameGuide = (string)query.Element("Guide")
};
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
if (selectedIndex == "0")
GuidesListBox.ItemsSource = guide.Where(ngc => ngc.GameTitle.StartsWith("Feel"));
else if (selectedIndex == "1")
GuidesListBox.ItemsSource = guide.Where(ngc => ngc.GameTitle.StartsWith("Serious"));
But not like this:
if (selectedIndex == "0")
GuidesListBox.ItemsSource = guide.Where(ngc => ngc.GameID.StartsWith("000"));
else if (selectedIndex == "1")
GuidesListBox.ItemsSource = guide.Where(ngc => ngc.GameID.StartsWith("001"));
i want to use the GameID Instead The GameTitle.
Sorry about no XML, too early in the morning for me, lol
Here:
https://dl.dropbox.com/u/27136243/AchivementHunters/XML/GameList.xml
Here is the Class:
public class NewGamesClass
{
string gameID;
string gameTitle;
string gamedescription;
string gameImage;
string gameGuide;
string videoLink;
public string GameID
{ get { return gameID; } set { gameID = value; } }
public string GameTitle
{ get { return gameTitle; } set { gameTitle = value; } }
public string GameDescription
{ get { return gamedescription; } set { gamedescription = value; } }
public string GameImage
{ get { return gameImage; } set { gameImage = value; } }
public string GameGuide
{ get { return gameGuide; } set { gameGuide = value; } }
public string VideoLink
{ get { return videoLink; } set { videoLink = value; } }
}

Most of the <MaxPayne3> nodes do not have a child <ID> node, hence this code:
select new NewGamesClass
{
GameID = (string)query.Element("ID"),
GameTitle = (string)query.Element("Title"),
GameDescription = (string)query.Element("Description"),
GameGuide = (string)query.Element("Guide")
}
will produce mostly nulls for GameID. Then this code:
guide.Where(ngc => ngc.GameID.StartsWith("000"))
will throw NullReferenceException for all those elements
change it to this:
guide.Where(ngc => !String.IsNullOrEmpty(ngc.GameID) && ngc.GameID.StartsWith("000"))
and it should work fine.

Given that we have no data, the only significant difference here is the .GameTitle. vs .GameID.. I'm assuming that "won't work" here means "it throws an exception". The primary exception I can predict there would be NullReferenceException, because .GameID is null, so .GameID.anything is illegal.
Which makes me think that <ID>whatever</ID> doesn't exist at the location you think it does (which will result in .GameID being a null reference). Things to verify:
that it exists there
that the case is correct (ID, Id, id, etc)
that is is an element, not an attribute
that it isn't in a namespace
Update: and here's the problem from the data:
<MaxPayne3>
<Title>Part I Complete - 20G</Title>
<Description>Complete Part I Of The Story</Description>
<Guide>Story related, cannot be missed.</Guide>
<Image>http://www.xbox360achievements.org/images/achievements/925/-K6lMA==.jpg</Image>
<VideoLink/>
</MaxPayne3>
which has no <ID>. There are a lot more like this. So: you need to handle that. A pragmatic check would be:
...blah...guide.Where(ngc => ngc.GameID != null && ngc.GameID.StartsWith("000"));

Related

How to deserialize an array containing a list?

I would like to deserialize an array containing some things and 3 List.
The program works fine except for List. The lists are created but they contain nothing !
Can you help me ?
Here is how the xml file looks like :
<blind>
<folder>C:\Users\Michael\Desktop\BT 1 normal\Programme BT\</folder>
<nombre_titres>25</nombre_titres>
<numero></numero>
<theme></theme>
<heure_debut></heure_debut>
<mdp>a</mdp>
<lien></lien>
<playlist>
<extrait>
<artiste>Abba</artiste>
<titre>Take a chance on me</titre>
<PointAT>1.25</PointAT>
<PointA>0.5</PointA>
<PointT>0.5</PointT>
<JoueursAT>
<joueurAT>Ahkayaqua</joueurAT>
<joueurAT>Angelene</joueurAT>
</JoueursAT>
<JoueursA>
<joueurA></joueurA>
</JoueursA>
<JoueursT>
<joueurT></joueurT>
</JoueursT>
</extrait>
<extrait>
....
</extrait>
</playlist>
</blind>
My code to deserialize :
XElement xmle;
xmle = XElement.Load(_folder + "Blind.xml");
textBox1.Text = xmle.Element("numero").Value;
textBox4.Text = xmle.Element("theme").Value;
textBox3.Text = xmle.Element("heure_debut").Value;
textBox5.Text = xmle.Element("lien").Value;
textBox2.Text = xmle.Element("mdp").Value;
extraits = (from ex in xmle.Element("playlist").Elements("extrait")
select new Extrait
(ex.Element("artiste").Value,
ex.Element("titre").Value,
0,
0,
0,
(from jat in ex.Element("JoueursAT").Elements("JoueurAT")
select jat.Element("JoueurAT").Value).ToList(),
(from ja in ex.Element("JoueursA").Elements("JoueurA")
select ja.Element("JoueurA").Value).ToList(),
(from jt in ex.Element("JoueursT").Elements("JoueurT")
select jt.Element("JoueurT").Value).ToList())).ToArray();
And here is my class:
public class Extrait
{
private String _Artiste;
private String _Titre;
private double _PointA;
private double _PointT;
private double _PointAT;
private List<String> _JoueurA;
private List<String> _JoueurT;
private List<String> _JoueurAT;
public String Artiste
{
get { return _Artiste; }
set { _Artiste = value; }
}
public String Titre
{
get { return _Titre; }
set { _Titre = value; }
}
public Double PointA
{
get { return _PointA; }
set { _PointA = value; }
}
public Double PointT
{
get { return _PointT; }
set { _PointT = value; }
}
public Double PointAT
{
get { return _PointAT; }
set { _PointAT = value; }
}
public List<String> JoueurA
{
get { return _JoueurA; }
set { _JoueurA = value; }
}
public List<String> JoueurT
{
get { return _JoueurT; }
set { _JoueurT = value; }
}
public List<String> JoueurAT
{
get { return _JoueurAT; }
set { _JoueurAT = value; }
}
public Extrait(String Artiste, String Titre, Double PointA, Double PointT, Double PointAT, List<String> JoueurAT, List<String> JoueurA, List<String> JoueurT)
{
_Artiste = Artiste;
_Titre = Titre;
_PointA = PointA;
_PointT = PointT;
_PointAT = PointAT;
_JoueurAT = JoueurAT;
_JoueurA = JoueurA;
_JoueurT = JoueurT;
}
}
Well, I've tried many possibilities, but none worked !
If this is your actual xml, then look at the inner tags - they start with lower letter. Your xml have <joueurAT> while you selecting elements with name .Elements("JoueurAT") - node names are case-sensitive.
Your code should look like:
extraits = (from ex in xmle.Element("playlist").Elements("extrait")
select new Extrait
(ex.Element("artiste").Value,
ex.Element("titre").Value,
0,
0,
0,
(from jat in ex.Element("JoueursAT").Elements("joueurAT")
select jat.Value).ToList(),
(from ja in ex.Element("JoueursA").Elements("joueurA")
select ja.Value).ToList(),
(from jt in ex.Element("JoueursT").Elements("joueurT")
select jt.Value).ToList())).ToArray();
This should fix it. Basically the default behaviour is to use two-level nesting for lists (meaning it defaults to [XmlArray] plus [XmlArrayItem]; you only have one-level here, so you need to tell it.
[XmlElement]
public List<String> JoueurA
{
get { return _JoueurA; }
set { _JoueurA = value; }
}
[XmlElement]
public List<String> JoueurT
{
get { return _JoueurT; }
set { _JoueurT = value; }
}
[XmlElement]
public List<String> JoueurAT
{
get { return _JoueurAT; }
set { _JoueurAT = value; }
}
Btw; you might find it more convenient to use something like auto-properties here; an example for both regular properties and lists:
public double PointAT {get;set;}
[XmlElement]
public List<string> JoueurA {get;} = new List<string>();
This is a lot more convenient than messing with all the fields yourself.
You probably also want to make sure you have a public parameterless constructor; frankly I'd just remove the custom constructor (in which case: a public parameterless constructor is included for free), but otherwise - I'd just add:
public Extrait() {}
The change is needed in the blind class
[XmlRoot("blind")]
public class Blind
{
[XmlArray("playlist")]
[XmlArrayItem("extrait")]
public List<Extrait> extrait { get; set; }
}
public class Extrait
{
}
(from jat in ex.Element("JoueursAT").Elements("JoueurAT")
select jat.Element("JoueurAT").Value).ToList()
should become
(from jat in ex.Element("JoueursAT").Elements("joueurAT")
select jat.Value).ToList()
I'm not sure if C#'s XML library is case sensitive when it comes to token names, but when in doubt, it's better to play it safe.
You also tried to access an element "joueurAT" when making your select despite having actually looped over them already, so you can just access the Value property directly.
Also instead of using LINQ the way you do, you could try the extension methods, which tend to be more readable :
xmle.Element("playlist").Elements("extrait")
.Select(ex => new Extrait
{
Artiste = ex.Element("artiste").Value,
Titre = ex.Element("titre").Value,
PointA = 0,
PointT = 0,
PointAT = 0,
JoueurA = ex.Element("JoueursAT").Elements("joueurAT").Select(jat => jat.Value).ToList(),
JoueurT = ex.Element("JoueursA").Elements("joueurA").Select(ja => ja.Value).ToList(),
JoueurAT = ex.Element("JoueursT").Elements("joueurT").Select(jt => jt.Value).ToList()
});

Using Linq to determine if there is more than 2 distinct items in a class

I have a class that contains a list of another class which has a property that I want to check if it has more than one distinct value.
e.g
public class BasketModel
{
public BasketModel()
{
BasketOrderLines = new List<BasketOrderLine>();
}
.
.
.
public class BasketOrderLine
{
public int OrderLineId { get; set; }
public string ImageUrl { get; set; }
public string ProductType { get; set; }
.
.
Given a basket model object I want to find out if there are more than one distinct value in the ProductType.
e.g If all Product Types are "A" then that would be false, if 3 products are of type "A" and one is of type "B" then this would be true.
Cheers
Macca
Your title: "more than two distinct", your question body: "more than one distinct"
If the title is a typo:
bool notDistinctTypes = theBasket.BasketOrderLine
.Select(o => o.ProductType)
.Distinct()
.Skip(1)
.Any();
This doesn't need to enumerate all items to find out if there is more than one ProductType.
// Does this basket contains three or more types
public bool HasSeveralTypes(BasketModel basket)
{
if (basket == null)
return false;
int differentTypes = basket.BasketOrderLines
.Select(l => l.ProductType)
.Distinct()
.Count();
return (differentTypes > 2);
}
Something like this :
Public bool CheckDistinct (){
var res = basketOrderLines.Select(o => o.ProductType).Distinct ().Count ();
return res > 1;
}
There are a few ways to do this, here's one:
public class BasketModel
{
public BasketModel()
{
BasketOrderLines = new List<BasketOrderLine>();
}
public bool HasMulitpleDistinctProducts
{
get
{
if (!BasketOrderLines.Any())
{
return true; // or false?
}
return BasketOrderLines.Select(b => b.ProductType).Distinct().Count() > 1;
}
}
}
Here is a type extension you can call directly from your list. The pros of this code is to be adaptable to any type implementing IEquals and not only string + kick to use from your code.
The code :
public static class Tools
{
public static bool fDistinctProductType(this List<BasketOrderLine> lstToAnalyse)
{
BasketOrderLine ProductTypeA = lstToAnalyse.FirstOrDefault();
if (ProductTypeA == null) // It's null when lstToAnalyse is empty
return false;
BasketOrderLine ProductTypeB = lstToAnalyse.Where(b => b.ProductType.Equals(ProductTypeA.ProductType)).FirstOrDefault();
if (ProductTypeB == null) // It's null when it doesn't exists a distinct ProductType
return false;
return true;
}
}
How to call:
List<BasketOrderLine> lst = new List<BasketOrderLine>();
// Add element to list
if (lst.fDistinctProductType())
{
// DO Something
}

Message is not re updating

I have 2 kind of "Description" for class "People",
Having data with Message:, Priority: and Tag:
Having data without these
List<People> lstPeople = new List<People>
{
new People { Message = "[1y] Message: test messg1 Priority: top Tag: t1" },
new People { Message = "without Message, Priority and Tag desc" }
};
var data = lstPeople;
I would like to extract data after "Message:", "Priority:" and "Tag:" and re-update of each entity of "People" class "Message", "Priority" and "Tag".
Below code does update for "Priority" and "Tag" but not for "Message". What could be the reason?
public class People
{
const string ALERT_DESC_MARKER = "Message:";
const string PRIORITY_MARKER = "Priority:";
const string TAG_MARKER = "Tag:";
private string _description;
private string _tag;
private string _priority;
public string Message
{
get
{
if (_description.Contains(ALERT_DESC_MARKER) && _description.Contains(PRIORITY_MARKER))
return _description ?? GetTextPart(_description, ALERT_DESC_MARKER, PRIORITY_MARKER).Trim();
else
return _description;
}
set
{
_description = value;
}
}
public string Priority
{
get
{
if (_description.Contains(PRIORITY_MARKER) && _description.Contains(TAG_MARKER))
return _priority = GetTextPart(_description, PRIORITY_MARKER, TAG_MARKER).Trim();
else
return _priority;
}
set
{
_priority = value;
}
}
public string Tag
{
get
{
if (_description.Contains(TAG_MARKER))
return _tag = GetTextPart(_description, TAG_MARKER, null).Trim();
else
return _tag;
}
set
{
_tag = value;
}
}
private string GetTextPart(string text, string before, string after)
{
string result = null;
int posBefore = text.IndexOf(before);
if (after != null)
{
int posAfter = text.IndexOf(after);
result = text.Remove(posAfter).Substring(posBefore + before.Length).TrimEnd();
}
else
result = text.Substring(posBefore + before.Length);
return result;
}
}
The problem is in the getter of Message.
As the documentation says:
The ?? operator is called the null-coalescing operator. It returns the left-hand operand if the operand is not null; otherwise it returns the right hand operand.
So in your case you always return the entire string if _description is not null. Instead you need to return GetTextPart if this condition (_decription != null) is met. Like this:
if (_description.Contains(ALERT_DESC_MARKER) && _description.Contains(PRIORITY_MARKER))
return _description != null ? GetTextPart(_description, ALERT_DESC_MARKER, PRIORITY_MARKER).Trim() : _description;
EDIT:
Here is your test example:
List<People> lstPeople = new List<People>
{
new People { Message = "[1y] Message: test messg1 Priority: top Tag: t1" },
new People { Message = "without Message, Priority and Tag desc" }
};
var data = lstPeople;
foreach (var item in lstPeople)
{
Console.WriteLine(item.Message);
Console.WriteLine(item.Priority);
Console.WriteLine(item.Tag);
}
Output first Item:
test messg1
top
t1
Output second Item:
without Message, Priority and Tag desc
in the second item Priority and Tag are null.
EDIT 2:
Here is the Message getter. The rest of the code that I used to test is identical to the one that you posted. The commented out part is your old/posted version
public string Message
{
get
{
if (_description.Contains(ALERT_DESC_MARKER) && _description.Contains(PRIORITY_MARKER))
return _description != null ? GetTextPart(_description, ALERT_DESC_MARKER, PRIORITY_MARKER).Trim(): _description;
//if (_description.Contains(ALERT_DESC_MARKER) && _description.Contains(PRIORITY_MARKER))
// return _description ?? GetTextPart(_description, ALERT_DESC_MARKER, PRIORITY_MARKER).Trim();
else
return _description;
}
set
{
_description = value;
}
}

How to check CONTAINS with multiple values

I am trying to find all the zones that contain 2 or more zone members where the search term is a string value. Here is the code I have. In the FindCommmonZones method when I try to cast the result of an Intersect to an ObservableCollection I get a run-time on an invalid cast. The question is, is there a better way to do this? The string array that is the paramter for FindCommonZones() can be any count of strings. StackOverflow had some other similar posts but none really answered my question - it looked like they all pertained more to SQL.
Some code:
public class Zone
{
public List<ZoneMember> MembersList = new List<ZoneMember>();
private string _ZoneName;
public string zoneName{ get{return _ZoneName;} set{_ZoneName=value;} }
public Zone ContainsMember(string member)
{
var contained = this.MembersList.FirstOrDefault(m => m.MemberWWPN.
Contains(member) || m.MemberAlias.Contains(member));
if (contained != null) { return this; }
else { return null; }
}
}
public class ZoneMember
// a zone member is a member of a zone
// zones have ports, WWPNs, aliases or all 3
{
private string _Alias = string.Empty;
public string MemberAlias {get{return _Alias;} set{_Alias = value; } }
private FCPort _Port = null;
public FCPort MemberPort { get { return _Port; } set { _Port = value; } }
private string _WWPN = string.Empty;
public string MemberWWPN { get { return _WWPN; } set { _WWPN = value; } }
private bool _IsLoggedIn;
public bool IsLoggedIn { get { return _IsLoggedIn; } set { _IsLoggedIn = value; } }
private string _FCID;
public string FCID {get{return _FCID;} set{ _FCID=value; } }
}
private ObservableCollection<ZoneResult> FindCommonZones(string[] searchterms)
{
ObservableCollection<ZoneResult> tempcollection =
new ObservableCollection<ZoneResult>();
//find the zones for the first search term
tempcollection = this.FindZones(searchterms[0]);
//now search for the rest of the search terms and compare
//them to existing result
for (int i = 1; i < searchterms.Count(); i++ )
{
// this line gives an exception trying to cast
tempcollection = (ObservableCollection<ZoneResult>)tempcollection.
Intersect(this.FindZones(searchterms[i]));
}
return tempcollection;
}
private ObservableCollection<ZoneResult> FindZones(string searchterm)
// we need to track the vsan where the zone member is found
// so use a foreach to keep track
{
ObservableCollection<ZoneResult> zonecollection = new ObservableCollection<ZoneResult>();
foreach (KeyValuePair<int, Dictionary<int, CiscoVSAN>> fabricpair in this.FabricDictionary)
{
foreach (KeyValuePair<int, CiscoVSAN> vsanpair in fabricpair.Value)
{
var selection = vsanpair.Value.ActiveZoneset.
ZoneList.Select(z => z.ContainsMember(searchterm)).
Where(m => m != null).OrderBy(z => z.zoneName);
if (selection.Count() > 0)
{
foreach (Zone zone in selection)
{
foreach (ZoneMember zm in zone.MembersList)
{
ZoneResult zr = new ZoneResult(zone.zoneName,
zm.MemberWWPN, zm.MemberAlias, vsanpair.Key.ToString());
zonecollection.Add(zr);
}
}
}
}
}
return zonecollection;
}
Intersect is actually Enumerable.Intersect and is returning an IEnumerable<ZoneResult>. This is not castable to an ObservableCollection because it isn't one - it is the enumeration of the intersecting elements in both collections.
You can, however create a new ObservableCollection from the enumeration:
tempcollection = new ObservableCollection<ZoneResult>(tempcollection
.Intersect(this.FindZones(searchterms[i]));
Depending on how many elements you have, how ZoneResult.Equals is implemented, and how many search terms you expect, this implementation may or may not be feasable (FindZones does seem a little overly-complicated with O(n^4) at first glance). If it seems to be a resource hog or bottleneck, it's time to optimize; otherwise I would just leave it alone if it works.
One suggested optimization could be the following (incorporating #Keith's suggestion to change ContainsMember to a bool) - although it is untested, I probably have my SelectManys wrong, and it really largely amounts to the same thing, you hopefully get the idea:
private ObservableCollection<ZoneResult> FindCommonZones(string[] searchterms)
{
var query = this.FabricDictionary.SelectMany(fabricpair =>
fabricpair.Value.SelectMany(vsanpair =>
vsanpair.Value.ActiveZoneSet.ZoneList
.Where(z=>searchterms.Any(term=>z.ContainsMember(term)))
.SelectMany(zone =>
zone.MembersList.Select(zm=>new ZoneResult(zone.zoneName, zm.MemberWWPN, zm.MemberAlias, vsanpair.Key.ToString()))
)
)
.Distinct()
.OrderBy(zr=>zr.zoneName);
return new ObservableCollection<ZoneResult>(query);
}

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