XDocument and load too large XML file on demand [duplicate] - c#

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.

Related

Why XmlTextReader become so slowly in Unity?

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);
}
}
}

How to read and write from file in C# UWA (Universal Windows App)

I am trying to figure out how to read and write files in a UWA application. I understand that I need to open a FileStreamm, but I can't figure out how to do that.
I started with this code:
FileStream fs = new FileStream(#"C:\XML\test.txt", FileMode.Create, FileAccess.Write);
seems to work, no red lines.
At the end of all of that I am told to put in Flush and Close, like this:
FileStream fs = new FileStream(#"C:\XML\test.txt", FileMode.Create,
...
fs.Flush();
fs.Close();
Now, this is where I hit a snag, because fs.Close(); is not even on the list of functions on fs. I just get a red line in my IDE if I try to hardcode it.
Can someone please take the time to help me understand how to do this with UWA? For some reason it seems like there is a different approach in Windows 10 apps, and I have a VERY hard time finding anything that shows me how to do it right. All the tutorials and SOF forum input are about older versions (non-UWA).
When I do this in a console application it all works as expected.
My end goal is to be able to read and write to an XML file in this kind of fashion:
XDocument doc = XDocument.Load(input);
XElement person = doc.Element("Person");
person.Add(new XElement("Employee",
new XElement("Name", "David"),
new XElement("Dept", "Chef")));
doc.Save(output);
I'm going down this path because an answer to my previous question told me to use a FileStream, but I simply cannot make that work in UWA.
You cannot just access any file from a Universal Windows App. Access to the file system is restricted.
See the documentation for details.
To help you further we need to know more about your application. What kind of files do you want to access for what reason?
Example on how to read an Xml File, modify it and store it in an Universal app. You need a button with the following Click handler and a TextBox named "TextBoxLog".
private async void ButtonDemo_Click(object sender, RoutedEventArgs e)
{
// Get our local storage folder
var localFolder = ApplicationData.Current.LocalFolder;
XmlDocument xmlDocument;
// Try to get file
var file = await localFolder.TryGetItemAsync("MyData.xml") as IStorageFile;
if(file != null)
{
// File exists -> Load into XML document
xmlDocument = await XmlDocument.LoadFromFileAsync(file);
}
else
{
// File does not exist, create new document in memory
xmlDocument = new XmlDocument();
xmlDocument.LoadXml(#"<?xml version=""1.0"" encoding=""UTF-8""?>" + Environment.NewLine + "<root></root>");
}
// Now show the current contents
TextBoxLog.Text = "";
var lEntries = xmlDocument.GetElementsByTagName("Entry");
foreach(var lEntry in lEntries)
{
TextBoxLog.Text += lEntry.InnerText + Environment.NewLine;
}
// Now add a new entry
var node = xmlDocument.CreateElement("Entry");
node.InnerText = DateTime.Now.ToString();
xmlDocument.DocumentElement.AppendChild(node);
// If the file does not exist yet, create it
if(file == null)
{
file = await localFolder.CreateFileAsync("MyData.xml");
}
// Now save the document
await xmlDocument.SaveToFileAsync(file);
}
Okay, the (simple) solution is to put the xml-file in the PROJECTFOLDER/bin/x86/debug/appX and then write the data to a list this way:
public class dataRaw
{
public string data { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
}
//You can call this class with x = collectionGenerator.getList() (it returns a list<T>)
public class collectionGenerator
{
public static List<dataRaw> getList()
{
//This is the xml file in the folder
var doc = XDocument.Load("Data.xml");
//This parse the XML and adds in to the list "dataList"
var dataList = doc.Root
.Descendants("Person")
.Select(node => new dataRaw
{
//data, firstName and lastName are in app variables from dataRaw put into listData.
//Number, FirstName and LastName are the nodes in the XML file.
data = node.Element("Number").Value,
firstName = node.Element("FirstName").Value,
lastName = node.Element("LastName").Value,
})
.ToList();
return dataList;
}
}

Read only root-element from XML

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.

Unable to save changes to XML document stored in Sharepoint 2010 Document Library

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');

Editing XML file in Silverlight does not work, but why?

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;
}
}
}

Categories

Resources