Please note that I'm new to C# and I learn it right now :) I couldn't find something similar to my problem, so I came here.
I have an application in which I add customers (it's in the final stage). All customers are stored in an XML file. Every single customer gets a new customer number. In my xml file I got an XmlNode called CustNo. Now if the user add a new customer and type in a number which already exist, it should pop up a message box to say that this number already exists. I got this c# code:
XDocument xdoc = XDocument.Load(path + "\\save.xml");
var xmlNodeExist = String.Format("Buchhaltung/Customers/CustNo");
var CustNoExist = xdoc.XPathSelectElement(xmlNodeExist);
if (CustNoExist != null)
{
MessageBox.Show("asdf");
}
And my XML file looks like this:
<Buchhaltung>
<Customers>
<CustNo>12</CustNo>
<Surname>Random</Surname>
<Forename>Name</Forename>
<Addr>Address</Addr>
<Zip>12345</Zip>
<Place>New York</Place>
<Phone>1234567890</Phone>
<Mail>example#test.com</Mail>
</Customers>
<Customers>
<CustNo>13</CustNo>
<Surname>Other</Surname>
<Forename>Forename</Forename>
<Addr>My Address</Addr>
<Zip>67890</Zip>
<Place>Manhattan</Place>
<Phone>0987654321</Phone>
<Mail>test#example.com</Mail>
</Customers>
</Buchhaltung>
But then the message box always pops up. What am I doing wrong?
That's because your XPath return all CustNo elements, no matter of it's content.
Try following:
var myNumber = 12;
var xmlNodeExist = String.Format("Buchhaltung/Customers/CustNo[. = {0}]", myNumber.ToString());
or using First and LINQ to XML:
var myNumber = 12;
var xmlNodeExist = "Buchhaltung/Customers/CustNo";
var CustNoExist = xdoc.XPathSelectElements(xmlNodeExist).FirstOrDefault(x => (int)x == myNumber);
You are currently testing for existance of any 'CustNo' element. See this reference about the XPath syntax.
Your XPath should say something like this:
Buchhaltung//Customers[CustNo='12']
which would say "any customers element containing a 'CustNo' element with value = '12'"
Combining that with your current code:
var custNoGivenByCustomer = "12";
var xmlNodeExistsXpath = String.Format("Buchhaltung//Customers[CustNo='{0}']", custNoGivenByCustomer );
var CustNoExist = xdoc.XPathSelectElement(xmlNodeExistsXpath);
You can use LINQ to XML
var number = textBox1.Text;
var CustNoExist = xdoc.Descendants("CustNo").Any(x => (string)x == number);
if(CustNoExist)
{
MessageBox.Show("asdf");
}
This is because you select the CustNo elements regardless of their value. This will filter it to the desired customer number:
int custNo = 12;
var xmlNodeExist = String.Format("Buchhaltung/Customers[CustNo={0}]", custNo);
It selects the Customers elements instead, but since you're just checking for existence, that's unimportant.
W3Schools has a good tutorial/reference on XPath.
Related
im using a method to parse a xdocument to an object but i have a situation in this line of code:
var xElementTax = xElementXml.Element(xn + "tax");
var aux = xElementTax.Element(xn + "taxNN").Value;
In the taxNN XName the NN part is a random number, i.e: tax01, tax02, tax03 and goes on. It could be any two digit number.
How can i deal with this situation wheres i dont have a fixed tag? The only fixed part of the tag is the tax word.
Thanks.
Are you looping through all the elements of xElementTax?
If so you can just go with this:
foreach(XElement auxElement in xElementTax.Elements)
{
var aux = auxElement.Value;
// And so on
}
If you want only those which match "taxNN" you can go instead with:
foreach(XElement auxElement in xElementTax.Elements.Where(x => x.Name.ToString().StartsWith(xn + "tax"))
{
var aux = auxElement.Value;
...
}
If there's only going to be one of them you can go with:
XElement auxElement = xElementTax.Elements.Where(
x => x.Name.ToString().StartsWith(xn + "tax").FirstOrDefault();
var aux = auxElement.Value;
i have created a custom XDocument in c# and it looks like the following
<Filters datetimegenerated="28.07.2013 23:12PM">
<SimpleStringFilter column="xxx" table="yyy" groupby="True" seperatereport="true">
good,bad,ugly
</SimpleStringFilter>
<NumaricalFilter column="zzz" table = "mmm">zzz = 100 or zzz= 50</NumaricalFilter>
</Filters>
parsing it with in c# doesn't seem to work here is my code when i try to parse the StringFilterTags, however i get zero count from the above sample
var filters = from simplestringfilter in xdoc.Root.Element("Filters").Elements("SimpleStringFilter")
let column = simplestringfilter.Attribute("column")
let table = simplestringfilter.Attribute("table")
let groupby = simplestringfilter.Attribute("groupby")
let seperatecolumnby = simplestringfilter.Attribute("seperatereport")
let filterstringval = simplestringfilter.Value
select new
{
Column = column,
Table = table,
GroupBy = groupby,
SeperateColumnBy = seperatecolumnby,
Filterstring = filterstringval
};
what am i doing wrong?
Your query is searching off of the root element checking to see if it has a child Filters element. Since the root is the Filters element, that obviously fails which is why you are not getting any results.
There are two ways to resolve this problem. Just don't search for the Filters off of the root and your query should be fine.
var filters =
from simplestringfilter in xdoc.Root.Elements("SimpleStringFilter")
...
A better way to write it IMHO would be to not query off of the root but the document itself. It will look more natural.
var filters =
from simplestringfilter in xdoc.Element("Filters")
.Elements("SimpleStringFilter")
...
How do you parse this kind of XML file with LINQ?
<houses>
<house nbr="146" city="Linköping" owner="john"/>
<house nbr="134" city="Norrköping" owner="wayne"/>
<house nbr="146" city="Köping" owner="steffe"/>
</houses>
All examples I can find only describe how to parse when each element has a value.
If this was the case I would have done it like this:
var houses = from house in xmlDoc.Descendants("house")
select new RowData
{
number = spec.Element("nbr").Value,
city = spec.Element("city").Value,
owner = spec.Element("owner").Value,
};
return houses ;
But this xml file is not formatted that way.
Try this:
var houses = from house in document.Descendants("house")
select new RowData
{
number = (int)house.Attribute("nbr"),
city = (string)house.Attribute("city"),
owner = (string)house.Attribute("owner")
};
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Use LINQ to read all nodes from XML
I am trying to read an XML file using Linq in C# windows application. The sample of the xml string is given below.
<Root>
<Name>John Doe</Name>
<Data>FBCCF14D504B7B2DBCB5A5BDA75BD93B</Data>
<customer>true</customer>
<Accounts>1</Accounts>
<dataSet>
<Type1>Found matching records.</Type1>
<Type2>No matches found.</Type2>
<Type3>Found matching records.</Type3>
</dataSet>
</Root>
I want to display all the data inside the <dataset> tag and <datatag> i want to read <customer> tag as well.
I have created a class with members (string type, string status). Where in type i want to store the type1, 2...and in status i want to store what is inside the type node.
I am able to accomplish this but in the code i have to give
type1 = (string)row.Element("type1"),
type2=(string)row.Element("type2"),
i want to have a generic code in which i dont have to mention every type. In other words i want to read all the child nodes of tag whithout mentioning the tag name. I have spent 2 hours searching for this on google, but haven't found anything yet.
Expected output
save the information in class object (type and status).
And i want to read the customer tag so that i can know whether the person is already a customer
Any help will be very much appreciated.
Thanks
Update
According to inputs received from Raphaël Althaus
I have the following code:
var list = xml.Descendants("dataSet").Elements()
.Select(m => new CustomerInfo
{
Type = m.Name.LocalName,
Value = m.Value
}).ToList();
foreach (CustomerInfo item in list)
{
MessageBox.Show(item.Type+ " "+item.Value);
}
and for reading the Customer tag i have written more code.
var isCustomer = from customer in xmlDoc.Descendants("Root")
select new
{
customer = tutorial.Element("customer").Value,
}
Can i do both in one query?. Or this method is not so heavy on performance, so i can use this?
something like that ?
var q = xml.Descendants("dataSet").Elements()
.Select(m => new
{
type = m.Name.LocalName,
value = m.Value
}).ToList();
You can also directly populate a list of your "class with members"
var list = xml.Descendants("dataSet").Elements()
.Select(m => new <TheNameOfYourClass>
{
Type = m.Name.LocalName,
Value = m.Value
}).ToList();
EDIT :
to get the "customer" value, I would do another query
var customerElement = xml.Element("customer");
var isCustomer = customerElement != null && customerElement.Value == "true";
So you could mix all of that it in a little function
public IList<YourClass> ParseCustomers(string xmlPath, out isCustomer) {
var xml = XElement.Load(xmlPath);
var customerElement = xml.Element("customer");
isCustomer = customerElement != null && customerElement.Value == "true";
return xml.Descendants("dataSet").Elements()
.Select(m => new <YourClass>
{
Type = m.Name.LocalName,
Value = m.Value
}).ToList();
}
Currently working on a wp7 App, its quite basic. the user has a counter and if a date element exists for the current day in an XML file the count is updated, if not a new date element is created for that day and with the count as the value.
My priblem is, all is working fine if a new XML file is created, the current date element is updated no problem, but if I test the following day, a new element is created, but when I want to update the count, a new date element is added. I don't get this as all the code works on a new file, but if the file is a day old its not for some reason.
XML code
<?xml version="1.0" encoding="utf-8"?>
<Countlog>
<date Count="9">4/21/2012</date>
<date Count="4">4/21/2012</date>
<date Count="18">4/21/2012</date>
</Countlog>
C#
private void save_btn_Click(object sender, RoutedEventArgs e)
{
String _count = Count_tb.Text;
String s_todaysdate = todaysdate.Date.ToShortDateString();
IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream("Countlog.xml", FileMode.Open, myIsolatedStorage);
StreamReader reader = new StreamReader(isoStream);
XDocument _xml = XDocument.Load(reader);
isoStream.Close();
var query = from r in _xml.Descendants("Countlog")
where r.Element("date").Value == (DateTime.Now.ToShortDateString())
select r.Element("date");
if (!query.Any())
{
XElement Addnewdate = new XElement("date", s_todaysdate, new XAttribute("Count", _count));
_xml.Root.Add(Addnewdate);
MessageBox.Show("no matching date");
}
else
{
foreach (XElement _date in query)
{
_date.Attribute("Count").Value = _count.ToString();
MessageBox.Show("Updating date");
}
}
IsolatedStorageFileStream isoStreamsave = new IsolatedStorageFileStream("Countlog.xml", FileMode.Truncate, myIsolatedStorage);
_xml.Save(isoStreamsave);
isoStreamsave.Close();
}
private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (pivotholder.SelectedIndex == 1)
{
IsolatedStorageFileStream isoFileStream2 = myIsolatedStorage.OpenFile("Countlog.xml", FileMode.Open);
StreamReader reader = new StreamReader(isoFileStream2);
XML_result.Text = reader.ReadToEnd();
reader.Close();
}
}
Please let me know if you need more info, This is my first time posting here after lurking around for the past few years.
Cheers
Jon
Okay, I've worked it out. This query:
var query = from r in _xml.Descendants("Countlog")
where r.Element("date").Value == (DateTime.Now.ToShortDateString())
select r.Element("date");
will only match if the first date element has the right value. You're iterating over all Countlog elements (of which there's only one), and looking for the first date element (because that's what Element(...) does).
You could change this to use simply:
var query = _xml.Root.Elements("date")
.Where(x => x.Value == (DateTime.Now.ToShortDateString())
However, I would suggest an alternative format to start with:
var date = DateTime.Today;
var query = _xml.Root.Elements("date")
.Where(x => (DateTime) x.Value == date);
Then to add a new element:
XElement element = new XElement("date",
new XAttribute("count", count),
date);
Or to update one:
element.Attribute("count").SetValue(count);
This uses the data-type handling of LINQ to XML, instead of converting everything to strings explicitly.
1) Your query in its current form searches for the date that is a real date at that moment
where r.Element("date").Value == (DateTime.Now.ToShortDateString())
On the other hand, what you want to query for, I believe, is some other date which you store in s_todaysdate variable.
2) As already pointed out by Jon Skeet, you assume there is only one <date> element.
Start your query over _xml.Root.Elements("date").
Thus, the final query needs to be modified to:
var query = from date in _xml.Root.Elements("date")
where date.Value == s_todaysdate
select date;