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;
Related
I am trying to read and store data from an xml file. I have been reading about various methods to read the data such as XmlReader, XmlTextReader, LinQ, etc.
My XML file is
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<circuit name="local">
<Device>device1</Device>
<Point>point1></Point>
</circuit>
<circuit name ="remote">
<Device>device2</Device>
<Point>point2</Point>
</circuit>
</configuration>
I am trying to extract Device and Point set so I can pass those along to be used in a database query. I used this code and the foreach loop to verify the contents, but it only gets the first set.
XDocument msrDoc = XDocument.Load("BNOC MSR.config");
var data = from item in msrDoc.Descendants("circuit")
select new
{
device = item.Element("Device").Value,
point = item.Element("Point").Value
};
foreach (var p in data)
Console.WriteLine(p.ToString());
I have also tried this, but my arrays were all null
String[] deviceList = new String[1];
String[] pointList = new String[1];
int n = 0;
XmlDocument msrDoc = new XmlDocument();
msrDoc.Load("BNOC MSR.config");
var itemNodes = msrDoc.SelectNodes("circuit");
foreach (XmlNode node in itemNodes)
{
var circuit = node.SelectNodes("circuit");
foreach (XmlNode cir in circuit)
{
deviceList[n] = cir.SelectSingleNode("Device").InnerText;
pointList[n] = cir.SelectSingleNode("Point").InnerText;
}
}
Any help would be greatly appreciated.
Are you sure you don't want to use the built-in Properties.Settings for this?
Circuit local = Properties.Settings.Default.localCircuit;
Circuit remote = Properties.Settings.Default.remoteCircuit;
https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/using-application-settings-and-user-settings
I believe there is something wrong with the way you are testing the result. The code:
void Main()
{
var fileLocation = #"C:\BrianTemp\input.txt";
var xml = File.ReadAllText(fileLocation);
XDocument msrDoc = XDocument.Load(fileLocation);
var data = from item in msrDoc.Descendants("circuit")
select new
{
device = item.Element("Device").Value,
point = item.Element("Point").Value
};
foreach (var p in data)
{
//It is best practice to use statement blocks {} to prevent silly errors.
//Sometimes you want to execute multiple statements, especially as code changes later
Console.WriteLine($"{p}");
}
}
Produces the expected output:
{ device = device1, point = point1 }
{ device = device2, point = point2 }
You said:
I used this code and the foreach loop to verify the contents, but it
only gets the first set.
As you can see the code produces 2 results as it should.
Note: I corrected the XML file to remove the extra >
<Point>point1></Point><==
I see two problems in your code (and I only tried the second method you posted):
Your string arrays are too small, change to:
String[] deviceList = new String[1];
String[] pointList = new String[1];
The line var itemNodes = msrDoc.SelectNodes("circuit"); should be
var itemNodes = msrDoc.SelectNodes("configuration");
I'm developing a web service that will receive a XML string and will compare with the internalName string. I'm using LINQ to parse the XML (and I think I'm doing it correctly) but I'm not sure how to compare the "value" withinternalName, per example.
[WebMethod]
public string WMCompare (string xml, string internalName)
{
XDocument xmlDoc = XDocument.Load(xml);
var result = from ele in xmlDoc.Descendants("property")
select new
{
key = (string)ele.Element("key"),
value = (string)ele.Element("value")
};
foreach (var i in result)
{
}
}
}
Thank you for your attention and I'm sorry about the newbie question. It's my first time working with XML.
Considering that you are comparing string with value:
var newResult = result.Where(r => r.value.Equals(internalName))
Alternatively, you may also compare while parsing your XML:
var result1 = from ele in doc.Descendants("property")
where ele.HasElements && ele.Element("value") != null && ele.Element("value").Equals(internalName)
select new
{
key = (string)ele.Element("key"),
value = (string)ele.Element("value")
};
Good day, in my project I save all my data into an XML file. When you start up the program it reads from said XML file (Name: ProjectList.xml).
I have searched, and read through quite a few "similar" problems, where I've tried their solutions and it still came up with the same.
So, the problem:
I can read the 'ID' and 'Employee ID' values fine, I can even read my employee data from another part of the software using the same code and it works 100%. But for some reason when it has to read the TimeStart and TimeStop values, the string returns as "" (Empty).
I've had a similar problem reading pure Integers from XML but that was fixed with
ReadElementContentAsInt()
Tried the similar
ReadElementContentAsDateTime()
seeing as it's a DateTime value directly written from DateTime.ToString(). But it also returns as "" (Empty).
I welcome any suggestions, other than to move away from XML :P
Sample Code:
if (File.Exists(FileName))
using (XmlReader reader = XmlReader.Create(FileName))
{
while (reader.Read())
{
if (reader.IsStartElement())
{
switch (reader.Name)
{
... (Shortening example of code for the sake of relevancy)
case "TimeStart":
string str = reader.ReadOuterXml();
DateTime t = DateTime.ParseExact(str, "yyyy/MM/dd hh:mm:ss tt", CultureInfo.InvariantCulture);
_ProjectData[count].TimeStamps[c].Start = t;
break;
case "TimeStop":
t = DateTime.ParseExact(reader.Value.Trim(), "yyyy/MM/dd hh:mm:ss tt", CultureInfo.InvariantCulture);
_ProjectData[count].TimeStamps[c].Stop = t;
break;
}
}
}
}
Sample XML file:
<Projects>
<Project>
<ID>D11</ID>
<TimeStamps>
<TimeStamp>
<EmployeeID>0</EmployeeID>
<TimeStart>2016/05/24 8:47:30 PM</TimeStart>
<TimeStop>2016/05/24 8:47:32 PM</TimeStop>
</TimeStamp>
</TimeStamps>
</Project>
<Project>
<ID>D12</ID>
<TimeStamps>
<TimeStamp>
<EmployeeID>0</EmployeeID>
<TimeStart>2016/05/24 8:51:06 PM</TimeStart>
<TimeStop>2016/05/24 9:31:27 PM</TimeStop>
</TimeStamp>
<TimeStamp>
<EmployeeID>0</EmployeeID>
<TimeStart>2016/05/24 9:47:44 PM</TimeStart>
<TimeStop>2016/05/24 10:51:11 PM</TimeStop>
</TimeStamp>
</TimeStamps>
</Project>
</Projects>
You can use ReadElementContentAsString() to get string value. Value property itself is empty because NodeType property is Element and reader will always return empty string in that case.
Alternatively you can try
reader.Read();
string str = reader.Value;
DateTime t = DateTime.ParseExact(str, "yyyy/MM/dd hh:mm:ss tt", CultureInfo.InvariantCulture);
_ProjectData[count].TimeStamps[c].Start = t;
to set reader in correct position.
LINQ to XML is a really nice API that lets you easily query your XML using LINQ queries. For example, this query would let you find a Project by ID and get the (only) timestamp's start time - but you could write any sort of query, really:
var doc = XDocument.Load(fileName);
var startForD11 = (DateTime)doc
.Descendants("Project")
.Where(x => (string) x.Element("ID") == "D11")
.Descendants("TimeStart")
.Single();
Or get all the timestamps:
var timestamps =
from project in doc.Descendants("Project")
let projectId = (int) project.Element("ID")
from timestamp in project.Descendants("Timestamp")
select new
{
ProjectId = projectId,
EmployeeId = (int) timestamp.Element("EmployeeID"),
TimeStart = (DateTime) timestamp.Element("TimeStart"),
TimeEnd = (DateTime) timestamp.Element("TimeEnd"),
};
See this fiddle for a working demo. It's probably worth further investigation if you've not come across it.
I am trying to export the XML file from the database table using XElement. I am using EF 6.0 Code first.
I am preparing XML using XElement by below code
TooltypeXml =
new XElement("ToolTypes",
(from tbl in db.ToolType
where tbl.CreationDate >= objLastSyncByDevice.LocaltoServerLastAccessDate
|| tbl.LastModifieDate >= objLastSyncByDevice.LocaltoServerLastAccessDate
|| tbl.IsDeleted == true
select new
{
tbl.ToolTypeId,
tbl.ToolTypeName,
tbl.Action,
tbl.UpdatedBy,
tbl.CreationDate,
tbl.CreatedBy,
tbl.LastModifieDate,
tbl.IsDeleted
}).ToList()
.Select(x =>
new XElement("ToolType",
new XElement("ToolTypeName", x.ToolTypeName),
new XElement("Action", x.Action),
new XElement("UpdatedBy", x.UpdatedBy),
new XElement("CreationDate", x.CreationDate),
new XElement("CreatedBy", x.CreatedBy),
new XElement("LastModifieDate", x.LastModifieDate),
new XElement("IsDeleted", x.IsDeleted))));
So It does create the XML format successfully , what I want is how I can write my Linq expression so I don't need to specify each and every fields in expression to select. Because I always want all the fields from table and if I do change something in table I don't need to change anything in code. Please help. Thanks in advance.
I got the solution of this problem,
I have added following code to get all properties of a class and create each fields as XElement.
private void CreateXElemetsOfClass(System.Type typeOfClass, string TableName, dynamic objData, string p_mainElementName, string MachineID)
{
try
{
if (objData != null && objData.Count > 0)
{
System.Reflection.PropertyInfo[] properties = typeOfClass.GetProperties();
List<XElement> lstXElements = new List<XElement>();
List<XElement> lstmainElements = new List<XElement>();
XElement rootElement = new XElement(TableName);
foreach (var item in objData)
{
lstXElements = new List<XElement>();
XElement mainElement = new XElement(p_mainElementName);
foreach (System.Reflection.PropertyInfo property in properties)
{
var notMapped = property.GetCustomAttributes(typeof(NotMappedAttribute), false);
if (notMapped.Length == 0)
{
lstXElements.Add(new XElement(property.Name, property.GetValue(item, null)));
}
}
mainElement.Add(lstXElements);
lstmainElements.Add(mainElement);
}
rootElement.Add(lstmainElements);
string XMLFilePath = serializetoxmlNew(rootElement, MachineID, TableName);
}
}
catch (Exception ex)
{
throw;
}
}
Here, type is System.Type classType = typeof(FileTypes);,
objData is a list i want to retrieve in XML,
p_mainElementName = "FileType".
It will generate an XML as below,
<?xml version="1.0" encoding="utf-8"?>
<FileTypes>
<FileType>
<FileTypeId>1b254bc3-7516-4f9c-89ea-d9b20ecbf005</FileTypeId>
<Description>Excel</Description>
<CreatedBy>22a8be24-9272-4d7e-9248-e9064f917884</CreatedBy>
<UpdatedBy>22a8be24-9272-4d7e-9248-e9064f917884</UpdatedBy>
<CreateDate>2014-08-22T16:05:53.177</CreateDate>
<UpdateDate>2014-08-22T16:05:53.177</UpdateDate>
<FileExtension>.xls</FileExtension>
<FileTypeImage>excel.png</FileTypeImage>
<Action>Create</Action>
<IsDeleted>false</IsDeleted>
</FileType>
<FileType>
<FileTypeId>f3362487-d96e-4cc8-bc4b-5120866f95ce</FileTypeId>
<Description>Adobe Acrobat Reader</Description>
<CreatedBy>22a8be24-9272-4d7e-9248-e9064f917884</CreatedBy>
<UpdatedBy>22a8be24-9272-4d7e-9248-e9064f917884</UpdatedBy>
<CreateDate>2014-08-22T16:05:12.407</CreateDate>
<UpdateDate>2014-08-22T16:05:12.407</UpdateDate>
<FileExtension>.pdf</FileExtension>
<FileTypeImage>pdf.png</FileTypeImage>
<Action>Create</Action>
<IsDeleted>false</IsDeleted>
</FileType>
</FileTypes>
Hope this helps you! It is perfectly working.
It looks like you would better use serialization of your objects to XML and do not create the custom serialization function.
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.