I need to parse a big XML file (≈100MB, 400000rows) then put the data in a List.
First I try to parse the XML file in a C# console application, it taked about 2s to finish the job.
Then I copy the code into Unity, and it taked about 17s to finish the job.
Anybody know why it become so slowly? And how to make it faster?
Thanks!
Code:
// Used for storing data
stateList = new List<BallisticState>();
XmlTextReader reader = new XmlTextReader(filepath);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == "row")
{
string value = reader.GetAttribute("value").TrimStart();
BallisticState state = new BallisticState();
// this method converts string to float
SetBallisticState(state, value);
stateList.Add(state);
}
}
}
Related
I have a very large xml-file (let's say it has about 300000 elements). In my part of the application I only have to know if the name of the root-element is ApplicationLog and if there is an attribute called LogId in the root-element.
To read the XML I use:
XDocument document;
using (StreamReader streamReader = new StreamReader(filePath, true))
{
document = XDocument.Load(streamReader);
}
and to get the information I need:
try
{
if (document.Root != null)
{
if (string.Equals(document.Root.Name.LocalName, "ApplicationLog", StringComparison.InvariantCultureIgnoreCase) &&
document.Root.HasAttributes && (from o in document.Root.Attributes() where string.Equals(o.Name.LocalName, "LogId", StringComparison.InvariantCultureIgnoreCase) select o).Any())
{
isRelevantFile = true;
}
}
}
catch (Exception e)
{
}
This just works fine.
The problem is that the XDocument.Load takes about 15 seconds to load a XML-File which is about 20MB.
I also tried it with the XmlDocument, but there I have the same problem.
My first idea for a solution was to read the file as text and parse the first lines for the searched element/attribute. But this seems to be not so professional to me.
Does anybody know a better way to achieve this?
Use the XmlReader API with
using (XmlReader xr = XmlReader.Create(filePath))
{
xr.MoveToContent();
if (xr.LocalName == "ApplicationLog" ...)
}
You can try the solution provided here or use/develop a SAX reader such as this one. You can find more information on SAX here.
This question already has answers here:
XDocument Get Part of XML File
(3 answers)
Closed 8 years ago.
I did write this code to read a url of xml file:
XDocument feedXml = XDocument.Load("url address of xml file here");
var feeds = from feed in feedXml.Descendants("List")
select new Event {
Id = Int32.Parse(feed.Element("ID").Value),
Name = feed.Element("Name").Value,
City = feed.Element("City").Value
};
return feeds;
My problem is that the file is too large (about 40MB) and take too much time to load.
So I am using XmlReader to read xml file but this was not applicable too because I don't know how to load every (for example 10) records in every page on demand and I should read the whole file every time and skip other records to reach appropriate elements, shouldn't I?
string XmlFileUrl = #"url address of xml file here";
using (XmlReader reader = new XmlTextReader(XmlFileUrl))
{
bool openItem = false;
Event item = new Event();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == "List")
{
item = new Event();
openItem = true;
}
else if (reader.Name == "Name" && openItem)
item.Name = reader.ReadElementContentAsString();
...
}
else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "List" && openItem)
{
openItem = false;
feeds.Add(item);
}
}
}
Is there any way to use Jquery ajax or convert the xml file to json with paging to load just needed data on every page, or any suggestion?
I think this is not so easy to achieve with a XML structure and maybe in this case is XDocument.Load not the appropriate method, because AFAIK it always loads the whole document immediately. You can try the overload with the Stream parameter, instead of URL, and try loading only a part of the the file over the net. Probably you need to write your own loader (that gets only a portion of the file, maybe XmlDocument?) and parse the incomplete structure by yourself.
If you can call portions of the XML file controlled by the URI (for example: http://domain/entries?page=10&take=20 and this call returns a valid XML), then an option would be to use this URL instead of the link to the whole file, something like:
var pagedUri = #"http://domain/entries?page=10&take=20";
XDocument feedXml = XDocument.Load(pagedUri);
var feeds = from feed in feedXml.Descendants("List")
select new Event {
Id = Int32.Parse(feed.Element("ID").Value),
Name = feed.Element("Name").Value,
City = feed.Element("City").Value
};
return feeds;
Have a look at this SO post where a similar problem is addressed.
I am working on a project that requires all SQL connection and query information to be stored in XML files. To make my project configurable, I am trying to create a means to let the user configure his sql connection string information (datasource, catalog, username and password) via a series of text boxes. This input will then be saved to the appropriate node within the SQL document.
I can get the current information from the XML file, and display that information within text boxes for the user's review and correction, but I'm encountering an error when it comes time to save the changes.
Here is the code I'm using to update and save the xml document.
protected void submitBtn_Click(object sender, EventArgs e)
{
SPFile file = methods.web.GetFile("MyXMLFile.xml");
myDoc = new XmlDocument();
byte[] bites = file.OpenBinary();
Stream strm1 = new MemoryStream(bites);
myDoc.Load(strm1);
XmlNode node;
node = myDoc.DocumentElement;
foreach (XmlNode node1 in node.ChildNodes)
{
foreach (XmlNode node2 in node1.ChildNodes)
{
if (node2.Name == "name1")
{
if (node2.InnerText != box1.Text)
{
}
}
if (node2.Name == "name2")
{
if (node2.InnerText != box2.Text)
{
}
}
if (node2.Name == "name3")
{
if (node2.InnerText != box3.Text)
{
node2.InnerText = box3.Text;
}
}
if (node2.Name == "name4")
{
if (node2.InnerText != box4.Text)
{
}
}
}
}
myDoc.Save(strm1);
}
Most of the conditionals are empty at this point because I'm still testing.
The code works great until the last line, as I said. At that point, I get the error "Memory Stream is not expandable." I understand that using a memory stream to update a stored file is incorrect, but I can't figure out the right way to do this.
I've tried to implement the solution given in the similar question at Memory stream is not expandable but that situation is different from mine and so the implementation makes no sense to me. Any clarification would be greatly appreciated.
Using the MemoryStream constructor that takes a byte array as an argument creates a non-resizable instance of a MemoryStream. Since you are making changes to the file (and therefore the underlying bytes), you need a resizable MemoryStream. This can be accomplished by using the parameterless constructor of the MemoryStream class and writing the byte array into the MemoryStream.
Try this:
SPFile file = methods.web.GetFile("MyXMLFile.xml");
myDoc = new XmlDocument();
byte[] bites = file.OpenBinary();
using(MemoryStream strm1 = new MemoryStream()){
strm1.Write(bites, 0, (int)bites.Length);
strm1.Position = 0;
myDoc.Load(strm1);
// all of your edits to the file here
strm1.Position = 0;
// save the file back to disk
using(var fs = new FileStream("FILEPATH",FileMode.Create,FileAccess.ReadWrite)){
myDoc.Save(fs);
}
}
To get the FILEPATH for a Sharepoint file, it'd be something along these lines (I don't have a Sharepoint development environment set up right now):
SPFile file = methods.web.GetFile("MyXMLFile.xml")
var filepath = file.ParentFolder.ServerRelativeUrl + "\\" + file.Name;
Or it might be easier to just use the SaveBinary method of the SPFile class like this:
// same code from above
// all of your edits to the file here
strm1.Position = 0;
// don't use a FileStream, just SaveBinary
file.SaveBinary(strm1);
I didn't test this code, but I've used it in Sharepoint solutions to modify XML (mainly OpenXML) documents in Sharepoint lists. Read this blogpost for more information
You could look into using the XDocument class instead of XmlDocument class.
http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument.aspx
I prefer it because of the simplicity and it eliminates having to use Memory Stream.
Edit: You can append to the file like this:
XDocument doc = XDocument.Load('filePath');
doc.Root.Add(
new XElement("An Element Name",
new XAttribute("An Attribute", "Some Value"),
new XElement("Nested Element", "Inner Text"))
);
doc.Save(filePath);
Or you can search for an element and update like this:
doc.Root.Elements("The element").First(m =>
m.Attribute("An Attribute").Value == "Some value to match").SetElementValue(
"The element to change", "Value to set element to");
doc.Save('filePath');
Im trying to open and edit XML file inside Silverlight element, but I can't edit it.
My XML file (Customers.xml) looks like this:
<?xml version="1.0"?>
<customers>
<customer>Joe</customer>
<customer>Barrel</customer>
</customers>
And my C# logic:
[...]
XDocument xdoc = XDocument.Load("Customers.xml");
xdoc.Root.Add(new XElement("customer", "Stephano")); //here I wish it to add Stephano as a customer.
using (var file = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = file.OpenFile("Customers.xml", FileMode.Create))
{
xdoc.Save(stream); //and here I wish it to save it to the file
}
}
PopulateCustomersList();
/\ here is a function that is used to display the content of XML file, it goes:
private void PopulateCustomersList()
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.XmlResolver = new XmlXapResolver();
XmlReader reader = XmlReader.Create("Customers.xml");
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "customer")
{
//OutputTextBlock.Text = reader.GetAttribute("first");
customersList.Items.Add(new ListBoxItem()
{
Content = reader.ReadInnerXml()
});
}
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "customers")
{
break;
}
}
reader.Close();
}
In my xaml file I have
<ListBox x:Name="customersList" />
so it gets displayed, but the problem is that only Joe and Barrel gets to be displayed and where is Stephano?
I got this code from various tutorials and forums, I don't quite understand it, I know it may be strange way to do that, but I just can't find out how to do that and I'm trying all sort of things. The funniest thing is that I found on many forums a way to save the file, which looks like this:
xdoc.Save("Customers.xml"); but my Visual Studio says that the arguments are wrong, because it is a string. How am I supposed to tell him that its a file?
Okay:
.Save() saves the current XDocument, I.E it's going to save the XML file you loaded up here
XDocument xdoc = XDocument.Load("Customers.xml");
So it should be something like (this is coded without any knowledge more than you provided)
XDocument xdoc = XDocument.Load("Customers.xml");
xdoc.Root.Add(new XElement("customer", "Stephano"));
xdoc.Save();
PopulateCustomersList(xdoc);
private void PopulateCustomersList(XDocument xdoc)
{
foreach(XElement in element xdoc.Root.Elements("customer"))
{
customersList.Items.Add(new ListBoxItem()
{
Content = (string)element;
}
}
}
Recently, I began developing an application that utilizes the use of XML documents in a C# program. Everything worked fine to begin with, but as I edited the code, and added a bit more features to the program in case the XML gets deleted, corrupted, etc, the program didn't want to function properly anymore.
The following code is used when submitting the data and transferring it to XML:
private void doneCreate_Click(object sender, EventArgs e)
{
//Initialize new XMLDocument class.
XmlDocument XmlDoc = new XmlDocument();
//See if the card data file is there, if not, create it.
if (File.Exists(xmlPath) == false)
{
using (FileStream createFile = File.Create(xmlPath))
{
Byte[] FileData = new UTF8Encoding(true).GetBytes(toBase64("<studycards></studycards>"));
// Add some information to the file.
createFile.Write(FileData, 0, FileData.Length);
createFile.Close();
}
XMLData = "<studycards></studycards>";
}
else
{
XMLData = readXML();
if (XMLData == "")
{
XMLData = "<studycards></studycards>";
}
else
{
XMLData = fromBase64(XMLData);
}
}
XmlDoc.LoadXml(XMLData);
XmlElement Group = XmlDoc.CreateElement("Group", null);
XmlAttribute Group_Attr = XmlDoc.CreateAttribute("Name");
Group_Attr.Value = groupName.Text;
Group.Attributes.Append(Group_Attr);
foreach (string[] Card in CardData)
{
try
{
FrontData = Card[0].ToString();
BackData = Card[1].ToString();
NewCard = XmlDoc.CreateElement("Card");
FrontElement = XmlDoc.CreateElement("Front");
FrontElement.InnerText = FrontData;
BackElement = XmlDoc.CreateElement("Back");
BackElement.InnerText = BackData;
NewCard.AppendChild(FrontElement);
NewCard.AppendChild(BackElement);
Group.AppendChild(NewCard);
}
catch
{
break;
}
}
XmlDoc.DocumentElement.AppendChild(Group);
XmlTextWriter write = new XmlTextWriter(xmlPath, null);
write.Formatting = Formatting.Indented;
XmlDoc.Save(write);
}
Upon attempting to click "Done" in the application, I'm presented with the following Exception:
The process cannot access the file 'C:\Users\Toshiba\documents\visual studio 2010\Projects\StudyCards\StudyCards\bin\Debug\Resources\card_data.xml' because it is being used by another process.
Any solutions?
This may well be at least part of the problem:
XmlTextWriter write = new XmlTextWriter(xmlPath, null);
write.Formatting = Formatting.Indented;
XmlDoc.Save(write);
You're never closing the writer, so I would expect it to keep the file open. That will stop future attempts to open the file, until the finalizer kicks in.
Alternatively it could be part of readXML(), which you haven't shown - again, if that leaves the file open, that would cause problems.